diff --git a/src/com/google/javascript/rhino/jstype/JSType.java b/src/com/google/javascript/rhino/jstype/JSType.java index 98e30edaa4c..e595959c69d 100644 --- a/src/com/google/javascript/rhino/jstype/JSType.java +++ b/src/com/google/javascript/rhino/jstype/JSType.java @@ -44,13 +44,14 @@ import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.HashBasedTable; -import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.JSDocInfo; import com.google.javascript.rhino.TypeI; import java.io.Serializable; import java.util.Comparator; import java.util.IdentityHashMap; +import java.util.List; /** * Represents JavaScript value types.

@@ -78,8 +79,8 @@ public abstract class JSType implements TypeI, Serializable { private static final CanCastToVisitor CAN_CAST_TO_VISITOR = new CanCastToVisitor(); - private static final ImmutableList COVARIANT_TYPES = - ImmutableList.of("Object", "IArrayLike", "Array"); + private static final ImmutableSet COVARIANT_TYPES = + ImmutableSet.of("Object", "IArrayLike", "Array"); /** * Total ordering on types based on their textual representation. @@ -302,7 +303,9 @@ public boolean containsArray() { // Check if this is a union that contains an array if (this.isUnionType()) { JSType arrayType = registry.getNativeType(JSTypeNative.ARRAY_TYPE); - for (JSType alternate : this.toMaybeUnionType().getAlternates()) { + List alternates = this.toMaybeUnionType().getAlternates(); + for (int i = 0; i < alternates.size(); i++) { + JSType alternate = alternates.get(i); if (alternate.isSubtype(arrayType)) { return true; } @@ -1126,7 +1129,9 @@ static JSType filterNoResolvedType(JSType type) { } else if (type.isUnionType()) { UnionType unionType = type.toMaybeUnionType(); boolean needsFiltering = false; - for (JSType alt : unionType.getAlternates()) { + List alternates = unionType.getAlternates(); + for (int i = 0; i < alternates.size(); i++) { + JSType alt = alternates.get(i); if (alt.isNoResolvedType()) { needsFiltering = true; break; @@ -1406,8 +1411,10 @@ static boolean isSubtypeHelper(JSType thisType, JSType thatType, // unions if (thatType.isUnionType()) { UnionType union = thatType.toMaybeUnionType(); - for (JSType element : union.alternatesWithoutStucturalTyping) { - if (thisType.isSubtype(element, implicitImplCache, subtypingMode)) { + List alternates = union.alternatesWithoutStucturalTyping; + // use indexed iteration to avoid iterator allocations + for (int i = 0; i < alternates.size(); i++) { + if (thisType.isSubtype(alternates.get(i), implicitImplCache, subtypingMode)) { return true; } } diff --git a/src/com/google/javascript/rhino/jstype/UnionType.java b/src/com/google/javascript/rhino/jstype/UnionType.java index 0b852d92aed..1d640203d82 100644 --- a/src/com/google/javascript/rhino/jstype/UnionType.java +++ b/src/com/google/javascript/rhino/jstype/UnionType.java @@ -44,9 +44,7 @@ import com.google.common.base.Joiner; import com.google.common.base.Predicate; import com.google.javascript.rhino.ErrorReporter; - import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; @@ -62,26 +60,29 @@ * statement is captured by making {@code (String,boolean)} and * {@code (boolean,String)} equal.

* + * * The implementation of this class prevents the creation of nested * unions.

*/ public class UnionType extends JSType { private static final long serialVersionUID = 1L; + // NOTE: to avoid allocating iterators, all the loops below iterate over alternates by index + // instead of using the for-each loop idiom. + // alternates without merging structural interfaces and their subtypes - Collection alternatesWithoutStucturalTyping; + List alternatesWithoutStucturalTyping; // alternates under structural typing - Collection alternates; + List alternates; private int hashcode; /** * Creates a union type. * - * @param alternatesWithoutStructuralTyping the alternates of the union without - * structural typing subtype + * @param alternatesWithoutStructuralTyping the alternates of the union without structural typing + * subtype */ - UnionType(JSTypeRegistry registry, - Collection alternatesWithoutStructuralTyping) { + UnionType(JSTypeRegistry registry, List alternatesWithoutStructuralTyping) { super(registry); this.alternatesWithoutStucturalTyping = alternatesWithoutStructuralTyping; @@ -95,11 +96,12 @@ public class UnionType extends JSType { /** * Gets the alternate types of this union type. - * @return The alternate types of this union type. The returned set is - * immutable. + * + * @return The alternate types of this union type. The returned set is immutable. */ - public Collection getAlternates() { - for (JSType t : alternatesWithoutStucturalTyping) { + public List getAlternates() { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType t = alternatesWithoutStucturalTyping.get(i); if (t.isUnionType()) { rebuildAlternates(); break; @@ -109,13 +111,14 @@ public Collection getAlternates() { } /** - * Gets the alternate types of this union type, including structural interfaces - * and implicit implementations as are distinct alternates. - * @return The alternate types of this union type. The returned set is - * immutable. + * Gets the alternate types of this union type, including structural interfaces and implicit + * implementations as are distinct alternates. + * + * @return The alternate types of this union type. The returned set is immutable. */ - public Collection getAlternatesWithoutStructuralTyping() { - for (JSType t : alternatesWithoutStucturalTyping) { + public List getAlternatesWithoutStructuralTyping() { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType t = alternatesWithoutStucturalTyping.get(i); if (t.isUnionType()) { rebuildAlternates(); break; @@ -235,7 +238,8 @@ public JSType findPropertyType(String propertyName) { @Override public boolean canBeCalled() { - for (JSType t : alternatesWithoutStucturalTyping) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType t = alternatesWithoutStucturalTyping.get(i); if (!t.canBeCalled()) { return false; } @@ -246,7 +250,8 @@ public boolean canBeCalled() { @Override public JSType autobox() { UnionTypeBuilder restricted = new UnionTypeBuilder(registry); - for (JSType t : alternatesWithoutStucturalTyping) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType t = alternatesWithoutStucturalTyping.get(i); restricted.addAlternate(t.autobox()); } return restricted.build(); @@ -255,7 +260,8 @@ public JSType autobox() { @Override public JSType restrictByNotNullOrUndefined() { UnionTypeBuilder restricted = new UnionTypeBuilder(registry); - for (JSType t : alternatesWithoutStucturalTyping) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType t = alternatesWithoutStucturalTyping.get(i); restricted.addAlternate(t.restrictByNotNullOrUndefined()); } return restricted.build(); @@ -264,7 +270,8 @@ public JSType restrictByNotNullOrUndefined() { @Override public TernaryValue testForEquality(JSType that) { TernaryValue result = null; - for (JSType t : alternatesWithoutStucturalTyping) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType t = alternatesWithoutStucturalTyping.get(i); TernaryValue test = t.testForEquality(that); if (result == null) { result = test; @@ -285,7 +292,8 @@ public TernaryValue testForEquality(JSType that) { */ @Override public boolean isNullable() { - for (JSType t : alternatesWithoutStucturalTyping) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType t = alternatesWithoutStucturalTyping.get(i); if (t.isNullable()) { return true; } @@ -298,7 +306,8 @@ public boolean isNullable() { */ @Override public boolean isVoidable() { - for (JSType t : alternatesWithoutStucturalTyping) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType t = alternatesWithoutStucturalTyping.get(i); if (t.isVoidable()) { return true; } @@ -311,7 +320,8 @@ public boolean isVoidable() { */ @Override public boolean isExplicitlyVoidable() { - for (JSType t : alternatesWithoutStucturalTyping) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType t = alternatesWithoutStucturalTyping.get(i); if (t.isExplicitlyVoidable()) { return true; } @@ -321,7 +331,8 @@ public boolean isExplicitlyVoidable() { @Override public boolean isUnknownType() { - for (JSType t : alternatesWithoutStucturalTyping) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType t = alternatesWithoutStucturalTyping.get(i); if (t.isUnknownType()) { return true; } @@ -331,7 +342,9 @@ public boolean isUnknownType() { @Override public boolean isStruct() { - for (JSType typ : getAlternates()) { + List alternates = getAlternates(); + for (int i = 0; i < alternates.size(); i++) { + JSType typ = alternates.get(i); if (typ.isStruct()) { return true; } @@ -341,7 +354,9 @@ public boolean isStruct() { @Override public boolean isDict() { - for (JSType typ : getAlternates()) { + List alternates = getAlternates(); + for (int i = 0; i < alternates.size(); i++) { + JSType typ = alternates.get(i); if (typ.isDict()) { return true; } @@ -352,7 +367,8 @@ public boolean isDict() { @Override public JSType getLeastSupertype(JSType that) { if (!that.isUnknownType() && !that.isUnionType()) { - for (JSType alternate : alternatesWithoutStucturalTyping) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType alternate = alternatesWithoutStucturalTyping.get(i); if (!alternate.isUnknownType() && that.isSubtype(alternate)) { return this; } @@ -364,14 +380,18 @@ public JSType getLeastSupertype(JSType that) { JSType meet(JSType that) { UnionTypeBuilder builder = new UnionTypeBuilder(registry); - for (JSType alternate : alternatesWithoutStucturalTyping) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType alternate = alternatesWithoutStucturalTyping.get(i); if (alternate.isSubtype(that)) { builder.addAlternate(alternate); } } if (that.isUnionType()) { - for (JSType otherAlternate : that.toMaybeUnionType().alternatesWithoutStucturalTyping) { + List thoseAlternatesWithoutStucturalTyping = + that.toMaybeUnionType().alternatesWithoutStucturalTyping; + for (int i = 0; i < thoseAlternatesWithoutStucturalTyping.size(); i++) { + JSType otherAlternate = thoseAlternatesWithoutStucturalTyping.get(i); if (otherAlternate.isSubtype(this)) { builder.addAlternate(otherAlternate); } @@ -395,13 +415,14 @@ JSType meet(JSType that) { */ boolean checkUnionEquivalenceHelper( UnionType that, EquivalenceMethod eqMethod, EqCache eqCache) { - Collection thatAlternates = that.getAlternatesWithoutStructuralTyping(); + List thatAlternates = that.getAlternatesWithoutStructuralTyping(); if (eqMethod == EquivalenceMethod.IDENTITY && getAlternatesWithoutStructuralTyping().size() != thatAlternates.size()) { return false; } - for (JSType alternate : thatAlternates) { - if (!hasAlternate(alternate, eqMethod, eqCache)) { + for (int i = 0; i < thatAlternates.size(); i++) { + JSType thatAlternate = thatAlternates.get(i); + if (!hasAlternate(thatAlternate, eqMethod, eqCache)) { return false; } } @@ -410,7 +431,9 @@ && getAlternatesWithoutStructuralTyping().size() != thatAlternates.size()) { private boolean hasAlternate(JSType type, EquivalenceMethod eqMethod, EqCache eqCache) { - for (JSType alternate : getAlternatesWithoutStructuralTyping()) { + List alternatesWithoutStructuralTyping = getAlternatesWithoutStructuralTyping(); + for (int i = 0; i < alternatesWithoutStructuralTyping.size(); i++) { + JSType alternate = alternatesWithoutStructuralTyping.get(i); if (alternate.checkEquivalenceHelper(type, eqMethod, eqCache)) { return true; } @@ -420,7 +443,8 @@ private boolean hasAlternate(JSType type, EquivalenceMethod eqMethod, @Override public boolean hasProperty(String pname) { - for (JSType alternate : alternatesWithoutStucturalTyping) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType alternate = alternatesWithoutStucturalTyping.get(i); if (alternate.hasProperty(pname)) { return true; } @@ -440,7 +464,8 @@ public UnionType toMaybeUnionType() { @Override public boolean isObject() { - for (JSType alternate : alternatesWithoutStucturalTyping) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType alternate = alternatesWithoutStucturalTyping.get(i); if (!alternate.isObject()) { return false; } @@ -457,7 +482,8 @@ public boolean isObject() { * @return {@code true} if the alternate is in the union */ public boolean contains(JSType type) { - for (JSType alt : alternatesWithoutStucturalTyping) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType alt = alternatesWithoutStucturalTyping.get(i); if (alt.isEquivalentTo(type)) { return true; } @@ -481,7 +507,8 @@ public boolean contains(JSType type) { */ public JSType getRestrictedUnion(JSType type) { UnionTypeBuilder restricted = new UnionTypeBuilder(registry); - for (JSType t : alternatesWithoutStucturalTyping) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType t = alternatesWithoutStucturalTyping.get(i); // Keep all unknown/unresolved types. if (t.isUnknownType() || t.isNoResolvedType() || !t.isSubtype(type)) { restricted.addAlternate(t); @@ -524,7 +551,8 @@ protected boolean isSubtype(JSType that, if (that.isAllType()) { return true; } - for (JSType element : alternatesWithoutStucturalTyping) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType element = alternatesWithoutStucturalTyping.get(i); if (subtypingMode == SubtypingMode.IGNORE_NULL_UNDEFINED && (element.isNullType() || element.isVoidType())) { continue; @@ -540,7 +568,8 @@ protected boolean isSubtype(JSType that, public JSType getRestrictedTypeGivenToBooleanOutcome(boolean outcome) { // gather elements after restriction UnionTypeBuilder restricted = new UnionTypeBuilder(registry); - for (JSType element : alternatesWithoutStucturalTyping) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType element = alternatesWithoutStucturalTyping.get(i); restricted.addAlternate( element.getRestrictedTypeGivenToBooleanOutcome(outcome)); } @@ -550,7 +579,8 @@ public JSType getRestrictedTypeGivenToBooleanOutcome(boolean outcome) { @Override public BooleanLiteralSet getPossibleToBooleanOutcomes() { BooleanLiteralSet literals = BooleanLiteralSet.EMPTY; - for (JSType element : alternatesWithoutStucturalTyping) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType element = alternatesWithoutStucturalTyping.get(i); literals = literals.union(element.getPossibleToBooleanOutcomes()); if (literals == BooleanLiteralSet.BOTH) { break; @@ -563,7 +593,8 @@ public BooleanLiteralSet getPossibleToBooleanOutcomes() { public TypePair getTypesUnderEquality(JSType that) { UnionTypeBuilder thisRestricted = new UnionTypeBuilder(registry); UnionTypeBuilder thatRestricted = new UnionTypeBuilder(registry); - for (JSType element : alternatesWithoutStucturalTyping) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType element = alternatesWithoutStucturalTyping.get(i); TypePair p = element.getTypesUnderEquality(that); if (p.typeA != null) { thisRestricted.addAlternate(p.typeA); @@ -581,7 +612,8 @@ public TypePair getTypesUnderEquality(JSType that) { public TypePair getTypesUnderInequality(JSType that) { UnionTypeBuilder thisRestricted = new UnionTypeBuilder(registry); UnionTypeBuilder thatRestricted = new UnionTypeBuilder(registry); - for (JSType element : alternatesWithoutStucturalTyping) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType element = alternatesWithoutStucturalTyping.get(i); TypePair p = element.getTypesUnderInequality(that); if (p.typeA != null) { thisRestricted.addAlternate(p.typeA); @@ -599,7 +631,8 @@ public TypePair getTypesUnderInequality(JSType that) { public TypePair getTypesUnderShallowInequality(JSType that) { UnionTypeBuilder thisRestricted = new UnionTypeBuilder(registry); UnionTypeBuilder thatRestricted = new UnionTypeBuilder(registry); - for (JSType element : alternatesWithoutStucturalTyping) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType element = alternatesWithoutStucturalTyping.get(i); TypePair p = element.getTypesUnderShallowInequality(that); if (p.typeA != null) { thisRestricted.addAlternate(p.typeA); @@ -626,9 +659,8 @@ public T visit(Visitor visitor) { JSType resolveInternal(ErrorReporter t, StaticTypedScope 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) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType alternate = alternatesWithoutStucturalTyping.get(i); alternate.resolve(t, scope); } // Ensure the union is in a normalized state. @@ -647,7 +679,8 @@ public String toDebugHashCodeString() { @Override public boolean setValidator(Predicate validator) { - for (JSType a : alternatesWithoutStucturalTyping) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType a = alternatesWithoutStucturalTyping.get(i); a.setValidator(validator); } return true; @@ -657,7 +690,8 @@ public boolean setValidator(Predicate validator) { public JSType collapseUnion() { JSType currentValue = null; ObjectType currentCommonSuper = null; - for (JSType a : alternatesWithoutStucturalTyping) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType a = alternatesWithoutStucturalTyping.get(i); if (a.isUnknownType()) { return getNativeType(JSTypeNative.UNKNOWN_TYPE); } @@ -686,14 +720,16 @@ public JSType collapseUnion() { @Override public void matchConstraint(JSType constraint) { - for (JSType alternate : alternatesWithoutStucturalTyping) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType alternate = alternatesWithoutStucturalTyping.get(i); alternate.matchConstraint(constraint); } } @Override public boolean hasAnyTemplateTypesInternal() { - for (JSType alternate : alternatesWithoutStucturalTyping) { + for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) { + JSType alternate = alternatesWithoutStucturalTyping.get(i); if (alternate.hasAnyTemplateTypes()) { return true; } diff --git a/src/com/google/javascript/rhino/jstype/UnionTypeBuilder.java b/src/com/google/javascript/rhino/jstype/UnionTypeBuilder.java index 0ab70652e15..3a223d3398c 100644 --- a/src/com/google/javascript/rhino/jstype/UnionTypeBuilder.java +++ b/src/com/google/javascript/rhino/jstype/UnionTypeBuilder.java @@ -48,10 +48,8 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.javascript.rhino.jstype.JSType.SubtypingMode; - import java.io.Serializable; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; @@ -110,7 +108,7 @@ public UnionTypeBuilder(JSTypeRegistry registry) { this.maxUnionSize = maxUnionSize; } - Collection getAlternates() { + List getAlternates() { JSType specialCaseType = reduceAlternatesWithoutUnion(); if (specialCaseType != null) { return ImmutableList.of(specialCaseType); @@ -161,7 +159,10 @@ public UnionTypeBuilder addAlternate(JSType alternate, boolean isStructural) { if (!isAllType && !isNativeUnknownType) { if (alternate.isUnionType()) { UnionType union = alternate.toMaybeUnionType(); - for (JSType unionAlt : union.getAlternatesWithoutStructuralTyping()) { + List alternatesWithoutStructuralTyping = + union.getAlternatesWithoutStructuralTyping(); + for (int i = 0; i < alternatesWithoutStructuralTyping.size(); i++) { + JSType unionAlt = alternatesWithoutStructuralTyping.get(i); addAlternate(unionAlt); } } else {