@Override
  JSType resolveInternal(ErrorReporter t, StaticScope<JSType> scope) {
    setResolvedTypeInternal(this);

    call = (ArrowType) safeResolve(call, t, scope);
    if (prototypeSlot != null) {
      prototypeSlot.setType(safeResolve(prototypeSlot.getType(), t, scope));
    }

    // Warning about typeOfThis if it doesn't resolve to an ObjectType
    // is handled further upstream.
    //
    // TODO(nicksantos): Handle this correctly if we have a UnionType.
    JSType maybeTypeOfThis = safeResolve(typeOfThis, t, scope);
    if (maybeTypeOfThis != null) {
      if (maybeTypeOfThis.isNullType() || maybeTypeOfThis.isVoidType()) {
        typeOfThis = maybeTypeOfThis;
      } else {
        maybeTypeOfThis = ObjectType.cast(maybeTypeOfThis.restrictByNotNullOrUndefined());
        if (maybeTypeOfThis != null) {
          typeOfThis = maybeTypeOfThis;
        }
      }
    }

    ImmutableList<ObjectType> resolvedImplemented =
        resolveTypeListHelper(implementedInterfaces, t, scope);
    if (resolvedImplemented != null) {
      implementedInterfaces = resolvedImplemented;
    }

    ImmutableList<ObjectType> resolvedExtended =
        resolveTypeListHelper(extendedInterfaces, t, scope);
    if (resolvedExtended != null) {
      extendedInterfaces = resolvedExtended;
    }

    if (subTypes != null) {
      for (int i = 0; i < subTypes.size(); i++) {
        subTypes.set(i, JSType.toMaybeFunctionType(subTypes.get(i).resolve(t, scope)));
      }
    }

    return super.resolveInternal(t, scope);
  }
  /**
   * A function is a subtype of another if their call methods are related via subtyping and {@code
   * this} is a subtype of {@code that} with regard to the prototype chain.
   */
  @Override
  public boolean isSubtype(JSType that) {
    if (JSType.isSubtypeHelper(this, that)) {
      return true;
    }

    if (that.isFunctionType()) {
      FunctionType other = that.toMaybeFunctionType();
      if (other.isInterface()) {
        // Any function can be assigned to an interface function.
        return true;
      }
      if (isInterface()) {
        // An interface function cannot be assigned to anything.
        return false;
      }

      // If functionA is a subtype of functionB, then their "this" types
      // should be contravariant. However, this causes problems because
      // of the way we enforce overrides. Because function(this:SubFoo)
      // is not a subtype of function(this:Foo), our override check treats
      // this as an error. Let's punt on all this for now.
      // TODO(nicksantos): fix this.
      boolean treatThisTypesAsCovariant =
          // An interface 'this'-type is non-restrictive.
          // In practical terms, if C implements I, and I has a method m,
          // then any m doesn't necessarily have to C#m's 'this'
          // type doesn't need to match I.
          (other.typeOfThis.toObjectType() != null
                  && other.typeOfThis.toObjectType().getConstructor() != null
                  && other.typeOfThis.toObjectType().getConstructor().isInterface())
              ||

              // If one of the 'this' types is covariant of the other,
              // then we'll treat them as covariant (see comment above).
              other.typeOfThis.isSubtype(this.typeOfThis)
              || this.typeOfThis.isSubtype(other.typeOfThis);
      return treatThisTypesAsCovariant && this.call.isSubtype(other.call);
    }

    return getNativeType(JSTypeNative.FUNCTION_PROTOTYPE).isSubtype(that);
  }
