Skip to content

Commit

Permalink
Rollback "Changes the collection used to track subtypes..."
Browse files Browse the repository at this point in the history
Also fix the underlying issue in a simpler way.

Some digging reveals that the only source of duplication is PrototypeObjectType re-adding extended superclasses (as opposed to extended interfaces), which is only necessary to pick up some corner cases of externs whose superclasses were not added the first time around.

Instead of a complicated new set implementation, we simply keep track of whether the subclass was added to its superclass the first time around, and skip adding it again if so.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=220185687
  • Loading branch information
shicks authored and brad4d committed Nov 7, 2018
1 parent cf1e8ec commit 9238a9c
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 442 deletions.
144 changes: 0 additions & 144 deletions src/com/google/javascript/rhino/CyclicSerializableLinkedHashSet.java

This file was deleted.

91 changes: 42 additions & 49 deletions src/com/google/javascript/rhino/jstype/FunctionType.java
Expand Up @@ -42,20 +42,16 @@
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.javascript.rhino.jstype.JSTypeNative.OBJECT_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.OBJECT_TYPE;
import static com.google.javascript.rhino.jstype.JSTypeNative.U2U_CONSTRUCTOR_TYPE; import static com.google.javascript.rhino.jstype.JSTypeNative.U2U_CONSTRUCTOR_TYPE;


import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.javascript.rhino.CyclicSerializableLinkedHashSet;
import com.google.javascript.rhino.ErrorReporter; import com.google.javascript.rhino.ErrorReporter;
import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token; import com.google.javascript.rhino.Token;
import com.google.javascript.rhino.jstype.JSType.EqCache;
import com.google.javascript.rhino.jstype.JSType.SubtypingMode;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
Expand All @@ -67,7 +63,6 @@
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.annotation.Nullable;


/** /**
* This derived type provides extended information about a function, including its return type and * This derived type provides extended information about a function, including its return type and
Expand Down Expand Up @@ -149,13 +144,18 @@ enum ConstructorAmbiguity {
private ImmutableList<ObjectType> extendedInterfaces = ImmutableList.of(); private ImmutableList<ObjectType> extendedInterfaces = ImmutableList.of();


/** /**
* For the instance type of this ctor, the ctor types of all known subtypes of that instance type. * The types which are subtypes of this function. It is lazily initialized and only relevant for
* * constructors. In all other cases it is {@code null}.
* <p>This field is only applicable to ctor functions. */
private List<FunctionType> subTypes = null;

/**
* Whether this constructor was added to its superclass constructor's subtypes list, to avoid a
* limited amount of duplication that can happen from unresolved supertypes. This only tracks
* classes extending classes (no interfaces), since there is no way to duplicate interfaces via
* methods accessible outside this class.
*/ */
@Nullable private boolean wasAddedToExtendedConstructorSubtypes = false;
// @MonotonicNonNull
private CyclicSerializableLinkedHashSet<FunctionType> knownSubtypeCtors;


