Skip to content

Commit

Permalink
Fix NPE during deserialization of RawNominalType when using NTI.
Browse files Browse the repository at this point in the history
Serialize and deserialize RawNominalType.interfaces as a List instead of a Set to avoid NPE during Set deserialization. Set deserialization requires calling .equals and .hashCode on partially deserialized objects when cycles are present.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=172175094
  • Loading branch information
rluble authored and lauraharker committed Oct 16, 2017
1 parent 31e3ee0 commit 36f375e
Showing 1 changed file with 17 additions and 5 deletions.
22 changes: 17 additions & 5 deletions src/com/google/javascript/jscomp/newtypes/RawNominalType.java
Expand Up @@ -29,6 +29,9 @@
import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Node;
import java.io.IOException; import java.io.IOException;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.Map; import java.util.Map;
Expand Down Expand Up @@ -64,11 +67,12 @@ public final class RawNominalType extends Namespace {
// If this type is generic, we don't record which instantiation A inherits from. // If this type is generic, we don't record which instantiation A inherits from.
// We don't store subclasses for Object because there are too many. // We don't store subclasses for Object because there are too many.


// TODO(rluble): Serialize this field. If this field is serialized naively, a cycle is introduced // These two fields have handled by custom serialization to avoid deserialization NPEs due
// which results in NPE when attempting to deserialize an HashSet that contains object that are // to the problematic interaction caused by cycles in the graph on objects that implement
// only partially deserialized. // equals(), hashCode() and Sets.
private transient Set<RawNominalType> subtypes = new LinkedHashSet<>(); private transient Set<RawNominalType> subtypes = new LinkedHashSet<>();
private ImmutableSet<NominalType> interfaces = null; private transient Collection<NominalType> interfaces = null;

private final Kind kind; private final Kind kind;
private final boolean isAbstractClass; private final boolean isAbstractClass;
// Used in GlobalTypeInfo to find type mismatches in the inheritance chain. // Used in GlobalTypeInfo to find type mismatches in the inheritance chain.
Expand Down Expand Up @@ -355,7 +359,7 @@ public NominalType getSuperClass() {
return this.superclass; return this.superclass;
} }


public ImmutableSet<NominalType> getInterfaces() { public Iterable<NominalType> getInterfaces() {
return this.interfaces == null ? ImmutableSet.<NominalType>of() : this.interfaces; return this.interfaces == null ? ImmutableSet.<NominalType>of() : this.interfaces;
} }


Expand Down Expand Up @@ -797,9 +801,17 @@ public void refreezeAfterDeserialization() {
} }


@GwtIncompatible("ObjectInputStream") @GwtIncompatible("ObjectInputStream")
@SuppressWarnings("unchecked")
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject(); in.defaultReadObject();
this.subtypes = new LinkedHashSet<>(); this.subtypes = new LinkedHashSet<>();
this.interfaces = (Collection<NominalType>) in.readObject();
}

@GwtIncompatible("ObjectOutputStream")
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
out.writeObject(new ArrayList<>(this.interfaces));
} }


// equals and hashCode default to reference equality, which is what we want // equals and hashCode default to reference equality, which is what we want
Expand Down

0 comments on commit 36f375e

Please sign in to comment.