Skip to content

Commit

Permalink
[NTI] When Bar extends Foo, the prototype object of Bar.prototype sho…
Browse files Browse the repository at this point in the history
…uld be a Foo instead of Foo.prototype.

In OTI this is accomplished by using PrototypeObjectType.
Fixes issue with disambiguation.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=174082073
  • Loading branch information
dimvar authored and brad4d committed Nov 1, 2017
1 parent 28408f8 commit 643cc3e
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 19 deletions.
2 changes: 1 addition & 1 deletion src/com/google/javascript/jscomp/AmbiguateProperties.java
Expand Up @@ -168,7 +168,7 @@ Map<String, String> getRenamingMap() {
private int getIntForType(TypeI type) { private int getIntForType(TypeI type) {
// Templatized types don't exist at runtime, so collapse to raw type // Templatized types don't exist at runtime, so collapse to raw type
if (type != null && type.isGenericObjectType()) { if (type != null && type.isGenericObjectType()) {
type = type.toMaybeObjectType().getPrototypeObject().getOwnerFunction().getInstanceType(); type = type.toMaybeObjectType().getRawType();
} }
if (intForType.containsKey(type)) { if (intForType.containsKey(type)) {
return intForType.get(type).intValue(); return intForType.get(type).intValue();
Expand Down
20 changes: 2 additions & 18 deletions src/com/google/javascript/jscomp/newtypes/JSType.java
Expand Up @@ -2041,19 +2041,8 @@ public final FunctionTypeI getSuperClassConstructor() {
@Override @Override
public final JSType getPrototypeObject() { public final JSType getPrototypeObject() {
checkState(this.isSingletonObj()); checkState(this.isSingletonObj());
JSType proto = getNominalTypeIfSingletonObj().getPrototypeObject(); ObjectType proto = getObjTypeIfSingletonObj().getPrototypeObject();
if (this.equals(proto)) { return proto != null ? fromObjectType(proto) : null;
// In JS's dynamic semantics, the only object without a __proto__ is
// Object.prototype, but it's not representable in NTI.
// Object.prototype is the only case where we are equal to our own prototype.
// In this case, we should return null.
Preconditions.checkState(
isBuiltinObjectPrototype(),
"Failed to reach Object.prototype in prototype chain, unexpected self-link found at %s",
this);
return null;
}
return proto;
} }


@Override @Override
Expand Down Expand Up @@ -2101,11 +2090,6 @@ public final boolean isAmbiguousObject() {
return obj != null && obj.isAmbiguousObject(); return obj != null && obj.isAmbiguousObject();
} }


final boolean isBuiltinObjectPrototype() {
ObjectType obj = getObjTypeIfSingletonObj();
return obj != null && obj.getNominalType().isBuiltinObject() && obj.isPrototypeObject();
}

@Override @Override
public final boolean isLiteralObject() { public final boolean isLiteralObject() {
return isSingletonObj() && getNominalTypeIfSingletonObj().isLiteralObject(); return isSingletonObj() && getNominalTypeIfSingletonObj().isLiteralObject();
Expand Down
34 changes: 34 additions & 0 deletions src/com/google/javascript/jscomp/newtypes/ObjectType.java
Expand Up @@ -37,6 +37,7 @@
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import javax.annotation.Nullable;


/** /**
* {@link JSType}s include a possibly-empty set of ObjectType instances, * {@link JSType}s include a possibly-empty set of ObjectType instances,
Expand Down Expand Up @@ -209,6 +210,39 @@ boolean isNamespace() {
return this.ns != null; return this.ns != null;
} }


private boolean isBuiltinObjectPrototype() {
return this.nominalType.isBuiltinObject() && isPrototypeObject();
}

/**
* Returns the prototype of this object type. For Object.prototype it returns null.
* When Bar extends Foo, the prototype of Bar.prototype is the canonical Foo instance. (This is
* accomplished in OTI by using the special type PrototypeObjectType.)
* As a result, this method may return a type X for which isPrototypeObject is false, i.e.,
* a type that has no owner function.
*/
@Nullable
ObjectType getPrototypeObject() {
ObjectType proto;
if (isPrototypeObject() && !isBuiltinObjectPrototype()) {
proto = this.nominalType.getInstanceAsObjectType();
} else {
proto = this.nominalType.getPrototypeObject().getObjTypeIfSingletonObj();
}
if (this.equals(proto)) {
// In JS's dynamic semantics, the only object without a __proto__ is
// Object.prototype, but it's not representable in NTI.
// Object.prototype is the only case where we are equal to our own prototype.
// In this case, we should return null.
Preconditions.checkState(
isBuiltinObjectPrototype(),
"Failed to reach Object.prototype in prototype chain, unexpected self-link found at %s",
this);
return null;
}
return proto;
}

boolean isPrototypeObject() { boolean isPrototypeObject() {
return getOwnerFunction() != null; return getOwnerFunction() != null;
} }
Expand Down
34 changes: 34 additions & 0 deletions test/com/google/javascript/jscomp/AmbiguatePropertiesTest.java
Expand Up @@ -1041,4 +1041,38 @@ public void testUnannotatedConstructorsDontCrash() {
"function Bar() {}", "function Bar() {}",
"Bar.prototype.a;")); "Bar.prototype.a;"));
} }

public void testGenericPrototypeObject() {
String js = LINE_JOINER.join(
"/**",
" * @constructor",
" * @template T",
" */",
"function Foo() {",
" this.a = 1;",
"}",
"/** @constructor @extends {Foo<number>} */",
"function Bar() {}",
"/** @constructor */",
"function Baz() {",
" this.b = 2;",
"}");

String output = LINE_JOINER.join(
"/**",
" * @constructor",
" * @template T",
" */",
"function Foo() {",
" this.a = 1;",
"}",
"/** @constructor @extends {Foo<number>} */",
"function Bar() {}",
"/** @constructor */",
"function Baz() {",
" this.a = 2;",
"}");

test(js, output);
}
} }
34 changes: 34 additions & 0 deletions test/com/google/javascript/jscomp/DisambiguatePropertiesTest.java
Expand Up @@ -197,6 +197,40 @@ public void testPrototypeAndInstance4() {
testSets(js, js, "{a=[[Foo.prototype]]}"); testSets(js, js, "{a=[[Foo.prototype]]}");
} }


public void testPrototypeAndInstance5() {
String js = LINE_JOINER.join(
"/** @constructor */",
"function Foo() {",
" this.a = 1;",
"}",
"/** @constructor @extends {Foo} */",
"function Bar() {",
" this.a = 2;",
"}",
"/** @constructor */",
"function Baz() {",
" this.a = 3;",
"}",
"var x = (new Bar).a;");

String output = LINE_JOINER.join(
"/** @constructor */",
"function Foo() {",
" this.Foo$a = 1;",
"}",
"/** @constructor @extends {Foo} */",
"function Bar() {",
" this.Foo$a = 2;",
"}",
"/** @constructor */",
"function Baz() {",
" this.Baz$a = 3;",
"}",
"var x = (new Bar).Foo$a;");

test(js, output);
}

public void testTwoTypes1() { public void testTwoTypes1() {
String js = "" String js = ""
+ "/** @constructor */ function Foo() {}\n" + "/** @constructor */ function Foo() {}\n"
Expand Down

0 comments on commit 643cc3e

Please sign in to comment.