/**
  * Implements the instructions for completeValue as defined in the
  * "Field entries" section of the spec.
  *
  * If the field type is Non-Null, then this recursively completes the value
  * for the inner type. It throws a field error if that completion returns null,
  * as per the "Nullability" section of the spec.
  *
  * If the field type is a List, then this recursively completes the value
  * for the inner type on each item in the list.
  *
  * If the field type is a Scalar or Enum, ensures the completed value is a legal
  * value of the type by calling the `serialize` method of GraphQL type
  * definition.
  *
  * Otherwise, the field type expects a sub-selection set, and will complete the
  * value by evaluating all sub-selections.
  */
 private static function completeValue(ExecutionContext $exeContext, Type $returnType, $fieldASTs, ResolveInfo $info, &$result)
 {
     // If field type is NonNull, complete for inner type, and throw field error
     // if result is null.
     if ($returnType instanceof NonNull) {
         $completed = self::completeValue($exeContext, $returnType->getWrappedType(), $fieldASTs, $info, $result);
         if ($completed === null) {
             throw new Error('Cannot return null for non-nullable type.', $fieldASTs instanceof \ArrayObject ? $fieldASTs->getArrayCopy() : $fieldASTs);
         }
         return $completed;
     }
     // If result is null-like, return null.
     if (null === $result) {
         return null;
     }
     // If field type is Scalar or Enum, serialize to a valid value, returning
     // null if serialization is not possible.
     if ($returnType instanceof ScalarType || $returnType instanceof EnumType) {
         return $returnType->serialize($result);
     }
     // If field type is List, and return type is Composite - complete by executing these fields with list value as parameter
     if ($returnType instanceof ListOfType) {
         $itemType = $returnType->getWrappedType();
         Utils::invariant(is_array($result) || $result instanceof \Traversable, 'User Error: expected iterable, but did not find one.');
         // For Object[]:
         // Allow all object fields to process list value in it's `map` callback:
         if ($itemType instanceof ObjectType) {
             // Filter out nulls (as `map` doesn't expect it):
             $list = [];
             foreach ($result as $index => $item) {
                 if (null !== $item) {
                     $list[] = $item;
                 }
             }
             $subFieldASTs = self::collectSubFields($exeContext, $itemType, $fieldASTs);
             $mapped = self::executeFields($exeContext, $itemType, $list, $subFieldASTs);
             $i = 0;
             $completed = [];
             foreach ($result as $index => $item) {
                 if (null === $item) {
                     // Complete nulls separately
                     $completed[] = self::completeValueCatchingError($exeContext, $itemType, $fieldASTs, $info, $item);
                 } else {
                     // Assuming same order of mapped values
                     $completed[] = $mapped[$i++];
                 }
             }
             return $completed;
         } else {
             if ($itemType instanceof AbstractType) {
                 // Values sharded by ObjectType
                 $listPerObjectType = [];
                 // Helper structures to restore ordering after resolve calls
                 $resultTypeMap = [];
                 $typeNameMap = [];
                 $cursors = [];
                 $copied = [];
                 foreach ($result as $index => $item) {
                     $copied[$index] = $item;
                     if (null !== $item) {
                         $objectType = $itemType->getObjectType($item, $info);
                         if ($objectType && !$itemType->isPossibleType($objectType)) {
                             $exeContext->addError(new Error("Runtime Object type \"{$objectType}\" is not a possible type for \"{$itemType}\"."));
                             $copied[$index] = null;
                         } else {
                             $listPerObjectType[$objectType->name][] = $item;
                             $resultTypeMap[$index] = $objectType->name;
                             $typeNameMap[$objectType->name] = $objectType;
                         }
                     }
                 }
                 $mapped = [];
                 foreach ($listPerObjectType as $typeName => $list) {
                     $objectType = $typeNameMap[$typeName];
                     $subFieldASTs = self::collectSubFields($exeContext, $objectType, $fieldASTs);
                     $mapped[$typeName] = self::executeFields($exeContext, $objectType, $list, $subFieldASTs);
                     $cursors[$typeName] = 0;
                 }
                 // Restore order:
                 $completed = [];
                 foreach ($copied as $index => $item) {
                     if (null === $item) {
                         // Complete nulls separately
                         $completed[] = self::completeValueCatchingError($exeContext, $itemType, $fieldASTs, $info, $item);
                     } else {
                         $typeName = $resultTypeMap[$index];
                         $completed[] = $mapped[$typeName][$cursors[$typeName]++];
                     }
                 }
                 return $completed;
             } else {
                 // For simple lists:
                 $tmp = [];
                 foreach ($result as $item) {
                     $tmp[] = self::completeValueCatchingError($exeContext, $itemType, $fieldASTs, $info, $item);
                 }
                 return $tmp;
             }
         }
     }
     if ($returnType instanceof ObjectType) {
         $objectType = $returnType;
     } else {
         if ($returnType instanceof AbstractType) {
             $objectType = $returnType->getObjectType($result, $info);
             if ($objectType && !$returnType->isPossibleType($objectType)) {
                 throw new Error("Runtime Object type \"{$objectType}\" is not a possible type for \"{$returnType}\".");
             }
         } else {
             $objectType = null;
         }
     }
     if (!$objectType) {
         return null;
     }
     // If there is an isTypeOf predicate function, call it with the
     // current result. If isTypeOf returns false, then raise an error rather
     // than continuing execution.
     if (false === $objectType->isTypeOf($result, $info)) {
         throw new Error("Expected value of type {$objectType} but got: {$result}.", $fieldASTs);
     }
     // Collect sub-fields to execute to complete this value.
     $subFieldASTs = self::collectSubFields($exeContext, $objectType, $fieldASTs);
     $executed = self::executeFields($exeContext, $objectType, [$result], $subFieldASTs);
     return isset($executed[0]) ? $executed[0] : null;
 }