示例#3
0
 /**
  * Resolves a named type by looking up its first component in the scope, and subsequent components
  * as properties. The scope must have been fully parsed and a symbol table constructed.
  */
 private void resolveViaProperties(ErrorReporter reporter, StaticTypedScope<JSType> enclosing) {
   JSType value = lookupViaProperties(reporter, enclosing);
   // last component of the chain
   if (value != null && value.isFunctionType() && (value.isConstructor() || value.isInterface())) {
     FunctionType functionType = value.toMaybeFunctionType();
     setReferencedAndResolvedType(functionType.getInstanceType(), reporter);
   } else if (value != null && value.isNoObjectType()) {
     setReferencedAndResolvedType(
         registry.getNativeObjectType(JSTypeNative.NO_OBJECT_TYPE), reporter);
   } else if (value instanceof EnumType) {
     setReferencedAndResolvedType(((EnumType) value).getElementsType(), reporter);
   } else {
     // We've been running into issues where people forward-declare
     // non-named types. (This is legitimate...our dependency management
     // code doubles as our forward-declaration code.)
     //
     // So if the type does resolve to an actual value, but it's not named,
     // then don't respect the forward declaration.
     handleUnresolvedType(reporter, value == null || value.isUnknownType());
   }
 }
  /** Adds an alternate to the union type under construction. Returns this for easy chaining. */
  public UnionTypeBuilder addAlternate(JSType alternate, boolean isStructural) {
    // build() returns the bottom type by default, so we can
    // just bail out early here.
    if (alternate.isNoType()) {
      return this;
    }

    isAllType = isAllType || alternate.isAllType();
    containsVoidType = containsVoidType || alternate.isVoidType();

    boolean isAlternateUnknown = alternate instanceof UnknownType;
    isNativeUnknownType = isNativeUnknownType || isAlternateUnknown;
    if (isAlternateUnknown) {
      areAllUnknownsChecked = areAllUnknownsChecked && alternate.isCheckedUnknownType();
    }
    if (!isAllType && !isNativeUnknownType) {
      if (alternate.isUnionType()) {
        UnionType union = alternate.toMaybeUnionType();
        for (JSType unionAlt : union.getAlternatesWithoutStructuralTyping()) {
          addAlternate(unionAlt);
        }
      } else {
        if (alternates.size() > maxUnionSize) {
          return this;
        }

        // Function types are special, because they have their
        // own bizarre sub-lattice. See the comments on
        // FunctionType#supAndInf helper and above at functionTypePosition.
        if (alternate.isFunctionType() && functionTypePosition != -1) {
          // See the comments on functionTypePosition above.
          FunctionType other = alternates.get(functionTypePosition).toMaybeFunctionType();
          FunctionType supremum = alternate.toMaybeFunctionType().supAndInfHelper(other, true);
          alternates.set(functionTypePosition, supremum);
          result = null;
          return this;
        }

        // Look through the alternates we've got so far,
        // and check if any of them are duplicates of
        // one another.
        int currentIndex = 0;
        Iterator<JSType> it = alternates.iterator();
        while (it.hasNext()) {
          boolean removeCurrent = false;
          JSType current = it.next();

          // Unknown and NoResolved types may just be names that haven't
          // been resolved yet. So keep these in the union, and just use
          // equality checking for simple de-duping.
          if (alternate.isUnknownType()
              || current.isUnknownType()
              || alternate.isNoResolvedType()
              || current.isNoResolvedType()
              || alternate.hasAnyTemplateTypes()
              || current.hasAnyTemplateTypes()) {
            if (alternate.isEquivalentTo(current, isStructural)) {
              // Alternate is unnecessary.
              return this;
            }
          } else {

            // Because "Foo" and "Foo.<?>" are roughly equivalent
            // templatized types, special care is needed when building the
            // union. For example:
            //   Object is consider a subtype of Object.<string>
            // but we want to leave "Object" not "Object.<string>" when
            // building the subtype.
            //

            if (alternate.isTemplatizedType() || current.isTemplatizedType()) {
              // Cases:
              // 1) alternate:Array.<string> and current:Object ==> Object
              // 2) alternate:Array.<string> and current:Array ==> Array
              // 3) alternate:Object.<string> and
              //    current:Array ==> Array|Object.<string>
              // 4) alternate:Object and current:Array.<string> ==> Object
              // 5) alternate:Array and current:Array.<string> ==> Array
              // 6) alternate:Array and
              //    current:Object.<string> ==> Array|Object.<string>
              // 7) alternate:Array.<string> and
              //    current:Array.<number> ==> Array.<?>
              // 8) alternate:Array.<string> and
              //    current:Array.<string> ==> Array.<string>
              // 9) alternate:Array.<string> and
              //    current:Object.<string> ==> Object.<string>|Array.<string>

              if (!current.isTemplatizedType()) {
                if (isSubtype(alternate, current, isStructural)) {
                  // case 1, 2
                  return this;
                }
                // case 3: leave current, add alternate
              } else if (!alternate.isTemplatizedType()) {
                if (isSubtype(current, alternate, isStructural)) {
                  // case 4, 5
                  removeCurrent = true;
                }
                // case 6: leave current, add alternate
              } else {
                Preconditions.checkState(
                    current.isTemplatizedType() && alternate.isTemplatizedType());
                TemplatizedType templatizedAlternate = alternate.toMaybeTemplatizedType();
                TemplatizedType templatizedCurrent = current.toMaybeTemplatizedType();

                if (templatizedCurrent.wrapsSameRawType(templatizedAlternate)) {
                  if (alternate
                      .getTemplateTypeMap()
                      .checkEquivalenceHelper(
                          current.getTemplateTypeMap(),
                          EquivalenceMethod.IDENTITY,
                          SubtypingMode.NORMAL)) {
                    // case 8
                    return this;
                  } else {
                    // TODO(johnlenz): should we leave both types?
                    // case 7: add a merged alternate
                    // We currently merge to the templatized types to "unknown"
                    // which is equivalent to the raw type.
                    JSType merged = templatizedCurrent.getReferencedObjTypeInternal();
                    return addAlternate(merged);
                  }
                }
                // case 9: leave current, add alternate
              }
              // Otherwise leave both templatized types.
            } else if (isSubtype(alternate, current, isStructural)) {
              // Alternate is unnecessary.
              return this;
            } else if (isSubtype(current, alternate, isStructural)) {
              // Alternate makes current obsolete
              removeCurrent = true;
            }
          }

          if (removeCurrent) {
            it.remove();

            if (currentIndex == functionTypePosition) {
              functionTypePosition = -1;
            } else if (currentIndex < functionTypePosition) {
              functionTypePosition--;
              currentIndex--;
            }
          }
          currentIndex++;
        }

        if (alternate.isFunctionType()) {
          // See the comments on functionTypePosition above.
          Preconditions.checkState(functionTypePosition == -1);
          functionTypePosition = alternates.size();
        }

        alternates.add(alternate);
        result = null; // invalidate the memoized result
      }
    } else {
      result = null;
    }
    return this;
  }