/*
* Copyright 2006 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import static com.google.javascript.jscomp.CollapseProperties.UNSAFE_NAMESPACE_WARNING;
import com.google.javascript.rhino.Node;
/**
* Tests for {@link AggressiveInlineAliases} plus {@link CollapseProperties}.
*
*/
public final class InlineAndCollapsePropertiesTest extends CompilerTestCase {
private static final String EXTERNS =
"var window;\n"
+ "function alert(s) {}\n"
+ "function parseInt(s) {}\n"
+ "/** @constructor */ function String() {};\n"
+ "var arguments";
public InlineAndCollapsePropertiesTest() {
super(EXTERNS);
}
@Override
protected CompilerPass getProcessor(final Compiler compiler) {
return new CompilerPass() {
AggressiveInlineAliases aggressiveInlineAliases = new AggressiveInlineAliases(compiler);
CollapseProperties collapseProperties = new CollapseProperties(compiler);
@Override
public void process(Node externs, Node root) {
aggressiveInlineAliases.process(externs, root);
collapseProperties.process(externs, root);
}
};
}
@Override
public void setUp() {
enableNormalize();
}
@Override protected int getNumRepetitions() {
return 1;
}
public void testCollapse() {
test("var a = {}; a.b = {}; var c = a.b;",
"var c = null");
test("var a = {}; a.b = {}; var c = a.b; use(c);",
"var a$b = {}; var c = null; use(a$b);");
testSame("var a = {}; /** @nocollapse */ a.b;");
}
public void testObjLitDeclaration() {
test("var a = {b: {}, c: {}}; var d = a.b; var e = a.c",
"var d = null; var e = null;");
test("var a = {b: {}, c: {}}; var d = a.b; var e = a.c; use(d, e);",
"var a$b = {}; var a$c = {}; var d = null; var e = null; use(a$b, a$c);");
test("var a = {b: {}, /** @nocollapse */ c: {}}; var d = a.b; var e = a.c",
"var a = {/** @nocollapse */ c: {}}; var d = null; var e = null;");
test("var a = {b: {}, /** @nocollapse */ c: {}}; var d = a.b; var e = a.c; use(d, e);",
"var a$b = {};var a = {/** @nocollapse */ c: {}};var d = null;var e = null;use(a$b, a.c);");
}
public void testObjLitDeclarationWithGet1() {
testSame("var a = {get b(){}};");
}
public void testObjLitDeclarationWithGet2() {
test("var a = {b: {}, get c(){}}; var d = a.b; var e = a.c;",
"var a = {get c() {}}; var d=null; var e = a.c");
test("var a = {b: {}, get c(){}}; var d = a.b; var e = a.c; use(d);",
"var a$b = {};var a = {get c(){}};var d = null; var e = a.c; use(a$b)");
}
public void testObjLitDeclarationWithGet3() {
test("var a = {b: {get c() { return 3; }}};",
"var a$b = {get c() { return 3; }};");
}
public void testObjLitDeclarationWithSet1() {
testSame("var a = {set b(a){}};");
}
public void testObjLitDeclarationWithSet2() {
test("var a = {b: {}, set c(a){}}; var d = a.b; var e = a.c",
"var a = {set c(a$jscomp$1){}}; var d=null; var e = a.c");
test("var a = {b: {}, set c(a){}}; var d = a.b; var e = a.c; use(d);",
"var a$b = {}; var a = {set c(a$jscomp$1){}}; var d=null; var e = a.c; use(a$b);");
}
public void testObjLitDeclarationWithSet3() {
test("var a = {b: {set c(d) {}}};",
"var a$b = {set c(d) {}};");
}
public void testObjLitDeclarationWithGetAndSet1() {
test("var a = {b: {get c() { return 3; },set c(d) {}}};",
"var a$b = {get c() { return 3; },set c(d) {}};");
}
public void testObjLitAssignmentDepth3() {
test("var a = {}; a.b = {}; a.b.c = {d: 1, e: 2}; var f = a.b.c.d",
"var a$b$c$d = 1; var a$b$c$e = 2; var f = null");
test("var a = {}; a.b = {}; a.b.c = {d: 1, e: 2}; var f = a.b.c.d; use(f);",
"var a$b$c$d = 1; var a$b$c$e = 2; var f = null; use(a$b$c$d);");
test("var a = {}; a.b = {}; a.b.c = {d: 1, /** @nocollapse */ e: 2}; "
+ "var f = a.b.c.d; var g = a.b.c.e",
"var a$b$c$d = 1; var a$b$c = {/** @nocollapse */ e: 2}; var f = null; var g = null;");
test("var a = {}; a.b = {}; a.b.c = {d: 1, /** @nocollapse */ e: 2}; "
+ "var f = a.b.c.d; var g = a.b.c.e; use(f, g);",
"var a$b$c$d = 1; var a$b$c = { /** @nocollapse */ e: 2};"
+ "var f = null; var g = null; use(a$b$c$d, a$b$c.e);");
testSame("var a = {}; /** @nocollapse*/ a.b = {}; "
+ "a.b.c = {d: 1, e: 2}; "
+ "var f = null; var g = null;");
}
public void testObjLitAssignmentDepth4() {
test("var a = {}; a.b = {}; a.b.c = {}; a.b.c.d = {e: 1, f: 2}; var g = a.b.c.d.e;",
"var a$b$c$d$e = 1; var a$b$c$d$f = 2; var g = null;");
test("var a = {}; a.b = {}; a.b.c = {}; a.b.c.d = {e: 1, f: 2}; var g = a.b.c.d.e; use(g);",
"var a$b$c$d$e = 1; var a$b$c$d$f = 2; var g = null; use(a$b$c$d$e);");
}
public void testAliasCreatedForObjectDepth1_1() {
// An object's properties are not collapsed if the object is referenced
// in a such a way that an alias is created for it, if that alias is used.
test("var a = {b: 0}; var c = a; c.b = 1; a.b == c.b;",
"var a$b = 0; var c = null; a$b = 1; a$b == a$b;");
test("var a = {b: 0}; var c = a; c.b = 1; a.b == c.b; use(c);",
"var a={b:0}; var c=null; a.b=1; a.b == a.b; use(a);");
}
public void testAliasCreatedForObjectDepth1_2() {
testSame("var a = {b: 0}; f(a); a.b;");
}
public void testAliasCreatedForObjectDepth1_3() {
testSame("var a = {b: 0}; new f(a); a.b;");
}
public void testMisusedConstructorTag() {
test("var a = {}; var d = a; a.b = function() {};"
+ "/** @constructor */ a.b.c = 0; a.b.c;",
"var d=null; var a$b=function(){}; /** @constructor */ var a$b$c=0; a$b$c;");
}
public void testAliasCreatedForCtorDepth1_1() {
// A constructor's properties *are* collapsed even if the function is
// referenced in a such a way that an alias is created for it,
// since a function with custom properties is considered a class and its
// non-prototype properties are considered static methods and variables.
// People don't typically iterate through static members of a class or
// refer to them using an alias for the class name.
test("/** @constructor */ var a = function(){}; a.b = 1; "
+ "var c = a; c.b = 2; a.b == c.b;",
"/** @constructor */ var a = function(){}; var a$b = 1;"
+ "var c = null; a$b = 2; a$b == a$b;");
// Sometimes we want to prevent static members of a constructor from
// being collapsed.
test("/** @constructor */ var a = function(){};"
+ "/** @nocollapse */ a.b = 1; var c = a; c.b = 2; a.b == c.b;",
"/** @constructor */ var a = function(){};"
+ "/** @nocollapse */ a.b = 1; var c = null; a.b = 2; a.b == a.b;");
}
public void testAliasCreatedForFunctionDepth2() {
test(
"var a = {}; a.b = function() {}; a.b.c = 1; var d = a.b; a.b.c != d.c;",
"var a$b = function() {}; var a$b$c = 1; var d = null; a$b$c != a$b$c;");
test("var a = {}; a.b = function() {}; /** @nocollapse */ a.b.c = 1;"
+ "var d = a.b; a.b.c == d.c;",
"var a$b = function() {}; /** @nocollapse */ a$b.c = 1; var d = null; a$b.c == a$b.c;");
}
public void testAliasCreatedForCtorDepth2() {
test("var a = {}; /** @constructor */ a.b = function() {}; a.b.c = 1; var d = a.b;"
+ "a.b.c == d.c;",
"/** @constructor */ var a$b = function() {}; var a$b$c = 1; var d = null;"
+ "a$b$c == a$b$c;");
test("var a = {}; /** @constructor */ a.b = function() {}; "
+ "/** @nocollapse */ a.b.c = 1; var d = a.b;"
+ "a.b.c == d.c;",
"/** @constructor */ var a$b = function() {}; /** @nocollapse */ a$b.c = 1; var d = null;"
+ "a$b.c == a$b.c;");
}
public void testAliasCreatedForClassDepth1_1() {
// A class's name is always collapsed, even if one of its prefixes is
// referenced in such a way that an alias is created for it.
test("var a = {}; /** @constructor */ a.b = function(){};"
+ "var c = a; c.b = 0; a.b != c.b;",
"/** @constructor */ var a$b = function(){}; var c = null; a$b = 0; a$b != a$b;");
test("var a = {}; /** @constructor @nocollapse */ a.b = function(){};"
+ "var c = 1; c = a; c.b = 0; a.b == c.b;",
"var a = {}; /** @constructor @nocollapse */ a.b = function(){};"
+ "var c = 1; c = a; c.b = 0; a.b == c.b;",
null, UNSAFE_NAMESPACE_WARNING);
test("var a = {}; /** @constructor @nocollapse */ a.b = function(){};"
+ "var c = a; c.b = 0; a.b == c.b;",
"var a = {}; /** @constructor @nocollapse */ a.b = function(){};"
+ "var c = null; a.b = 0; a.b == a.b;");
test("var a = {}; /** @constructor @nocollapse */ a.b = function(){};"
+ "var c = a; c.b = 0; a.b == c.b; use(c);",
"var a = {}; /** @constructor @nocollapse */ a.b = function(){};"
+ "var c = null; a.b = 0; a.b == a.b; use(a);",
null, UNSAFE_NAMESPACE_WARNING);
}
public void testObjLitDeclarationUsedInSameVarList() {
// The collapsed properties must be defined in the same place in the var list
// where they were originally defined (and not, for example, at the end).
test("var a = {b: {}, c: {}}; var d = a.b; var e = a.c; use(d, e);",
"var a$b = {}; var a$c = {}; var d = null; var e = null; use(a$b, a$c);");
test("var a = {b: {}, /** @nocollapse */ c: {}}; var d = a.b; var e = a.c; use(d, e);",
"var a$b = {};var a = {/** @nocollapse */ c: {}};var d = null;var e = null;use(a$b, a.c);");
}
public void testAddPropertyToUncollapsibleObjectInLocalScopeDepth1() {
test("var a = {}; var c = a; use(c); (function() {a.b = 0;})(); a.b;",
"var a={}; var c=null; use(a); (function(){ a.b = 0; })(); a.b;");
test("var a = {}; var c = 1; c = a; (function() {a.b = 0;})(); a.b;",
"var a = {}; var c=1; c = a; (function(){a.b = 0;})(); a.b;");
}
public void testAddPropertyToUncollapsibleNamedCtorInLocalScopeDepth1() {
test(
"/** @constructor */ function a() {} var a$b; var c = a; "
+ "(function() {a$b = 0;})(); a$b;",
"/** @constructor */ function a() {} var a$b; var c = null; "
+ "(function() {a$b = 0;})(); a$b;");
}
public void testAddPropertyToUncollapsibleCtorInLocalScopeDepth1() {
test("/** @constructor */ var a = function() {}; var c = a; "
+ "(function() {a.b = 0;})(); a.b;",
"/** @constructor */ var a = function() {}; var a$b; "
+ "var c = null; (function() {a$b = 0;})(); a$b;");
}
public void testAddPropertyToUncollapsibleObjectInLocalScopeDepth2() {
test("var a = {}; a.b = {}; var d = a.b; use(d);"
+ "(function() {a.b.c = 0;})(); a.b.c;",
"var a$b = {}; var d = null; use(a$b);"
+ "(function() {a$b.c = 0;})(); a$b.c;");
}
public void testAddPropertyToUncollapsibleCtorInLocalScopeDepth2() {
test("var a = {}; /** @constructor */ a.b = function (){}; var d = a.b;"
+ "(function() {a.b.c = 0;})(); a.b.c;",
"/** @constructor */ var a$b = function (){}; var a$b$c; var d = null;"
+ "(function() {a$b$c = 0;})(); a$b$c;");
}
public void testPropertyOfChildFuncOfUncollapsibleObjectDepth1() {
test("var a = {}; var c = a; a.b = function (){}; a.b.x = 0; a.b.x;",
"var c = null; var a$b=function() {}; var a$b$x = 0; a$b$x;");
test("var a = {}; var c = a; a.b = function (){}; a.b.x = 0; a.b.x; use(c);",
"var a = {}; var c = null; a.b=function() {}; a.b.x = 0; a.b.x; use(a);");
}
public void testPropertyOfChildFuncOfUncollapsibleObjectDepth2() {
test("var a = {}; a.b = {}; var c = a.b; a.b.c = function (){}; a.b.c.x = 0; a.b.c.x;",
"var c=null; var a$b$c = function(){}; var a$b$c$x = 0; a$b$c$x");
test("var a = {}; a.b = {}; var c = a.b; a.b.c = function (){}; a.b.c.x = 0; a.b.c.x; use(c);",
"var a$b = {}; var c=null; a$b.c = function(){}; a$b.c.x=0; a$b.c.x; use(a$b);");
}
public void testAddPropertyToChildFuncOfUncollapsibleObjectInLocalScope() {
test("var a = {}; a.b = function (){}; a.b.x = 0;"
+ "var c = a; (function() {a.b.y = 1;})(); a.b.x; a.b.y;",
"var a$b=function() {}; var a$b$y; var a$b$x = 0; var c=null;"
+ "(function() { a$b$y=1; })(); a$b$x; a$b$y");
}
public void testAddPropertyToChildTypeOfUncollapsibleObjectInLocalScope() {
test(
LINE_JOINER.join(
"var a = {};",
"/** @constructor */",
"a.b = function (){};",
"a.b.x = 0;",
"var c = a;",
"(function() {a.b.y = 1;})();",
"a.b.x;",
"a.b.y;"),
LINE_JOINER.join(
"/** @constructor */",
"var a$b = function (){};",
"var a$b$y;",
"var a$b$x = 0;",
"var c = null;",
"(function() {a$b$y = 1;})();",
"a$b$x;",
"a$b$y;"));
test(
LINE_JOINER.join(
"var a = {};",
"/** @constructor */",
"a.b = function (){};",
"a.b.x = 0;",
"var c = a;",
"(function() {a.b.y = 1;})();",
"a.b.x;",
"a.b.y;",
"use(c);"),
LINE_JOINER.join(
"var a = {};",
"/** @constructor */",
"var a$b = function (){};",
"var a$b$y;",
"var a$b$x = 0;",
"var c = null;",
"(function() {a$b$y = 1;})();",
"a$b$x;",
"a$b$y;",
"use(a);"),
null, UNSAFE_NAMESPACE_WARNING);
}
public void testAddPropertyToChildOfUncollapsibleFunctionInLocalScope() {
test(
"function a() {} a.b = {x: 0}; var c = a; (function() {a.b.y = 0;})(); a.b.y;",
"function a() {} var a$b$x=0; var a$b$y; var c=null; (function(){a$b$y=0})(); a$b$y");
}
public void testAddPropertyToChildOfUncollapsibleCtorInLocalScope() {
test("/** @constructor */ var a = function() {}; a.b = {x: 0}; var c = a;"
+ "(function() {a.b.y = 0;})(); a.b.y;",
"/** @constructor */ var a = function() {}; var a$b$x = 0; var a$b$y; var c = null;"
+ "(function() {a$b$y = 0;})(); a$b$y;");
}
public void testFunctionAlias2() {
test("var a = {}; a.b = {}; a.b.c = function(){}; a.b.d = a.b.c;use(a.b.d)",
"var a$b$c = function(){}; var a$b$d = null;use(a$b$c);");
}
public void testLocalAlias1() {
test("var a = {b: 3}; function f() { var x = a; f(x.b); }",
"var a$b = 3; function f() { var x = null; f(a$b); }");
}
public void testLocalAlias2() {
test("var a = {b: 3, c: 4}; function f() { var x = a; f(x.b); f(x.c);}",
"var a$b = 3; var a$c = 4; "
+ "function f() { var x = null; f(a$b); f(a$c);}");
}
public void testLocalAlias3() {
test("var a = {b: 3, c: {d: 5}}; "
+ "function f() { var x = a; f(x.b); f(x.c); f(x.c.d); }",
"var a$b = 3; var a$c = {d: 5}; "
+ "function f() { var x = null; f(a$b); f(a$c); f(a$c.d);}");
}
public void testLocalAlias4() {
test("var a = {b: 3}; var c = {d: 5}; "
+ "function f() { var x = a; var y = c; f(x.b); f(y.d); }",
"var a$b = 3; var c$d = 5; "
+ "function f() { var x = null; var y = null; f(a$b); f(c$d);}");
}
public void testLocalAlias5() {
test("var a = {b: {c: 5}}; "
+ "function f() { var x = a; var y = x.b; f(a.b.c); f(y.c); }",
"var a$b$c = 5; "
+ "function f() { var x = null; var y = null; f(a$b$c); f(a$b$c);}");
}
public void testLocalAlias6() {
test("var a = {b: 3}; function f() { var x = a; if (x.b) { f(x.b); } }",
"var a$b = 3; function f() { var x = null; if (a$b) { f(a$b); } }");
}
public void testLocalAlias7() {
test("var a = {b: {c: 5}}; function f() { var x = a.b; f(x.c); }",
"var a$b$c = 5; function f() { var x = null; f(a$b$c); }");
}
public void testGlobalWriteToAncestor() {
testSame("var a = {b: 3}; function f() { var x = a; f(a.b); } a = 5;");
}
public void testGlobalWriteToNonAncestor() {
test("var a = {b: 3}; function f() { var x = a; f(a.b); } a.b = 5;",
"var a$b = 3; function f() { var x = null; f(a$b); } a$b = 5;");
}
public void testLocalWriteToAncestor() {
testSame("var a = {b: 3}; function f() { a = 5; var x = a; f(a.b); } ");
}
public void testLocalWriteToNonAncestor() {
test("var a = {b: 3}; "
+ "function f() { a.b = 5; var x = a; f(a.b); }",
"var a$b = 3; function f() { a$b = 5; var x = null; f(a$b); } ");
}
public void testLocalAliasOfAncestor() {
testSame("var a = {b: {c: 5}}; function g() { f(a); } "
+ "function f() { var x = a.b; f(x.c); }");
}
public void testGlobalAliasOfAncestor() {
test("var a = {b: {c: 5}}; function f() { var x = a.b; f(x.c); }",
"var a$b$c=5; function f() {var x=null; f(a$b$c); }");
}
public void testLocalAliasOfOtherName() {
testSame("var foo = function() { return {b: 3}; };"
+ "var a = foo(); a.b = 5; "
+ "function f() { var x = a.b; f(x); }");
}
public void testLocalAliasOfFunction() {
test("var a = function() {}; a.b = 5; "
+ "function f() { var x = a.b; f(x); }",
"var a = function() {}; var a$b = 5; "
+ "function f() { var x = null; f(a$b); }");
}
public void testNoInlineGetpropIntoCall() {
test("var b = x; function f() { var a = b; a(); }",
"var b = x; function f() { var a = null; b(); }");
test("var b = {}; b.c = x; function f() { var a = b.c; a(); }",
"var b$c = x; function f() { var a = null; b$c(); }");
}
public void testCommaOperator() {
test("var a = {}; a.b = function() {}, a.b();",
"var a$b; a$b=function() {}, a$b();");
test(
"var ns = {};\n"
+ "ns.Foo = {};\n"
+ "var Baz = {};\n"
+ "Baz.Foo = ns.Foo;\n"
+ "(Baz.Foo.bar = 10, 123);",
"var Baz$Foo=null;\n"
+ "var ns$Foo$bar;\n"
+ "(ns$Foo$bar = 10, 123);");
test(
"var ns = {};\n"
+ "ns.Foo = {};\n"
+ "var Baz = {};\n"
+ "Baz.Foo = ns.Foo;\n"
+ "function f() { (Baz.Foo.bar = 10, 123); }",
"var ns$Foo$bar;\n"
+ "var Baz$Foo=null;\n"
+ "function f() { (ns$Foo$bar = 10, 123); }");
}
public void testTypeDefAlias1() {
test(
"/** @constructor */ var D = function() {};\n"
+ "/** @constructor */ D.L = function() {};\n"
+ "/** @type {D.L} */ D.L.A = new D.L();\n"
+ "\n"
+ "/** @const */ var M = {};\n"
+ "/** @typedef {D.L} */ M.L = D.L;\n"
+ "\n"
+ "use(M.L.A);",
"/** @constructor */ var D = function() {};\n"
+ "/** @constructor */ var D$L = function() {};\n"
+ "/** @type {D.L} */ var D$L$A = new D$L();\n"
+ "/** @typedef {D.L} */ var M$L = null\n"
+ "use(D$L$A);");
}
public void testGlobalAliasWithProperties1() {
test("var ns = {}; "
+ "/** @constructor */ ns.Foo = function() {};\n"
+ "/** @enum {number} */ ns.Foo.EventType = {A:1, B:2};"
+ "/** @constructor */ ns.Bar = ns.Foo;\n"
+ "var x = function() {use(ns.Bar.EventType.A)};\n"
+ "use(x);",
"/** @constructor */ var ns$Foo = function(){};"
+ "var ns$Foo$EventType$A = 1;"
+ "var ns$Foo$EventType$B = 2;"
+ "/** @constructor */ var ns$Bar = null;"
+ "var x = function(){use(ns$Foo$EventType$A)};"
+ "use(x);");
}
public void testGlobalAliasWithProperties2() {
// Reassignment of properties was necessary to prevent invalid code in
// previous iterations of this optimization. Verify we don't break
// code like this. Now it causes a back-off of the collapsing because
// the value is assigned more than once.
test("var ns = {}; "
+ "/** @constructor */ ns.Foo = function() {};\n"
+ "/** @enum {number} */ ns.Foo.EventType = {A:1, B:2};"
+ "/** @constructor */ ns.Bar = ns.Foo;\n"
+ "/** @enum {number} */ ns.Bar.EventType = ns.Foo.EventType;\n"
+ "var x = function() {use(ns.Bar.EventType.A)};\n"
+ "use(x)",
"/** @constructor */ var ns$Foo = function(){};"
+ "/** @enum {number} */ var ns$Foo$EventType = {A:1, B:2};"
+ "/** @constructor */ var ns$Bar = null;"
+ "/** @enum {number} */ ns$Foo$EventType = ns$Foo$EventType;\n"
+ "var x = function(){use(ns$Foo$EventType.A)};"
+ "use(x);");
}
public void testGlobalAliasWithProperties3() {
test("var ns = {}; "
+ "/** @constructor */ ns.Foo = function() {};\n"
+ "/** @enum {number} */ ns.Foo.EventType = {A:1, B:2};"
+ "/** @constructor */ ns.Bar = ns.Foo;\n"
+ "/** @enum {number} */ ns.Bar.Other = {X:1, Y:2};\n"
+ "var x = function() {use(ns.Bar.Other.X)};\n"
+ "use(x)",
"/** @constructor */ var ns$Foo=function(){};"
+ "var ns$Foo$EventType$A=1;"
+ "var ns$Foo$EventType$B=2;"
+ "/** @constructor */ var ns$Bar=null;"
+ "var ns$Foo$Other$X=1;"
+ "var ns$Foo$Other$Y=2;"
+ "var x=function(){use(ns$Foo$Other$X)};"
+ "use(x)\n");
}
public void testGlobalAliasWithProperties4() {
testSame(""
+ "var nullFunction = function(){};\n"
+ "var blob = {};\n"
+ "blob.init = nullFunction;\n"
+ "use(blob)");
}
public void testGlobalAliasWithProperties5() {
testSame(
"/** @constructor */ var blob = function() {}",
"var nullFunction = function(){};\n"
+ "blob.init = nullFunction;\n"
+ "use(blob.init)",
null);
}
public void testLocalAliasOfEnumWithInstanceofCheck() {
test(
"/** @constructor */\n"
+ "var Enums = function() {\n"
+ "};\n"
+ "\n"
+ "/** @enum {number} */\n"
+ "Enums.Fruit = {\n"
+ " APPLE: 1,\n"
+ " BANANA: 2,\n"
+ "};\n"
+ "\n"
+ "function foo(f) {\n"
+ " if (f instanceof Enums) { alert('what?'); return; }\n"
+ "\n"
+ " var Fruit = Enums.Fruit;\n"
+ " if (f == Fruit.APPLE) alert('apple');\n"
+ " if (f == Fruit.BANANA) alert('banana');\n"
+ "}",
"/** @constructor */\n"
+ "var Enums = function() {};\n"
+ "var Enums$Fruit$APPLE = 1;\n"
+ "var Enums$Fruit$BANANA = 2;\n"
+ "function foo(f) {\n"
+ " if (f instanceof Enums) { alert('what?'); return; }\n"
+ " var Fruit = null;\n"
+ " if (f == Enums$Fruit$APPLE) alert('apple');\n"
+ " if (f == Enums$Fruit$BANANA) alert('banana');\n"
+ "}");
}
public void testCollapsePropertiesOfClass1() {
test(
"/** @constructor */\n"
+ "var namespace = function() {};\n"
+ "goog.inherits(namespace, Object);\n"
+ "\n"
+ "namespace.includeExtraParam = true;\n"
+ "\n"
+ "/** @enum {number} */\n"
+ "namespace.Param = {\n"
+ " param1: 1,\n"
+ " param2: 2\n"
+ "};\n"
+ "\n"
+ "if (namespace.includeExtraParam) {\n"
+ " namespace.Param.optParam = 3;\n"
+ "}\n"
+ "\n"
+ "function f() {\n"
+ " var Param = namespace.Param;\n"
+ " log(namespace.Param.optParam);\n"
+ " log(Param.optParam);\n"
+ "}",
"/** @constructor */\n"
+ "var namespace = function() {};\n"
+ "goog.inherits(namespace, Object);\n"
+ "var namespace$includeExtraParam = true;\n"
+ "var namespace$Param$param1 = 1;\n"
+ "var namespace$Param$param2 = 2;\n"
+ "if (namespace$includeExtraParam) {\n"
+ " var namespace$Param$optParam = 3;\n"
+ "}\n"
+ "function f() {\n"
+ " var Param = null;\n"
+ " log(namespace$Param$optParam);\n"
+ " log(namespace$Param$optParam);\n"
+ "}");
}
public void testCollapsePropertiesOfClass2() {
test(
"var goog = goog || {};\n"
+ "goog.addSingletonGetter = function(cls) {};\n"
+ "\n"
+ "var a = {};\n"
+ "\n"
+ "/** @constructor */\n"
+ "a.b = function() {};\n"
+ "goog.addSingletonGetter(a.b);\n"
+ "a.b.prototype.get = function(key) {};\n"
+ "\n"
+ "/** @constructor */\n"
+ "a.b.c = function() {};\n"
+ "a.b.c.XXX = new a.b.c();\n"
+ "\n"
+ "function f() {\n"
+ " var x = a.b.getInstance();\n"
+ " var Key = a.b.c;\n"
+ " x.get(Key.XXX);\n"
+ "}",
"var goog = goog || {};\n"
+ "var goog$addSingletonGetter = function(cls) {};\n"
+ "/** @constructor */\n"
+ "var a$b = function() {};\n"
+ "goog$addSingletonGetter(a$b);\n"
+ "a$b.prototype.get = function(key) {};\n"
+ "/** @constructor */\n"
+ "var a$b$c = function() {};\n"
+ "var a$b$c$XXX = new a$b$c();\n"
+ "\n"
+ "function f() {\n"
+ " var x = a$b.getInstance();\n"
+ " var Key = null;\n"
+ " x.get(a$b$c$XXX);\n"
+ "}");
}
public void test_b19179602() {
test(
"var a = {};\n"
+ "/** @constructor */ a.b = function() {};\n"
+ "a.b.staticProp = 5;\n"
+ "function f() {\n"
+ " while (true) {\n"
// b is declared inside a loop, so it is reassigned multiple times
+ " var b = a.b;\n"
+ " alert(b.staticProp);\n"
+ " }\n"
+ "}\n",
"/** @constructor */ var a$b = function() {};\n"
+ "var a$b$staticProp = 5;\n"
+ "\n"
+ "function f() {\n"
+ " while (true) {\n"
+ " var b = a$b;\n"
+ " alert(b.staticProp);\n"
+ " }\n"
+ "}",
null, AggressiveInlineAliases.UNSAFE_CTOR_ALIASING);
}
public void test_b19179602_declareOutsideLoop() {
test(
"var a = {};\n"
+ "/** @constructor */ a.b = function() {};\n"
+ "a.b.staticProp = 5;\n"
+ "function f() {\n"
// b is declared outside the loop
+ " var b = a.b;\n"
+ " while (true) {\n"
+ " alert(b.staticProp);\n"
+ " }\n"
+ "}",
"/** @constructor */"
+ "var a$b = function() {};\n"
+ "var a$b$staticProp = 5;\n"
+ "\n"
+ "function f() {\n"
+ " var b = null;\n"
+ " while (true) {\n"
+ " alert(a$b$staticProp);\n"
+ " }\n"
+ "}");
}
public void testCtorManyAssignmentsDontInlineWarn() {
test(
"var a = {};\n"
+ "/** @constructor */ a.b = function() {};\n"
+ "a.b.staticProp = 5;\n"
+ "function f(y, z) {\n"
+ " var x = a.b;\n"
+ " if (y) {\n"
+ " x = z;\n"
+ " }\n"
+ " return x.staticProp;\n"
+ "}",
"/** @constructor */ var a$b = function() {};\n"
+ "var a$b$staticProp = 5;\n"
+ "function f(y, z) {\n"
+ " var x = a$b;\n"
+ " if (y) {\n"
+ " x = z;\n"
+ " }\n"
+ " return x.staticProp;\n"
+ "}",
null, AggressiveInlineAliases.UNSAFE_CTOR_ALIASING);
}
public void testCodeGeneratedByGoogModule() {
// The static property is added to the exports object
test(
LINE_JOINER.join(
"var $jscomp = {};",
"$jscomp.scope = {};",
"/** @constructor */",
"$jscomp.scope.Foo = function() {};",
"var exports = $jscomp.scope.Foo;",
"exports.staticprop = {A:1};",
"var y = exports.staticprop.A;"),
LINE_JOINER.join(
"/** @constructor */",
"var $jscomp$scope$Foo = function() {}",
"var exports = null;",
"var $jscomp$scope$Foo$staticprop$A = 1;",
"var y = null;"));
// The static property is added to the constructor
test(
LINE_JOINER.join(
"var $jscomp = {};",
"$jscomp.scope = {};",
"/** @constructor */",
"$jscomp.scope.Foo = function() {};",
"$jscomp.scope.Foo.staticprop = {A:1};",
"var exports = $jscomp.scope.Foo;",
"var y = exports.staticprop.A;"),
LINE_JOINER.join(
"/** @constructor */",
"var $jscomp$scope$Foo = function() {}",
"var $jscomp$scope$Foo$staticprop$A = 1;",
"var exports = null;",
"var y = null;"));
}
public void testInlineCtorInObjLit() {
test(
LINE_JOINER.join(
"/** @constructor */",
"function Foo() {}",
"",
"/** @constructor */",
"var Bar = Foo;",
"",
"var objlit = {",
" 'prop' : Bar",
"};"),
LINE_JOINER.join(
"/** @constructor */",
"function Foo() {}",
"/** @constructor */",
"var Bar = null;",
"var objlit$prop = Foo;"));
}
public void testNoCollapseExportedNode() {
test(
"var x = {}; x.y = {}; var dontExportMe = x.y; use(dontExportMe);",
"var x$y = {}; var dontExportMe = null; use(x$y);");
test(
"var x = {}; x.y = {}; var _exportMe = x.y;",
"var x$y = {}; var _exportMe = x$y;");
}
public void testDontCrashCtorAliasWithEnum() {
test(
"var ns = {};\n"
+ "/** @constructor */\n"
+ "ns.Foo = function () {};\n"
+ "var Bar = ns.Foo;\n"
+ "/** @const @enum */\n"
+ "Bar.prop = { A: 1 };",
"/** @constructor */\n"
+ "var ns$Foo = function(){};\n"
+ "var Bar = null;\n"
+ "var ns$Foo$prop$A = 1");
}
public void testDontCrashNamespaceAliasAcrossScopes() {
test(
"var ns = {};\n"
+ "ns.VALUE = 0.01;\n"
+ "function f() {\n"
+ " var constants = ns;\n"
+ " (function() {\n"
+ " var x = constants.VALUE;\n"
+ " })();\n"
+ "}",
null);
}
}