diff --git a/src/com/google/javascript/rhino/jstype/JSTypeRegistry.java b/src/com/google/javascript/rhino/jstype/JSTypeRegistry.java index 84018756eef..dae8fcafe8a 100644 --- a/src/com/google/javascript/rhino/jstype/JSTypeRegistry.java +++ b/src/com/google/javascript/rhino/jstype/JSTypeRegistry.java @@ -194,12 +194,14 @@ public final class JSTypeRegistry { private final transient Set forwardDeclaredTypes; // A map of properties to the types on which those properties have been declared. - private transient Multimap typesIndexedByProperty = + // "Reference" types are excluded because those already exist in eachRefTypeIndexedByProperty to + // avoid blowing up the size of this map. + private final transient SetMultimap nonRefTypesIndexedByProperty = MultimapBuilder.hashKeys().linkedHashSetValues().build(); private JSType sentinelObjectLiteral; - // To avoid blowing up the size of typesIndexedByProperty, we use the sentinel object + // To avoid blowing up the size of nonRefTypesIndexedByProperty, we use the sentinel object // literal instead of registering arbitrarily many types. // But because of the way unions are constructed, some properties of record types in unions // are getting dropped and cause spurious "non-existent property" warnings. @@ -209,7 +211,7 @@ public final class JSTypeRegistry { // canPropertyBeDefined, if the type has a property in propertiesOfSupertypesInUnions, we // consider it to possibly have any property in droppedPropertiesOfUnions. This is a loose // check, but we restrict it to records that may be present in unions, and it allows us to - // keep typesIndexedByProperty small. + // keep nonRefTypesIndexedByProperty small. private final Set propertiesOfSupertypesInUnions = new HashSet<>(); private final Set droppedPropertiesOfUnions = new HashSet<>(); @@ -1080,31 +1082,22 @@ void registerDroppedPropertiesInUnion(RecordType subtype, RecordType supertype) * show up in the type registry"). */ public void registerPropertyOnType(String propertyName, JSType type) { - if (isObjectLiteralThatCanBeSkipped(type)) { - type = getSentinelObjectLiteral(); - } - if (type.isUnionType()) { - typesIndexedByProperty.putAll(propertyName, type.toMaybeUnionType().getAlternates()); - } else { - typesIndexedByProperty.put(propertyName, type); + for (JSType alternate : type.toMaybeUnionType().getAlternates()) { + registerPropertyOnType(propertyName, alternate); + } + return; } - addReferenceTypeIndexedByProperty(propertyName, type); - } + if (isObjectLiteralThatCanBeSkipped(type)) { + type = getSentinelObjectLiteral(); + } - private void addReferenceTypeIndexedByProperty( - String propertyName, JSType type) { if (type instanceof ObjectType && ((ObjectType) type).hasReferenceName()) { ObjectType objType = (ObjectType) type; eachRefTypeIndexedByProperty.put(propertyName, objType); - } else if (type instanceof NamedType) { - addReferenceTypeIndexedByProperty( - propertyName, ((NamedType) type).getReferencedType()); - } else if (type.isUnionType()) { - for (JSType alternate : type.toMaybeUnionType().getAlternates()) { - addReferenceTypeIndexedByProperty(propertyName, alternate); - } + } else { + nonRefTypesIndexedByProperty.put(propertyName, type); } } @@ -1148,19 +1141,26 @@ public PropDefinitionKind canPropertyBeDefined(JSType type, String propertyName) } } - if (typesIndexedByProperty.containsKey(propertyName)) { - for (JSType alternative : typesIndexedByProperty.get(propertyName)) { - JSType greatestSubtype = alternative.getGreatestSubtype(type); - if (!greatestSubtype.isEmptyType()) { - // We've found a type with this property. Now we just have to make - // sure it's not a type used for internal bookkeeping. - RecordType maybeRecordType = greatestSubtype.toMaybeRecordType(); - if (maybeRecordType != null && maybeRecordType.isSynthetic()) { - continue; - } + Iterable associatedTypes = ImmutableList.of(); + if (nonRefTypesIndexedByProperty.containsKey(propertyName)) { + associatedTypes = nonRefTypesIndexedByProperty.get(propertyName); + } + if (eachRefTypeIndexedByProperty.containsKey(propertyName)) { + associatedTypes = + Iterables.concat(associatedTypes, eachRefTypeIndexedByProperty.get(propertyName)); + } - return PropDefinitionKind.LOOSE; + for (JSType alternative : associatedTypes) { + JSType greatestSubtype = alternative.getGreatestSubtype(type); + if (!greatestSubtype.isEmptyType()) { + // We've found a type with this property. Now we just have to make + // sure it's not a type used for internal bookkeeping. + RecordType maybeRecordType = greatestSubtype.toMaybeRecordType(); + if (maybeRecordType != null && maybeRecordType.isSynthetic()) { + continue; } + + return PropDefinitionKind.LOOSE; } }