@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; }
/** * Computes the greatest subtype of two related templatized types. * * @return The greatest subtype. */ JSType getGreatestSubtypeHelper(JSType rawThat) { Preconditions.checkNotNull(rawThat); if (!wrapsSameRawType(rawThat)) { if (!rawThat.isTemplatizedType()) { if (this.isSubtype(rawThat)) { return this; } else if (rawThat.isSubtype(this)) { return filterNoResolvedType(rawThat); } } if (this.isObject() && rawThat.isObject()) { return this.getNativeType(JSTypeNative.NO_OBJECT_TYPE); } return this.getNativeType(JSTypeNative.NO_TYPE); } TemplatizedType that = rawThat.toMaybeTemplatizedType(); Preconditions.checkNotNull(that); if (getTemplateTypeMap() .checkEquivalenceHelper( that.getTemplateTypeMap(), EquivalenceMethod.INVARIANT, SubtypingMode.NORMAL)) { return this; } // For types that have the same raw type but different type parameters, // we simply create a type has a "unknown" type parameter. This is // equivalent to the raw type. return getReferencedObjTypeInternal(); }
@Override public boolean setValidator(Predicate<JSType> validator) { for (JSType a : alternatesWithoutStucturalTyping) { a.setValidator(validator); } return true; }
/** * Resolves a 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. * * @return The type of the symbol, or null if the type could not be found. */ private JSType lookupViaProperties(ErrorReporter reporter, StaticTypedScope<JSType> enclosing) { String[] componentNames = reference.split("\\.", -1); if (componentNames[0].length() == 0) { return null; } StaticTypedSlot<JSType> slot = enclosing.getSlot(componentNames[0]); if (slot == null) { return null; } // If the first component has a type of 'Unknown', then any type // names using it should be regarded as silently 'Unknown' rather than be // noisy about it. JSType slotType = slot.getType(); if (slotType == null || slotType.isAllType() || slotType.isNoType()) { return null; } JSType value = getTypedefType(reporter, slot); if (value == null) { return null; } // resolving component by component for (int i = 1; i < componentNames.length; i++) { ObjectType parentClass = ObjectType.cast(value); if (parentClass == null) { return null; } if (componentNames[i].length() == 0) { return null; } value = parentClass.getPropertyType(componentNames[i]); } return value; }
@Override public JSType restrictByNotNullOrUndefined() { UnionTypeBuilder restricted = new UnionTypeBuilder(registry); for (JSType t : alternatesWithoutStucturalTyping) { restricted.addAlternate(t.restrictByNotNullOrUndefined()); } return restricted.build(); }
@Override public JSType autobox() { UnionTypeBuilder restricted = new UnionTypeBuilder(registry); for (JSType t : alternatesWithoutStucturalTyping) { restricted.addAlternate(t.autobox()); } return restricted.build(); }
@Override public boolean isSubtype(JSType that) { if (JSType.isSubtype(this, that)) { return true; } else { return that.isObject() && !that.isNoType() && !that.isNoResolvedType(); } }
/** * A {@link UnionType} contains a given type (alternate) iff the member vector contains it. * * @param type The alternate which might be in this union. * @return {@code true} if the alternate is in the union */ public boolean contains(JSType type) { for (JSType alt : alternatesWithoutStucturalTyping) { if (alt.isEquivalentTo(type)) { return true; } } return false; }
private boolean hasAlternate(JSType type, EquivalenceMethod eqMethod, EqCache eqCache) { for (JSType alternate : getAlternatesWithoutStructuralTyping()) { if (alternate.checkEquivalenceHelper(type, eqMethod, eqCache)) { return true; } } return false; }
@Override public String toDebugHashCodeString() { List<String> hashCodes = new ArrayList<>(); for (JSType a : alternatesWithoutStucturalTyping) { hashCodes.add(a.toDebugHashCodeString()); } return "{(" + Joiner.on(",").join(hashCodes) + ")}"; }
@Override public boolean isObject() { for (JSType alternate : alternatesWithoutStucturalTyping) { if (!alternate.isObject()) { return false; } } return true; }
@Override public boolean hasProperty(String pname) { for (JSType alternate : alternatesWithoutStucturalTyping) { if (alternate.hasProperty(pname)) { return true; } } return false; }
@Override public boolean isDict() { for (JSType typ : getAlternates()) { if (typ.isDict()) { return true; } } return false; }
@Override public JSType getRestrictedTypeGivenToBooleanOutcome(boolean outcome) { // gather elements after restriction UnionTypeBuilder restricted = new UnionTypeBuilder(registry); for (JSType element : alternatesWithoutStucturalTyping) { restricted.addAlternate(element.getRestrictedTypeGivenToBooleanOutcome(outcome)); } return restricted.build(); }
/** * Gets the alternate types of this union type. * * @return The alternate types of this union type. The returned set is immutable. */ public Collection<JSType> getAlternates() { for (JSType t : alternatesWithoutStucturalTyping) { if (t.isUnionType()) { rebuildAlternates(); break; } } return alternates; }
@Override public boolean canBeCalled() { for (JSType t : alternatesWithoutStucturalTyping) { if (!t.canBeCalled()) { return false; } } return true; }
@Override public boolean isUnknownType() { for (JSType t : alternatesWithoutStucturalTyping) { if (t.isUnknownType()) { return true; } } return false; }
@Override public boolean hasAnyTemplateTypesInternal() { for (JSType alternate : alternatesWithoutStucturalTyping) { if (alternate.hasAnyTemplateTypes()) { return true; } } return false; }
/** * This predicate is used to test whether a given type can appear in an {@code Object} context, * such as the expression in a {@code with} statement. * * <p>Most types we will encounter, except notably {@code null}, have at least the potential for * converting to {@code Object}. Host defined objects can get peculiar. * * <p>VOID type is included here because while it is not part of the JavaScript language, * functions returning 'void' type can't be used as operands of any operator or statement. * * <p> * * @return {@code true} if the type is not {@link NullType} or {@link VoidType} */ @Override public boolean matchesObjectContext() { // TODO(user): Reverse this logic to make it correct instead of generous. for (JSType t : alternatesWithoutStucturalTyping) { if (t.matchesObjectContext()) { 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 BooleanLiteralSet getPossibleToBooleanOutcomes() { BooleanLiteralSet literals = BooleanLiteralSet.EMPTY; for (JSType element : alternatesWithoutStucturalTyping) { literals = literals.union(element.getPossibleToBooleanOutcomes()); if (literals == BooleanLiteralSet.BOTH) { break; } } return literals; }
/** Gets the string representation of an optional param. */ private void appendOptionalArgString( StringBuilder builder, JSType paramType, boolean forAnnotations) { if (paramType.isUnionType()) { // Remove the optionality from the var arg. paramType = paramType .toMaybeUnionType() .getRestrictedUnion(registry.getNativeType(JSTypeNative.VOID_TYPE)); } builder.append(paramType.toStringHelper(forAnnotations)).append("="); }
@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 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 JSType resolveInternal(ErrorReporter t, StaticTypedScope<JSType> scope) { setResolvedTypeInternal(this); // for circularly defined types. // Just resolve the alternates, but do not update as that breaks some error // reporting cases. for (JSType alternate : alternatesWithoutStucturalTyping) { alternate.resolve(t, scope); } // Ensure the union is in a normalized state. rebuildAlternates(); return this; }
@Override public TernaryValue testForEquality(JSType that) { TernaryValue result = null; for (JSType t : alternatesWithoutStucturalTyping) { TernaryValue test = t.testForEquality(that); if (result == null) { result = test; } else if (!result.equals(test)) { return UNKNOWN; } } return result; }
@Override public boolean isSubtype(JSType that) { if (JSType.isSubtypeHelper(this, that)) { return true; } // Union types if (that.isUnionType()) { // The static {@code JSType.isSubtype} check already decomposed // union types, so we don't need to check those again. return false; } // record types if (that.isRecordType()) { return RecordType.isSubtype(this, that.toMaybeRecordType()); } // Interfaces // Find all the interfaces implemented by this class and compare each one // to the interface instance. ObjectType thatObj = that.toObjectType(); ObjectType thatCtor = thatObj == null ? null : thatObj.getConstructor(); if (thatCtor != null && thatCtor.isInterface()) { Iterable<ObjectType> thisInterfaces = getCtorImplementedInterfaces(); for (ObjectType thisInterface : thisInterfaces) { if (thisInterface.isSubtype(that)) { return true; } } } if (getConstructor() != null && getConstructor().isInterface()) { for (ObjectType thisInterface : getCtorExtendedInterfaces()) { if (thisInterface.isSubtype(that)) { return true; } } } // other prototype based objects if (isUnknownType() || implicitPrototypeChainIsUnknown()) { // If unsure, say 'yes', to avoid spurious warnings. // TODO(user): resolve the prototype chain completely in all cases, // to avoid guessing. return true; } return this.isImplicitPrototype(thatObj); }
@Override public TypePair getTypesUnderShallowInequality(JSType that) { UnionTypeBuilder thisRestricted = new UnionTypeBuilder(registry); UnionTypeBuilder thatRestricted = new UnionTypeBuilder(registry); for (JSType element : alternatesWithoutStucturalTyping) { TypePair p = element.getTypesUnderShallowInequality(that); if (p.typeA != null) { thisRestricted.addAlternate(p.typeA); } if (p.typeB != null) { thatRestricted.addAlternate(p.typeB); } } return new TypePair(thisRestricted.build(), thatRestricted.build()); }
private String getDebugHashCodeStringOf(JSType type) { if (type == this) { return "me"; } else { return type.toDebugHashCodeString(); } }
@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(); }