/** Creates an instance for a function that might be a constructor. */ /** Creates an instance for a function that might be a constructor. */
FunctionType( FunctionType(
Expand Down Expand Up @@ -507,13 +507,14 @@ private boolean setPrototypeNoCheck(ObjectType prototype, Node propertyNode) {
if (isConstructor() || isInterface()) { if (isConstructor() || isInterface()) {
FunctionType superClass = getSuperClassConstructor(); FunctionType superClass = getSuperClassConstructor();
if (superClass != null) { if (superClass != null) {
superClass.addSubTypeIfNotPresent(this); superClass.addSubType(this);
wasAddedToExtendedConstructorSubtypes = true;
} }


if (isInterface()) { if (isInterface()) {
for (ObjectType interfaceType : getExtendedInterfaces()) { for (ObjectType interfaceType : getExtendedInterfaces()) {
if (interfaceType.getConstructor() != null) { if (interfaceType.getConstructor() != null) {
interfaceType.getConstructor().addSubTypeIfNotPresent(this); interfaceType.getConstructor().addSubType(this);
} }
} }
} }
Expand Down Expand Up @@ -1132,15 +1133,28 @@ public final void setSource(Node source) {
this.source = source; this.source = source;
} }


/** Adds a type to the set of known subtype ctors for this type. */ /** Adds a type to the list of subtypes for this type. */
final void addSubTypeIfNotPresent(FunctionType subtype) { private void addSubType(FunctionType subType) {
checkState(isConstructor() || isInterface()); if (subTypes == null) {

subTypes = new ArrayList<>();
if (knownSubtypeCtors == null) {
knownSubtypeCtors = new CyclicSerializableLinkedHashSet<>();
} }
subTypes.add(subType);
}


knownSubtypeCtors.add(subtype); /**
* Restricted package-accessible version of {@link #addSubType}, which ensures subtypes are not
* duplicated. Generally subtypes are added internally and are guaranteed not to be duplicated,
* but this has the possibility of missing unresolved supertypes (typically from externs). To
* handle that case, {@link PrototypeObjectType} also adds subclasses after resolution. This
* method only adds a subclass to the list if it didn't already add itself to its superclass in
* the earlier pass. Ideally, "subclass" here would only refer to classes, but there's an edge
* case where interfaces have the {@code Object} constructor added as its "superclass".
*/
final void addSubClassAfterResolution(FunctionType subClass) {
checkArgument(this == subClass.getSuperClassConstructor());
if (!subClass.wasAddedToExtendedConstructorSubtypes) {
addSubType(subClass);
}
} }


// NOTE(sdh): One might assume that immediately after calling this, hasCachedValues() should // NOTE(sdh): One might assume that immediately after calling this, hasCachedValues() should
Expand All @@ -1150,8 +1164,8 @@ final void addSubTypeIfNotPresent(FunctionType subtype) {
public final void clearCachedValues() { public final void clearCachedValues() {
super.clearCachedValues(); super.clearCachedValues();


if (knownSubtypeCtors != null) { if (subTypes != null) {
for (FunctionType subType : knownSubtypeCtors) { for (FunctionType subType : subTypes) {
subType.clearCachedValues(); subType.clearCachedValues();
} }
} }
Expand All @@ -1169,7 +1183,7 @@ public final void clearCachedValues() {


public final Iterable<FunctionType> getDirectSubTypes() { public final Iterable<FunctionType> getDirectSubTypes() {
return Iterables.concat( return Iterables.concat(
knownSubtypeCtors != null ? knownSubtypeCtors : ImmutableList.of(), subTypes != null ? subTypes : ImmutableList.<FunctionType>of(),
this.registry.getDirectImplementors(this)); this.registry.getDirectImplementors(this));
} }


Expand Down Expand Up @@ -1215,8 +1229,11 @@ JSType resolveInternal(ErrorReporter reporter) {
extendedInterfaces = resolvedExtended; extendedInterfaces = resolvedExtended;
} }


if (knownSubtypeCtors != null) { if (subTypes != null) {
resolveKnownSubtypeCtors(reporter); for (int i = 0; i < subTypes.size(); i++) {
FunctionType subType = subTypes.get(i);
subTypes.set(i, JSType.toMaybeFunctionType(subType.resolve(reporter)));
}
} }


return super.resolveInternal(reporter); return super.resolveInternal(reporter);
Expand Down Expand Up @@ -1247,30 +1264,6 @@ private ImmutableList<ObjectType> resolveTypeListHelper(
return changed ? resolvedList.build() : null; return changed ? resolvedList.build() : null;
} }


private void resolveKnownSubtypeCtors(ErrorReporter reporter) {
// We want to resolve all of the known subtypes-ctors so we can store those resolved types
// instead.
ImmutableList<FunctionType> setCopy;
do {
setCopy =
// However, resolving a type may cause more subtype-ctors to be registered. To avoid a
// `ConcurrentModificationException` we operate on a copy of the original set. We leave
// the original set in place in case resolution would re-add a previously known type.
ImmutableList.copyOf(knownSubtypeCtors).stream()
.map((t) -> JSType.toMaybeFunctionType(t.resolve(reporter)))
.collect(toImmutableList());

// We do this iteratively until resolving adds no more subtypes.
} while (setCopy.size() != knownSubtypeCtors.size());

// Additionally, resolving a type may change its `hashCode`, so we have to rebuild the set of
// subtype-ctors.
knownSubtypeCtors = null; // Reset
for (FunctionType subtypeCtor : setCopy) {
addSubTypeIfNotPresent(subtypeCtor);
}
}

@Override @Override
public final String toDebugHashCodeString() { public final String toDebugHashCodeString() {
if (this == registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE)) { if (this == registry.getNativeType(JSTypeNative.FUNCTION_INSTANCE_TYPE)) {
Expand Down
Expand Up @@ -493,7 +493,7 @@ JSType resolveInternal(ErrorReporter reporter) {
if (superCtor != null) { if (superCtor != null) {
// If the super ctor of this prototype object was not known before resolution, then the // If the super ctor of this prototype object was not known before resolution, then the
// subTypes would not have been set. Update them. // subTypes would not have been set. Update them.
superCtor.addSubTypeIfNotPresent(ctor); superCtor.addSubClassAfterResolution(ctor);
} }
} }
} }
Expand Down

0 comments on commit 9238a9c

Please sign in to comment.