private function resolveField(ObjectType $parentType, $source, $fieldNodes, $path)
    {
        $exeContext = $this->exeContext;
        $fieldNode  = $fieldNodes[0];
        $fieldName  = $fieldNode->name->value;
        $fieldDef   = $this->getFieldDef($exeContext->schema, $parentType, $fieldName);
        if (! $fieldDef) {
            return self::$UNDEFINED;
        }
        $returnType = $fieldDef->getType();
        // The resolve function's optional third argument is a collection of
        // information about the current execution state.
        $info = new ResolveInfo(
            $fieldName,
            $fieldNodes,
            $returnType,
            $parentType,
            $path,
            $exeContext->schema,
            $exeContext->fragments,
            $exeContext->rootValue,
            $exeContext->operation,
            $exeContext->variableValues
        );
        if ($fieldDef->resolveFn !== null) {
            $resolveFn = $fieldDef->resolveFn;
        } elseif ($parentType->resolveFieldFn !== null) {
            $resolveFn = $parentType->resolveFieldFn;
        } else {
            $resolveFn = $this->exeContext->fieldResolver;
        }
        // The resolve function's optional third argument is a context value that
        // is provided to every resolve function within an execution. It is commonly
        // used to represent an authenticated user, or request-specific caches.
        $context = $exeContext->contextValue;
        // Get the resolve function, regardless of if its result is normal
        // or abrupt (error).
        $result = $this->resolveOrError(
            $fieldDef,
            $fieldNode,
            $resolveFn,
            $source,
            $context,
            $info
        );
        $result = $this->completeValueCatchingError(
            $returnType,
            $fieldNodes,
            $info,
            $path,
            $result
        );

        return $result;
    }