@Override public JSType getLeastSupertype(JSType that) { if (!that.isUnknownType() && !that.isUnionType()) { for (JSType alternate : alternatesWithoutStucturalTyping) { if (!alternate.isUnknownType() && that.isSubtype(alternate)) { return this; } } } return getLeastSupertype(this, that); }
@Override public String toDebugHashCodeString() { if (this == registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE)) { return super.toDebugHashCodeString(); } StringBuilder b = new StringBuilder(32); b.append("function ("); int paramNum = call.parameters.getChildCount(); boolean hasKnownTypeOfThis = !typeOfThis.isUnknownType(); if (hasKnownTypeOfThis) { b.append("this:"); b.append(getDebugHashCodeStringOf(typeOfThis)); } if (paramNum > 0) { if (hasKnownTypeOfThis) { b.append(", "); } Node p = call.parameters.getFirstChild(); b.append(getDebugHashCodeStringOf(p.getJSType())); p = p.getNext(); while (p != null) { b.append(", "); b.append(getDebugHashCodeStringOf(p.getJSType())); p = p.getNext(); } } b.append(")"); b.append(": "); b.append(getDebugHashCodeStringOf(call.returnType)); return b.toString(); }
@Override public JSType collapseUnion() { JSType currentValue = null; ObjectType currentCommonSuper = null; for (JSType a : alternatesWithoutStucturalTyping) { if (a.isUnknownType()) { return getNativeType(JSTypeNative.UNKNOWN_TYPE); } ObjectType obj = a.toObjectType(); if (obj == null) { if (currentValue == null && currentCommonSuper == null) { // If obj is not an object, then it must be a value. currentValue = a; } else { // Multiple values and objects will always collapse to the ALL_TYPE. return getNativeType(JSTypeNative.ALL_TYPE); } } else if (currentValue != null) { // Values and objects will always collapse to the ALL_TYPE. return getNativeType(JSTypeNative.ALL_TYPE); } else if (currentCommonSuper == null) { currentCommonSuper = obj; } else { currentCommonSuper = registry.findCommonSuperObject(currentCommonSuper, obj); } } return currentCommonSuper; }
@Override public boolean isUnknownType() { for (JSType t : alternatesWithoutStucturalTyping) { if (t.isUnknownType()) { return true; } } return false; }
/** * Returns a more restricted union type than {@code this} one, in which all subtypes of {@code * type} have been removed. * * <p>Examples: * * <ul> * <li>{@code (number,string)} restricted by {@code number} is {@code string} * <li>{@code (null, EvalError, URIError)} restricted by {@code Error} is {@code null} * </ul> * * @param type the supertype of the types to remove from this union type */ public JSType getRestrictedUnion(JSType type) { UnionTypeBuilder restricted = new UnionTypeBuilder(registry); for (JSType t : alternatesWithoutStucturalTyping) { // Keep all unknown/unresolved types. if (t.isUnknownType() || t.isNoResolvedType() || !t.isSubtype(type)) { restricted.addAlternate(t); } } return restricted.build(); }
@Override public TernaryValue testForEquality(JSType that) { TernaryValue result = super.testForEquality(that); if (result != null) { return result; } if (that.isUnknownType() || that.isSubtype(getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN))) { return UNKNOWN; } return FALSE; }
@Override protected boolean isSubtype(JSType that, ImplCache implicitImplCache) { // unknown if (that.isUnknownType()) { return true; } // all type if (that.isAllType()) { return true; } for (JSType element : alternatesWithoutStucturalTyping) { if (!element.isSubtype(that, implicitImplCache)) { return false; } } return true; }
/** * 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; }