Skip to content

Commit

Permalink
[NTI] Change when to copy properties from window to Window.prototype,…
Browse files Browse the repository at this point in the history
… to handle

cases where Window is extended (e.g., happens in tests when people use fakes)

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=174250192
  • Loading branch information
dimvar authored and brad4d committed Nov 2, 2017
1 parent eeb661b commit c9000d6
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 16 deletions.
34 changes: 22 additions & 12 deletions src/com/google/javascript/jscomp/GlobalTypeInfoCollector.java
Expand Up @@ -299,6 +299,7 @@ public class GlobalTypeInfoCollector implements CompilerPass {
private final GlobalTypeInfo globalTypeInfo; private final GlobalTypeInfo globalTypeInfo;
private final SimpleInference simpleInference; private final SimpleInference simpleInference;
private final OrderedExterns orderedExterns; private final OrderedExterns orderedExterns;
private RawNominalType window;


public GlobalTypeInfoCollector(AbstractCompiler compiler) { public GlobalTypeInfoCollector(AbstractCompiler compiler) {
this.warnings = new WarningReporter(compiler); this.warnings = new WarningReporter(compiler);
Expand Down Expand Up @@ -368,30 +369,28 @@ public void process(Node externs, Node root) {


// (7) Adjust types of properties based on inheritance information. // (7) Adjust types of properties based on inheritance information.
// Report errors in the inheritance chain. Do Window last. // Report errors in the inheritance chain. Do Window last.
RawNominalType win = null; Collection<RawNominalType> windows = new ArrayList<>();
for (Map.Entry<Node, RawNominalType> entry : nominaltypesByNode.entrySet()) { for (Map.Entry<Node, RawNominalType> entry : nominaltypesByNode.entrySet()) {
RawNominalType rawType = entry.getValue(); RawNominalType rawType = entry.getValue();
if (rawType.getName().equals(WINDOW_CLASS) && entry.getKey().isFromExterns()) { if (this.window != null && rawType.hasAncestorClass(this.window)) {
win = rawType; windows.add(rawType);
continue; continue;
} }
checkAndFreezeNominalType(rawType); checkAndFreezeNominalType(rawType);
} }
JSType globalThisType; JSType globalThisType;
if (win != null) { if (this.window != null) {
// Copy properties from window to Window.prototype, because sometimes // Copy properties from window to Window.prototype, because sometimes
// people pass window around rather than using it directly. // people pass window around rather than using it directly.
// Copying the properties is correct only when there is a single object
// of type Window in the program. But in very rare cases, people subclass Window.
// Then, win is frozen here and we don't copy the properties.
// Window has been subclassed iff it is already frozen here.
Namespace winNs = getGlobalScope().getNamespace(WINDOW_INSTANCE); Namespace winNs = getGlobalScope().getNamespace(WINDOW_INSTANCE);
if (winNs != null && !win.isFrozen()) { if (winNs != null) {
winNs.copyWindowProperties(getCommonTypes(), win); winNs.copyWindowProperties(getCommonTypes(), this.window);
}
for (RawNominalType rawType : windows) {
checkAndFreezeNominalType(rawType);
} }
checkAndFreezeNominalType(win);
// Type the global THIS as window // Type the global THIS as window
globalThisType = win.getInstanceAsJSType(); globalThisType = this.window.getInstanceAsJSType();
} else { } else {
// Type the global THIS as a loose object // Type the global THIS as a loose object
globalThisType = getCommonTypes().getTopObject().withLoose(); globalThisType = getCommonTypes().getTopObject().withLoose();
Expand Down Expand Up @@ -447,6 +446,14 @@ public void visit(NodeTraversal t, Node n, Node parent) {
this.compiler.setExternProperties(ImmutableSet.copyOf(getExternPropertyNames())); this.compiler.setExternProperties(ImmutableSet.copyOf(getExternPropertyNames()));
} }


private void setWindow(RawNominalType rawType) {
this.window = rawType;
}

private static boolean isWindowRawType(RawNominalType rawType) {
return rawType.getName().equals(WINDOW_CLASS) && rawType.getDefSite().isFromExterns();
}

private RawNominalType dummyRawTypeForMissingExterns(String name) { private RawNominalType dummyRawTypeForMissingExterns(String name) {
Node defSite = NodeUtil.emptyFunction(); Node defSite = NodeUtil.emptyFunction();
defSite.setStaticSourceFile(new SimpleSourceFile("", true)); defSite.setStaticSourceFile(new SimpleSourceFile("", true));
Expand Down Expand Up @@ -1302,6 +1309,9 @@ private void maybeRecordNominalType(
rawType = RawNominalType.makeNominalInterface( rawType = RawNominalType.makeNominalInterface(
getCommonTypes(), defSite, qname, typeParameters, objKind); getCommonTypes(), defSite, qname, typeParameters, objKind);
} }
if (isWindowRawType(rawType)) {
setWindow(rawType);
}
nominaltypesByNode.put(defSite, rawType); nominaltypesByNode.put(defSite, rawType);
if (isRedeclaration) { if (isRedeclaration) {
return; return;
Expand Down
4 changes: 2 additions & 2 deletions src/com/google/javascript/jscomp/newtypes/Namespace.java
Expand Up @@ -271,8 +271,8 @@ public final void copyWindowProperties(JSTypes commonTypes, RawNominalType win)
win.addProtoProperty(entry.getKey(), null, ns.toJSType(), true); win.addProtoProperty(entry.getKey(), null, ns.toJSType(), true);
} }
for (Map.Entry<String, Property> entry : this.otherProps.entrySet()) { for (Map.Entry<String, Property> entry : this.otherProps.entrySet()) {
win.addProtoProperty( Property p = entry.getValue();
entry.getKey(), null, entry.getValue().getType(), true); win.addProtoProperty(entry.getKey(), null, p.getType(), p.isConstant());
} }
} }


Expand Down
Expand Up @@ -247,7 +247,7 @@ public void setCtorFunction(FunctionType ctorFn) {
this.ctorFn = ctorFn; this.ctorFn = ctorFn;
} }


boolean hasAncestorClass(RawNominalType ancestor) { public boolean hasAncestorClass(RawNominalType ancestor) {
checkState(ancestor.isClass()); checkState(ancestor.isClass());
if (this == ancestor) { if (this == ancestor) {
return true; return true;
Expand Down
51 changes: 50 additions & 1 deletion test/com/google/javascript/jscomp/NewTypeInferenceTest.java
Expand Up @@ -20504,7 +20504,8 @@ public void testPromoteTrueAndFalseToBooleanWhenInstantiatingGenerics() {
"x(true, false);")); "x(true, false);"));
} }


public void testExtendWindowDontCrash() { public void testFakeWindows() {
// Only the globals in externs are copied over to Window.prototype
typeCheck(LINE_JOINER.join( typeCheck(LINE_JOINER.join(
"/** @type {number} */", "/** @type {number} */",
"var globalvar;", "var globalvar;",
Expand All @@ -20518,6 +20519,54 @@ public void testExtendWindowDontCrash() {
"}", "}",
"f(new FakeWindow);"), "f(new FakeWindow);"),
NewTypeInference.INEXISTENT_PROPERTY); NewTypeInference.INEXISTENT_PROPERTY);

typeCheckCustomExterns(
LINE_JOINER.join(
DEFAULT_EXTERNS,
"/** @type {number} */",
"var foobar;"),
LINE_JOINER.join(
"/**",
" * @constructor",
" * @extends {Window}",
" */",
"function FakeWindow() {}",
"function f(/** !Window */ w) {",
" var /** string */ s = w.foobar;",
"}"),
NewTypeInference.MISTYPED_ASSIGN_RHS);

typeCheckCustomExterns(
LINE_JOINER.join(
DEFAULT_EXTERNS,
"/** @type {number} */",
"var foobar;"),
LINE_JOINER.join(
"/**",
" * @constructor",
" * @extends {Window}",
" */",
"function FakeWindow() {}",
"function f(/** !FakeWindow */ w) {",
" var /** string */ s = w.foobar;",
"}"),
NewTypeInference.MISTYPED_ASSIGN_RHS);

typeCheckCustomExterns(
LINE_JOINER.join(
DEFAULT_EXTERNS,
"/** @type {number} */",
"var foobar;"),
LINE_JOINER.join(
"/**",
" * @constructor",
" * @extends {Window}",
" */",
"function FakeWindow() {}",
"function f(/** !FakeWindow */ w) {",
" w.foobar = 'asdf';",
"}"),
NewTypeInference.MISTYPED_ASSIGN_RHS);
} }


public void testInferUndeclaredPrototypeProperty() { public void testInferUndeclaredPrototypeProperty() {
Expand Down

0 comments on commit c9000d6

Please sign in to comment.