/*
* 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.NAMESPACE_REDEFINED_WARNING;
import static com.google.javascript.jscomp.CollapseProperties.UNSAFE_NAMESPACE_WARNING;
/**
* Tests {@link CollapseProperties}.
*
*/
public final class CollapsePropertiesTest 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 CollapsePropertiesTest() {
super(EXTERNS);
}
@Override
protected CompilerPass getProcessor(final Compiler compiler) {
return new CollapseProperties(compiler);
}
@Override
public void setUp() {
enableNormalize();
}
@Override
protected int getNumRepetitions() {
return 1;
}
public void testMultiLevelCollapse() {
test("var a = {}; a.b = {}; a.b.c = {}; var d = 1; d = a.b.c;",
"var a$b$c = {}; var d = 1; d = a$b$c;");
test(
"var a = {}; a.b = {}; /** @nocollapse */ a.b.c = {}; var d = 1; d = a.b.c;",
"var a$b = {}; /** @nocollapse */ a$b.c = {}; var d = 1; d = a$b.c;");
}
public void testDecrement() {
test("var a = {}; a.b = 5; a.b--; a.b = 5",
"var a$b = 5; a$b--; a$b = 5");
}
public void testIncrement() {
test("var a = {}; a.b = 5; a.b++; a.b = 5",
"var a$b = 5; a$b++; a$b = 5");
}
public void testObjLitDeclarationWithGet1() {
testSame("var a = {get 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 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 testObjLitAssignmentDepth1() {
test("var a = {b: {}, c: {}}; var d = 1; var e = 1; d = a.b; e = a.c",
"var a$b = {}; var a$c = {}; var d = 1; var e = 1; d = a$b; e = a$c");
test("var a = {b: {}, /** @nocollapse */ c: {}}; var d = 1; d = a.b; var e = 1; e = a.c",
"var a$b = {}; var a = { /** @nocollapse */c: {}}; var d = 1; d = a$b; var e = 1; e = a.c");
}
public void testObjLitAssignmentDepth2() {
test("var a = {}; a.b = {c: {}, d: {}}; var e = 1; e = a.b.c; var f = 1; f = a.b.d",
"var a$b$c = {}; var a$b$d = {}; var e = 1; e = a$b$c; var f = 1; f = a$b$d;");
test("var a = {}; a.b = {c: {}, /** @nocollapse */ d: {}}; var e = 1; e = a.b.c;"
+ "var f = 1; f = a.b.d",
"var a$b$c = {}; var a$b = { /** @nocollapse */ d: {}}; var e = 1; e = a$b$c;"
+ "var f = 1; f = a$b.d;");
}
public void testGlobalObjectDeclaredToPreserveItsPreviousValue1() {
test("var a = a ? a : {}; a.c = 1;",
"var a = a ? a : {}; var a$c = 1;");
test("var a = a ? a : {}; /** @nocollapse */ a.c = 1;",
"var a = a ? a : {}; /** @nocollapse */ a.c = 1;");
}
public void testGlobalObjectDeclaredToPreserveItsPreviousValue2() {
test("var a = a || {}; a.c = 1;",
"var a = a || {}; var a$c = 1;");
testSame("var a = a || {}; /** @nocollapse */ a.c = 1;");
}
public void testGlobalObjectDeclaredToPreserveItsPreviousValue3() {
test("var a = a || {get b() {}}; a.c = 1;",
"var a = a || {get b() {}}; var a$c = 1;");
testSame("var a = a || {get b() {}}; /** @nocollapse */ a.c = 1;");
}
public void testGlobalObjectNameInBooleanExpressionDepth1_1() {
test("var a = {b: 0}; a.c = 1; if (a) x();",
"var a$b = 0; var a = {}; var a$c = 1; if (a) x();");
test("var a = {/** @nocollapse */ b: 0}; a.c = 1; if (a) x();",
"var a = {/** @nocollapse */ b: 0}; var a$c = 1; if (a) x();");
test("var a = {b: 0}; /** @nocollapse */ a.c = 1; if (a) x();",
"var a$b = 0; var a = {}; /** @nocollapse */ a.c = 1; if (a) x();");
}
public void testGlobalObjectNameInBooleanExpressionDepth1_2() {
test("var a = {b: 0}; a.c = 1; if (!(a && a.c)) x();",
"var a$b = 0; var a = {}; var a$c = 1; if (!(a && a$c)) x();");
test("var a = {/** @nocollapse */ b: 0}; a.c = 1; if (!(a && a.c)) x();",
"var a = {/** @nocollapse */ b: 0}; var a$c = 1; if (!(a && a$c)) x();");
test("var a = {b: 0}; /** @nocollapse */ a.c = 1; if (!(a && a.c)) x();",
"var a$b = 0; var a = {}; /** @nocollapse */ a.c = 1; if (!(a && a.c)) x();");
}
public void testGlobalObjectNameInBooleanExpressionDepth1_3() {
test("var a = {b: 0}; a.c = 1; while (a || a.c) x();",
"var a$b = 0; var a = {}; var a$c = 1; while (a || a$c) x();");
test("var a = {/** @nocollapse */ b: 0}; a.c = 1; while (a || a.c) x();",
"var a = {/** @nocollapse */ b: 0}; var a$c = 1; while (a || a$c) x();");
test("var a = {b: 0}; /** @nocollapse */ a.c = 1; while (a || a.c) x();",
"var a$b = 0; var a = {}; /** @nocollapse */ a.c = 1; while (a || a.c) x();");
}
public void testGlobalObjectNameInBooleanExpressionDepth1_4() {
testSame("var a = {}; a.c = 1; var d = a || {}; a.c;");
testSame("var a = {}; /** @nocollapse */ a.c = 1; var d = a || {}; a.c;");
}
public void testGlobalObjectNameInBooleanExpressionDepth1_5() {
testSame("var a = {}; a.c = 1; var d = a.c || a; a.c;");
testSame("var a = {}; /** @nocollapse */ a.c = 1; var d = a.c || a; a.c;");
}
public void testGlobalObjectNameInBooleanExpressionDepth1_6() {
test("var a = {b: 0}; a.c = 1; var d = !(a.c || a); a.c;",
"var a$b = 0; var a = {}; var a$c = 1; var d = !(a$c || a); a$c;");
test("var a = {/** @nocollapse */ b: 0}; a.c = 1; var d = !(a.c || a); a.c;",
"var a = {/** @nocollapse */ b: 0}; var a$c = 1; var d = !(a$c || a); a$c;");
test("var a = {b: 0}; /** @nocollapse */ a.c = 1; var d = !(a.c || a); a.c;",
"var a$b = 0; var a = {}; /** @nocollapse */ a.c = 1; var d = !(a.c || a); a.c;");
}
public void testGlobalObjectNameInBooleanExpressionDepth2() {
test("var a = {b: {}}; a.b.c = 1; if (a.b) x(a.b.c);",
"var a$b = {}; var a$b$c = 1; if (a$b) x(a$b$c);");
testSame("var a = {/** @nocollapse */ b: {}}; a.b.c = 1; if (a.b) x(a.b.c);");
test("var a = {b: {}}; /** @nocollapse */ a.b.c = 1; if (a.b) x(a.b.c);",
"var a$b = {}; /** @nocollapse */ a$b.c = 1; if (a$b) x(a$b.c);");
}
public void testGlobalObjectNameInBooleanExpressionDepth3() {
// TODO(user): Make CollapseProperties even more aggressive so that
// a$b.z gets collapsed. Right now, it doesn't get collapsed because the
// expression (a.b && a.b.c) could return a.b. But since it returns a.b iff
// a.b *is* safely collapsible, the Boolean logic should be smart enough to
// only consider the right side of the && as aliasing.
test("var a = {}; a.b = {}; /** @constructor */ a.b.c = function(){};"
+ " a.b.z = 1; var d = a.b && a.b.c;",
"var a$b = {}; /** @constructor */ var a$b$c = function(){};"
+ " a$b.z = 1; var d = a$b && a$b$c;", null,
UNSAFE_NAMESPACE_WARNING);
}
public void testGlobalFunctionNameInBooleanExpressionDepth1() {
test("function a() {} a.c = 1; if (a) x(a.c);",
"function a() {} var a$c = 1; if (a) x(a$c);");
test("function a() {} /** @nocollapse */ a.c = 1; if (a) x(a.c);",
"function a() {} /** @nocollapse */ a.c = 1; if (a) x(a.c);");
}
public void testGlobalFunctionNameInBooleanExpressionDepth2() {
test("var a = {b: function(){}}; a.b.c = 1; if (a.b) x(a.b.c);",
"var a$b = function(){}; var a$b$c = 1; if (a$b) x(a$b$c);");
testSame("var a = {/** @nocollapse */ b: function(){}}; a.b.c = 1; "
+ "if (a.b) x(a.b.c);");
test("var a = {b: function(){}}; /** @nocollapse */ a.b.c = 1; "
+ "if (a.b) x(a.b.c);",
"var a$b = function(){}; /** @nocollapse */ a$b.c = 1; if (a$b) x(a$b.c);");
}
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 testAliasCreatedForObjectDepth2_1() {
test("var a = {}; a.b = {c: 0}; var d = 1; d = a.b; a.b.c == d.c;",
"var a$b = {c: 0}; var d = 1; d = a$b; a$b.c == d.c;");
test("var a = {}; /** @nocollapse */ a.b = {c: 0}; var d = 1; d = a.b; "
+ "a.b.c == d.c;",
"var a = {}; /** @nocollapse */ a.b = {c: 0}; var d = 1; d = a.b; a.b.c == d.c;");
}
public void testAliasCreatedForObjectDepth2_2() {
test("var a = {}; a.b = {c: 0}; for (var p in a.b) { e(a.b[p]); }",
"var a$b = {c: 0}; for (var p in a$b) { e(a$b[p]); }");
}
public void testEnumDepth1() {
test("/** @enum */ var a = {b: 0, c: 1};",
"var a$b = 0; var a$c = 1;");
test(
"/** @enum */ var a = { /** @nocollapse */ b: 0, c: 1};",
"var a$c = 1; /** @enum */ var a = { /** @nocollapse */ b: 0};");
}
public void testEnumDepth2() {
test("var a = {}; /** @enum */ a.b = {c: 0, d: 1};",
"var a$b$c = 0; var a$b$d = 1;");
testSame("var a = {}; /** @nocollapse @enum */ a.b = {c: 0, d: 1};");
}
public void testAliasCreatedForEnumDepth1_1() {
// An enum's values are always collapsed, even if the enum object is
// referenced in a such a way that an alias is created for it.
// Unless an enum property has @nocollapse
test("/** @enum */ var a = {b: 0}; var c = 1; c = a; c.b = 1; a.b != c.b;",
"var a$b = 0; /** @enum */ var a = {b: a$b}; var c = 1; c = a; c.b = 1; a$b != c.b;");
test("/** @enum */ var a = { /** @nocollapse */ b: 0}; var c = 1; c = a; c.b = 1; a.b == c.b;",
"/** @enum */ var a = { /** @nocollapse */ b: 0}; var c = 1; c = a; c.b = 1; a.b == c.b;");
}
public void testAliasCreatedForEnumDepth1_2() {
test("/** @enum */ var a = {b: 0}; f(a); a.b;",
"var a$b = 0; /** @enum */ var a = {b: a$b}; f(a); a$b;");
}
public void testAliasCreatedForEnumDepth1_3() {
test("/** @enum */ var a = {b: 0}; new f(a); a.b;",
"var a$b = 0; /** @enum */ var a = {b: a$b}; new f(a); a$b;");
}
public void testAliasCreatedForEnumDepth1_4() {
test("/** @enum */ var a = {b: 0}; for (var p in a) { f(a[p]); }",
"var a$b = 0; /** @enum */ var a = {b: a$b}; for (var p in a) { f(a[p]); }");
}
public void testAliasCreatedForEnumDepth2_1() {
test("var a = {}; /** @enum */ a.b = {c: 0};"
+ "var d = 1; d = a.b; d.c = 1; a.b.c != d.c;",
"var a$b$c = 0; /** @enum */ var a$b = {c: a$b$c};"
+ "var d = 1; d = a$b; d.c = 1; a$b$c != d.c;");
testSame("var a = {}; /** @nocollapse @enum */ a.b = {c: 0};"
+ "var d = 1; d = a.b; d.c = 1; a.b.c == d.c;");
test("var a = {}; /** @enum */ a.b = {/** @nocollapse */ c: 0};"
+ "var d = 1; d = a.b; d.c = 1; a.b.c == d.c;",
"/** @enum */ var a$b = { /** @nocollapse */ c: 0};"
+ "var d = 1; d = a$b; d.c = 1; a$b.c == d.c;");
}
public void testAliasCreatedForEnumDepth2_2() {
test("var a = {}; /** @enum */ a.b = {c: 0};"
+ "for (var p in a.b) { f(a.b[p]); }",
"var a$b$c = 0; /** @enum */ var a$b = {c: a$b$c};"
+ "for (var p in a$b) { f(a$b[p]); }");
}
public void testAliasCreatedForEnumDepth2_3() {
test("var a = {}; var d = 1; d = a; /** @enum */ a.b = {c: 0};"
+ "for (var p in a.b) { f(a.b[p]); }",
"var a = {}; var d = 1; d = a; var a$b$c = 0; /** @enum */ var a$b = {c: a$b$c};"
+ "for (var p in a$b) { f(a$b[p]); }",
null, UNSAFE_NAMESPACE_WARNING);
}
public void testAliasCreatedForEnumOfObjects() {
test("var a = {}; /** @enum {Object} */ a.b = {c: {d: 1}}; a.b.c; searchEnum(a.b);",
"var a$b$c = {d: 1}; /** @enum {Object} */ var a$b = {c: a$b$c}; a$b$c; searchEnum(a$b)");
}
public void testAliasCreatedForEnumOfObjects2() {
test("var a = {}; "
+ "/** @enum {Object} */ a.b = {c: {d: 1}}; a.b.c.d;"
+ "searchEnum(a.b);",
"var a$b$c = {d: 1}; /** @enum {Object} */ var a$b = {c: a$b$c}; a$b$c.d; "
+ "searchEnum(a$b)");
}
public void testAliasCreatedForPropertyOfEnumOfObjects() {
test("var a = {}; "
+ "/** @enum {Object} */ a.b = {c: {d: 1}}; a.b.c;"
+ "searchEnum(a.b.c);",
"var a$b$c = {d: 1}; a$b$c; searchEnum(a$b$c);");
}
public void testAliasCreatedForPropertyOfEnumOfObjects2() {
test("var a = {}; "
+ "/** @enum {Object} */ a.b = {c: {d: 1}}; a.b.c.d;"
+ "searchEnum(a.b.c);",
"var a$b$c = {d: 1}; a$b$c.d; searchEnum(a$b$c);");
}
public void testMisusedEnumTag() {
testSame("var a = {}; var d = 1; d = a; a.b = function() {}; /** @enum */ a.b.c = 0; a.b.c;");
}
public void testAliasCreatedForFunctionDepth1_1() {
testSame("var a = function(){}; a.b = 1; var c = 1; c = a; c.b = 2; a.b != c.b;");
}
public void testAliasCreatedForFunctionDepth1_2() {
testSame("var a = function(){}; a.b = 1; f(a); a.b;");
}
public void testAliasCreatedForCtorDepth1_2() {
test("/** @constructor */ var a = function(){}; a.b = 1; f(a); a.b;",
"/** @constructor */ var a = function(){}; var a$b = 1; f(a); a$b;");
testSame("/** @constructor */ var a = function(){}; /** @nocollapse */ a.b = 1; f(a); a.b;");
}
public void testAliasCreatedForFunctionDepth1_3() {
testSame("var a = function(){}; a.b = 1; new f(a); a.b;");
}
public void testAliasCreatedForCtorDepth1_3() {
test("/** @constructor */ var a = function(){}; a.b = 1; new f(a); a.b;",
"/** @constructor */ var a = function(){}; var a$b = 1; new f(a); a$b;");
testSame("/** @constructor */ var a = function(){};"
+ "/** @nocollapse */ a.b = 1; new f(a); a.b;");
}
public void testAliasCreatedForClassDepth1_2() {
test(
"var a = {}; /** @constructor */ a.b = function(){}; f(a); a.b;",
"var a = {}; /** @constructor */ var a$b = function(){}; f(a); a$b;",
null,
UNSAFE_NAMESPACE_WARNING);
}
public void testAliasCreatedForClassDepth1_3() {
test(
"var a = {}; /** @constructor */ a.b = function(){}; new f(a); a.b;",
"var a = {}; /** @constructor */ var a$b = function(){}; new f(a); a$b;",
null,
UNSAFE_NAMESPACE_WARNING);
}
public void testAliasCreatedForClassDepth2_1() {
test(
LINE_JOINER.join(
"var a = {};",
"a.b = {};",
"/** @constructor */",
"a.b.c = function(){};",
"var d = 1;",
"d = a.b;",
"a.b.c != d.c;"),
LINE_JOINER.join(
"var a$b = {}; ",
"/** @constructor */",
"var a$b$c = function(){};",
"var d = 1;",
"d = a$b;",
"a$b$c != d.c;"),
null,
UNSAFE_NAMESPACE_WARNING);
test(
LINE_JOINER.join(
"var a = {};",
"a.b = {};",
" /** @constructor @nocollapse */",
" a.b.c = function(){}; ",
"var d = 1;",
" d = a.b; ",
"a.b.c == d.c;"),
LINE_JOINER.join(
"var a$b = {};",
"/** @constructor @nocollapse */",
"a$b.c = function(){};",
"var d = 1; ",
"d = a$b;",
"a$b.c == d.c;"),
null,
UNSAFE_NAMESPACE_WARNING);
}
public void testAliasCreatedForClassDepth2_2() {
test(
"var a = {}; a.b = {}; /** @constructor */ a.b.c = function(){}; f(a.b); a.b.c;",
"var a$b = {}; /** @constructor */ var a$b$c = function(){}; f(a$b); a$b$c;",
null,
UNSAFE_NAMESPACE_WARNING);
}
public void testAliasCreatedForClassDepth2_3() {
test(
"var a = {}; a.b = {}; /** @constructor */ a.b.c = function(){}; new f(a.b); a.b.c;",
"var a$b = {}; /** @constructor */ var a$b$c = function(){}; new f(a$b); a$b$c;",
null,
UNSAFE_NAMESPACE_WARNING);
}
public void testAliasCreatedForClassProperty() {
test(
"var a = {}; /** @constructor */ a.b = function(){}; a.b.c = {d:3}; new f(a.b.c); a.b.c.d;",
"/** @constructor */ var a$b = function(){}; var a$b$c = {d:3}; new f(a$b$c); a$b$c.d;");
testSame("var a = {}; /** @constructor @nocollapse */ a.b = function(){};"
+ "a.b.c = {d: 3}; new f(a.b.c); a.b.c.d;");
test(
LINE_JOINER.join(
"var a = {};",
"/** @constructor */",
"a.b = function(){};",
"/** @nocollapse */",
" a.b.c = {d: 3};",
" new f(a.b.c);",
" a.b.c.d;"),
LINE_JOINER.join(
"/** @constructor */",
" var a$b = function(){};",
" /** @nocollapse */",
" a$b.c = {d:3};",
" new f(a$b.c); ",
"a$b.c.d;"));
}
public void testNestedObjLit() {
test("var a = {}; a.b = {f: 0, c: {d: 1}}; var e = 1; e = a.b.c.d",
"var a$b$f = 0; var a$b$c$d = 1; var e = 1; e = a$b$c$d;");
test("var a = {}; a.b = {f: 0, /** @nocollapse */ c: {d: 1}}; var e = 1; e = a.b.c.d",
"var a$b$f = 0; var a$b ={ /** @nocollapse */ c: { d: 1 }}; var e = 1; e = a$b.c.d;");
test("var a = {}; a.b = {f: 0, c: {/** @nocollapse */ d: 1}}; var e = 1; e = a.b.c.d",
"var a$b$f = 0; var a$b$c = { /** @nocollapse */ d: 1}; var e = 1; e = a$b$c.d;");
}
public void testPropGetInsideAnObjLit() {
test("var x = {}; x.y = 1; var a = {}; a.b = {c: x.y}",
"var x$y = 1; var a$b$c = x$y;");
test(
"var x = {}; /** @nocollapse */ x.y = 1; var a = {}; a.b = {c: x.y}",
"var x = {}; /** @nocollapse */ x.y = 1; var a$b$c = x.y;");
test(
"var x = {}; x.y = 1; var a = {}; a.b = { /** @nocollapse */ c: x.y}",
"var x$y = 1; var a$b = { /** @nocollapse */ c: x$y};");
testSame("var x = {}; /** @nocollapse */ x.y = 1; var a = {};"
+ "/** @nocollapse */ a.b = {c: x.y}");
}
public void testObjLitWithQuotedKeyThatDoesNotGetRead() {
test("var a = {}; a.b = {c: 0, 'd': 1}; var e = 1; e = a.b.c;",
"var a$b$c = 0; var a$b$d = 1; var e = 1; e = a$b$c;");
test(
"var a = {}; a.b = {c: 0, /** @nocollapse */ 'd': 1}; var e = 1; e = a.b.c;",
"var a$b$c = 0; var a$b = {/** @nocollapse */ 'd': 1}; var e = 1; e = a$b$c;");
}
public void testObjLitWithQuotedKeyThatGetsRead() {
test("var a = {}; a.b = {c: 0, 'd': 1}; var e = a.b['d'];",
"var a$b = {c: 0, 'd': 1}; var e = a$b['d'];");
test(
"var a = {}; a.b = {c: 0, /** @nocollapse */ 'd': 1}; var e = a.b['d'];",
"var a$b = {c: 0, /** @nocollapse */ 'd': 1}; var e = a$b['d'];");
}
public void testFunctionWithQuotedPropertyThatDoesNotGetRead() {
test("var a = {}; a.b = function() {}; a.b['d'] = 1;",
"var a$b = function() {}; a$b['d'] = 1;");
test("var a = {}; /** @nocollapse */ a.b = function() {}; a.b['d'] = 1;",
"var a = {}; /** @nocollapse */ a.b = function() {}; a.b['d'] = 1;");
test("var a = {}; a.b = function() {}; /** @nocollapse */ a.b['d'] = 1;",
"var a$b = function() {}; /** @nocollapse */ a$b['d'] = 1;");
}
public void testFunctionWithQuotedPropertyThatGetsRead() {
test("var a = {}; a.b = function() {}; a.b['d'] = 1; f(a.b['d']);",
"var a$b = function() {}; a$b['d'] = 1; f(a$b['d']);");
testSame("var a = {}; /** @nocollapse */ a.b = function() {};"
+ "a.b['d'] = 1; f(a.b['d']);");
test("var a = {}; a.b = function() {}; /** @nocollapse */ a.b['d'] = 1; f(a.b['d']);",
"var a$b = function() {}; /** @nocollapse */ a$b['d'] = 1; f(a$b['d']);");
}
public void testObjLitAssignedToMultipleNames1() {
// An object literal that's assigned to multiple names isn't collapsed.
testSame("var a = b = {c: 0, d: 1}; var e = a.c; var f = b.d;");
testSame("var a = b = {c: 0, /** @nocollapse */ d: 1}; var e = a.c;"
+ "var f = b.d;");
}
public void testObjLitAssignedToMultipleNames2() {
testSame("a = b = {c: 0, d: 1}; var e = a.c; var f = b.d;");
}
public void testObjLitRedefinedInGlobalScope() {
testSame("a = {b: 0}; a = {c: 1}; var d = a.b; var e = a.c;");
}
public void testObjLitRedefinedInLocalScope() {
test("var a = {}; a.b = {c: 0}; function d() { a.b = {c: 1}; } e(a.b.c);",
"var a$b = {c: 0}; function d() { a$b = {c: 1}; } e(a$b.c);");
testSame("var a = {};/** @nocollapse */ a.b = {c: 0}; function d() { a.b = {c: 1};} e(a.b.c);");
// redefinition with @nocollapse
test("var a = {}; a.b = {c: 0}; "
+ "function d() { a.b = {/** @nocollapse */ c: 1}; } e(a.b.c);",
"var a$b = {c: 0}; function d() { a$b = {/** @nocollapse */ c: 1}; } e(a$b.c);");
}
public void testObjLitAssignedInTernaryExpression1() {
testSame("a = x ? {b: 0} : d; var c = a.b;");
}
public void testObjLitAssignedInTernaryExpression2() {
testSame("a = x ? {b: 0} : {b: 1}; var c = a.b;");
}
public void testGlobalVarSetToObjLitConditionally1() {
testSame("var a; if (x) a = {b: 0}; var c = x ? a.b : 0;");
}
public void testGlobalVarSetToObjLitConditionally1b() {
test("if (x) var a = {b: 0}; var c = x ? a.b : 0;",
"if (x) var a$b = 0; var c = x ? a$b : 0;");
testSame("if (x) var a = { /** @nocollapse */ b: 0}; var c = x ? a.b : 0;");
}
public void testGlobalVarSetToObjLitConditionally2() {
test("if (x) var a = {b: 0}; var c = 1; c = a.b; var d = a.c;",
"if (x){ var a$b = 0; var a = {}; }var c = 1; c = a$b; var d = a.c;");
testSame("if (x) var a = {/** @nocollapse */ b: 0}; var c = 1; c = a.b; var d = a.c;");
}
public void testGlobalVarSetToObjLitConditionally3() {
testSame("var a; if (x) a = {b: 0}; else a = {b: 1}; var c = a.b;");
testSame("var a; if (x) a = {b: 0}; else a = {/** @nocollapse */ b: 1};"
+ "var c = a.b;");
}
public void testObjectPropertySetToObjLitConditionally() {
test("var a = {}; if (x) a.b = {c: 0}; var d = a.b ? a.b.c : 0;",
"if (x){ var a$b$c = 0; var a$b = {} } var d = a$b ? a$b$c : 0;");
test(
"var a = {}; if (x) a.b = {/** @nocollapse */ c: 0}; var d = a.b ? a.b.c : 0;",
"if (x){ var a$b = {/** @nocollapse */ c: 0};} var d = a$b ? a$b.c : 0;");
}
public void testFunctionPropertySetToObjLitConditionally() {
test("function a() {} if (x) a.b = {c: 0}; var d = a.b ? a.b.c : 0;",
"function a() {} if (x){ var a$b$c = 0; var a$b = {} }"
+ "var d = a$b ? a$b$c : 0;");
testSame("function a() {} if (x) /** @nocollapse */ a.b = {c: 0};"
+ "var d = a.b ? a.b.c : 0;");
test("function a() {} if (x) a.b = {/** @nocollapse */ c: 0}; var d = a.b ? a.b.c : 0;",
"function a() {} if (x){ var a$b = {/** @nocollapse */ c: 0}; } var d = a$b ? a$b.c : 0;");
}
public void testPrototypePropertySetToAnObjectLiteral() {
test("var a = {b: function(){}}; a.b.prototype.c = {d: 0};",
"var a$b = function(){}; a$b.prototype.c = {d: 0};");
testSame("var a = {/** @nocollapse */ b: function(){}};"
+ "a.b.prototype.c = {d: 0};");
}
public void testObjectPropertyResetInLocalScope() {
test("var z = {}; z.a = 0; function f() {z.a = 5; return z.a}",
"var z$a = 0; function f() {z$a = 5; return z$a}");
testSame("var z = {}; z.a = 0;"
+ "function f() { /** @nocollapse */ z.a = 5; return z.a}");
testSame("var z = {}; /** @nocollapse */ z.a = 0;"
+ "function f() {z.a = 5; return z.a}");
}
public void testFunctionPropertyResetInLocalScope() {
test("function z() {} z.a = 0; function f() {z.a = 5; return z.a}",
"function z() {} var z$a = 0; function f() {z$a = 5; return z$a}");
testSame("function z() {} /** @nocollapse */ z.a = 0;"
+ "function f() {z.a = 5; return z.a}");
testSame("function z() {} z.a = 0;"
+ "function f() { /** @nocollapse */ z.a = 5; return z.a}");
}
public void testNamespaceResetInGlobalScope1() {
test(
"var a = {}; /** @constructor */ a.b = function() {}; a = {};",
"var a = {}; /** @constructor */ var a$b = function() {}; a = {};",
null,
NAMESPACE_REDEFINED_WARNING);
testSame("var a = {}; /** @constructor @nocollapse */a.b = function() {};"
+ "a = {};", NAMESPACE_REDEFINED_WARNING);
}
public void testNamespaceResetInGlobalScope2() {
test("var a = {}; a = {}; /** @constructor */ a.b = function() {};",
"var a = {}; a = {}; /** @constructor */ var a$b = function() {};",
null, NAMESPACE_REDEFINED_WARNING);
testSame("var a = {}; a = {}; /** @constructor @nocollapse */a.b = function() {};",
NAMESPACE_REDEFINED_WARNING);
}
public void testNamespaceResetInGlobalScope3() {
test("var a = {}; /** @constructor */ a.b = function() {}; a = a || {};",
"var a = {}; /** @constructor */ var a$b = function() {}; a = a || {};");
testSame("var a = {}; /** @constructor @nocollapse */ a.b = function() {}; a = a || {};");
}
public void testNamespaceResetInGlobalScope4() {
test("var a = {}; /** @constructor */ a.b = function() {}; var a = a || {};",
"var a = {}; /** @constructor */ var a$b = function() {}; var a = a || {};");
testSame("var a = {}; /** @constructor @nocollapse */a.b = function() {}; var a = a || {};");
}
public void testNamespaceResetInLocalScope1() {
test("var a = {}; /** @constructor */ a.b = function() {}; function f() { a = {}; }",
"var a = {}; /** @constructor */ var a$b = function() {}; function f() { a = {}; }",
null, NAMESPACE_REDEFINED_WARNING);
testSame("var a = {}; /** @constructor @nocollapse */a.b = function() {};"
+ " function f() { a = {}; }",
NAMESPACE_REDEFINED_WARNING);
}
public void testNamespaceResetInLocalScope2() {
test("var a = {}; function f() { a = {}; } /** @constructor */ a.b = function() {};",
"var a = {}; function f() { a = {}; } /** @constructor */ var a$b = function() {};",
null, NAMESPACE_REDEFINED_WARNING);
testSame("var a = {}; function f() { a = {}; }"
+ " /** @constructor @nocollapse */a.b = function() {};",
NAMESPACE_REDEFINED_WARNING);
}
public void testNamespaceDefinedInLocalScope() {
test(
"var a = {}; (function() { a.b = {}; })(); /** @constructor */ a.b.c = function() {};",
"var a$b; (function() { a$b = {}; })(); /** @constructor */ var a$b$c = function() {};");
test(
"var a = {}; (function() { /** @nocollapse */ a.b = {}; })();"
+ " /** @constructor */a.b.c = function() {};",
"var a = {}; (function() { /** @nocollapse */ a.b = {}; })();"
+ " /** @constructor */ var a$b$c = function() {};");
test(
"var a = {}; (function() { a.b = {}; })();"
+ " /** @constructor @nocollapse */a.b.c = function() {};",
"var a$b; (function() { a$b = {}; })();"
+ "/** @constructor @nocollapse */ a$b.c = function() {};");
}
public void testAddPropertyToObjectInLocalScopeDepth1() {
test("var a = {b: 0}; function f() { a.c = 5; return a.c; }",
"var a$b = 0; var a$c; function f() { a$c = 5; return a$c; }");
}
public void testAddPropertyToObjectInLocalScopeDepth2() {
test("var a = {}; a.b = {}; (function() {a.b.c = 0;})(); x = a.b.c;",
"var a$b$c; (function() {a$b$c = 0;})(); x = a$b$c;");
}
public void testAddPropertyToFunctionInLocalScopeDepth1() {
test("function a() {} function f() { a.c = 5; return a.c; }",
"function a() {} var a$c; function f() { a$c = 5; return a$c; }");
}
public void testAddPropertyToFunctionInLocalScopeDepth2() {
test("var a = {}; a.b = function() {}; function f() {a.b.c = 0;}",
"var a$b = function() {}; var a$b$c; function f() {a$b$c = 0;}");
}
public void testAddPropertyToUncollapsibleFunctionInLocalScopeDepth1() {
testSame("function a() {} var c = 1; c = a; (function() {a.b = 0;})(); a.b;");
}
public void testAddPropertyToUncollapsibleFunctionInLocalScopeDepth2() {
test("var a = {}; a.b = function (){}; var d = 1; d = a.b;"
+ "(function() {a.b.c = 0;})(); a.b.c;",
"var a$b = function (){}; var d = 1; d = a$b;"
+ "(function() {a$b.c = 0;})(); a$b.c;");
}
public void testResetObjectPropertyInLocalScope() {
test("var a = {b: 0}; a.c = 1; function f() { a.c = 5; }",
"var a$b = 0; var a$c = 1; function f() { a$c = 5; }");
}
public void testResetFunctionPropertyInLocalScope() {
test("function a() {}; a.c = 1; function f() { a.c = 5; }",
"function a() {}; var a$c = 1; function f() { a$c = 5; }");
}
public void testGlobalNameReferencedInLocalScopeBeforeDefined1() {
// Because referencing global names earlier in the source code than they're
// defined is such a common practice, we collapse them even though a runtime
// exception could result (in the off-chance that the function gets called
// before the alias variable is defined).
test("var a = {b: 0}; function f() { a.c = 5; } a.c = 1;",
"var a$b = 0; function f() { a$c = 5; } var a$c = 1;");
}
public void testGlobalNameReferencedInLocalScopeBeforeDefined2() {
test("var a = {b: 0}; function f() { return a.c; } a.c = 1;",
"var a$b = 0; function f() { return a$c; } var a$c = 1;");
}
public void testTwiceDefinedGlobalNameDepth1_1() {
testSame("var a = {}; function f() { a.b(); }"
+ "a = function() {}; a.b = function() {};");
}
public void testTwiceDefinedGlobalNameDepth1_2() {
testSame("var a = {}; /** @constructor */ a = function() {};"
+ "a.b = {}; a.b.c = 0; function f() { a.b.d = 1; }");
}
public void testTwiceDefinedGlobalNameDepth2() {
test("var a = {}; a.b = {}; function f() { a.b.c(); }"
+ "a.b = function() {}; a.b.c = function() {};",
"var a$b = {}; function f() { a$b.c(); }"
+ "a$b = function() {}; a$b.c = function() {};");
}
public void testFunctionCallDepth1() {
test("var a = {}; a.b = function(){}; var c = a.b();",
"var a$b = function(){}; var c = a$b()");
}
public void testFunctionCallDepth2() {
test("var a = {}; a.b = {}; a.b.c = function(){}; a.b.c();",
"var a$b$c = function(){}; a$b$c();");
}
public void testFunctionAlias1() {
test("var a = {}; a.b = {}; a.b.c = function(){}; a.b.d = a.b.c;a.b.d=null",
"var a$b$c = function(){}; var a$b$d = a$b$c;a$b$d=null;");
}
public void testCallToRedefinedFunction() {
test("var a = {}; a.b = function(){}; a.b = function(){}; a.b();",
"var a$b = function(){}; a$b = function(){}; a$b();");
}
public void testCollapsePrototypeName() {
test("var a = {}; a.b = {}; a.b.c = function(){}; "
+ "a.b.c.prototype.d = function(){}; (new a.b.c()).d();",
"var a$b$c = function(){}; a$b$c.prototype.d = function(){}; "
+ "new a$b$c().d();");
}
public void testReferencedPrototypeProperty() {
test("var a = {b: {}}; a.b.c = function(){}; a.b.c.prototype.d = {};"
+ "e = a.b.c.prototype.d;",
"var a$b$c = function(){}; a$b$c.prototype.d = {};"
+ "e = a$b$c.prototype.d;");
}
public void testSetStaticAndPrototypePropertiesOnFunction() {
test("var a = {}; a.b = function(){}; a.b.prototype.d = 0; a.b.c = 1;",
"var a$b = function(){}; a$b.prototype.d = 0; var a$b$c = 1;");
}
public void testReadUndefinedPropertyDepth1() {
test("var a = {b: 0}; var c = a.d;",
"var a$b = 0; var a = {}; var c = a.d;");
}
public void testReadUndefinedPropertyDepth2() {
test("var a = {b: {c: 0}}; f(a.b.c); f(a.b.d);",
"var a$b$c = 0; var a$b = {}; f(a$b$c); f(a$b.d);");
}
public void testCallUndefinedMethodOnObjLitDepth1() {
test("var a = {b: 0}; a.c();",
"var a$b = 0; var a = {}; a.c();");
}
public void testCallUndefinedMethodOnObjLitDepth2() {
test("var a = {b: {}}; a.b.c = function() {}; a.b.c(); a.b.d();",
"var a$b = {}; var a$b$c = function() {}; a$b$c(); a$b.d();");
}
public void testPropertiesOfAnUndefinedVar() {
testSame("a.document = d; f(a.document.innerHTML);");
}
public void testPropertyOfAnObjectThatIsNeitherFunctionNorObjLit() {
testSame("var a = window; a.document = d; f(a.document)");
}
public void testStaticFunctionReferencingThis1() {
// Note: Google's JavaScript Style Guide says to avoid using the 'this'
// keyword in a static function.
test("var a = {}; a.b = function() {this.c}; var d = 1; d = a.b;",
"var a$b = function() {this.c}; var d = 1; d = a$b;", null,
CollapseProperties.UNSAFE_THIS);
}
public void testStaticFunctionReferencingThis2() {
// This gives no warning, because "this" is in a scope whose name is not
// getting collapsed.
test("var a = {}; "
+ "a.b = function() { return function(){ return this; }; };",
"var a$b = function() { return function(){ return this; }; };");
}
public void testStaticFunctionReferencingThis3() {
test("var a = {b: function() {this.c}};",
"var a$b = function() { this.c };", null,
CollapseProperties.UNSAFE_THIS);
}
public void testStaticFunctionReferencingThis4() {
test("var a = {/** @this {Element} */ b: function() {this.c}};",
"var a$b = function() { this.c };");
}
public void testPrototypeMethodReferencingThis() {
testSame("var A = function(){}; A.prototype = {b: function() {this.c}};");
}
public void testConstructorReferencingThis() {
test("var a = {}; "
+ "/** @constructor */ a.b = function() { this.a = 3; };",
"/** @constructor */ var a$b = function() { this.a = 3; };");
}
public void testRecordReferencingThis() {
test("/** @const */ var a = {}; "
+ "/** @record */ a.b = function() { /** @type {string} */ this.a; };",
"/** @record */ var a$b = function() { /** @type {string} */ this.a; };");
}
public void testSafeReferenceOfThis() {
test(
"var a = {}; /** @this {Object} */ a.b = function() { this.a = 3; };",
" /** @this {Object} */ var a$b = function() { this.a = 3; };");
}
public void testGlobalFunctionReferenceOfThis() {
testSame("var a = function() { this.a = 3; };");
}
public void testFunctionGivenTwoNames() {
// It's okay to collapse f's properties because g is not added to the
// global scope as an alias for f. (Try it in your browser.)
test("var f = function g() {}; f.a = 1; h(f.a);",
"var f = function g() {}; var f$a = 1; h(f$a);");
}
public void testObjLitWithUsedNumericKey() {
testSame("a = {40: {}, c: {}}; var d = a[40]; var e = a.c;");
}
public void testObjLitWithUnusedNumericKey() {
test("var a = {40: {}, c: {}}; var e = 1; e = a.c;",
"var a$1 = {}; var a$c = {}; var e = 1; e = a$c");
}
public void testObjLitWithNonIdentifierKeys() {
testSame("a = {' ': 0, ',': 1}; var c = a[' '];");
testSame("var FOO = {\n"
+ " 'bar': {\n"
+ " 'baz,qux': {\n"
+ " 'beep': 'xxxxx',\n"
+ " },\n"
+ " }\n"
+ "};"
+ "alert(FOO);");
}
public void testChainedAssignments1() {
test("var x = {}; x.y = a = 0;",
"var x$y = a = 0;");
}
public void testChainedAssignments2() {
test("var x = {}; x.y = a = b = c();",
"var x$y = a = b = c();");
}
public void testChainedAssignments3() {
test("var x = {y: 1}; a = b = x.y;",
"var x$y = 1; a = b = x$y;");
}
public void testChainedAssignments4() {
testSame("var x = {}; a = b = x.y;");
}
public void testChainedAssignments5() {
test("var x = {}; a = x.y = 0;", "var x$y; a = x$y = 0;");
}
public void testChainedAssignments6() {
test("var x = {}; a = x.y = b = c();",
"var x$y; a = x$y = b = c();");
}
public void testChainedAssignments7() {
test("var x = {}; a = x.y = {}; /** @constructor */ x.y.z = function() {};",
"var x$y; a = x$y = {}; /** @constructor */ var x$y$z = function() {};",
null, UNSAFE_NAMESPACE_WARNING);
}
public void testChainedVarAssignments1() {
test("var x = {y: 1}; var a = x.y = 0;",
"var x$y = 1; var a = x$y = 0;");
}
public void testChainedVarAssignments2() {
test("var x = {y: 1}; var a = x.y = b = 0;",
"var x$y = 1; var a = x$y = b = 0;");
}
public void testChainedVarAssignments3() {
test("var x = {y: {z: 1}}; var b = 0; var a = x.y.z = 1; var c = 2;",
"var x$y$z = 1; var b = 0; var a = x$y$z = 1; var c = 2;");
}
public void testChainedVarAssignments4() {
test("var x = {}; var a = b = x.y = 0;",
"var x$y; var a = b = x$y = 0;");
}
public void testChainedVarAssignments5() {
test("var x = {y: {}}; var a = b = x.y.z = 0;",
"var x$y$z; var a = b = x$y$z = 0;");
}
public void testPeerAndSubpropertyOfUncollapsibleProperty() {
test("var x = {}; var a = x.y = 0; x.w = 1; x.y.z = 2;"
+ "b = x.w; c = x.y.z;",
"var x$y; var a = x$y = 0; var x$w = 1; x$y.z = 2;"
+ "b = x$w; c = x$y.z;");
}
public void testComplexAssignmentAfterInitialAssignment() {
test("var d = {}; d.e = {}; d.e.f = 0; a = b = d.e.f = 1;",
"var d$e$f = 0; a = b = d$e$f = 1;");
}
public void testRenamePrefixOfUncollapsibleProperty() {
test("var d = {}; d.e = {}; a = b = d.e.f = 0;",
"var d$e$f; a = b = d$e$f = 0;");
}
public void testNewOperator() {
// Using the new operator on a name doesn't prevent its (static) properties
// from getting collapsed.
test("var a = {}; a.b = function() {}; a.b.c = 1; var d = new a.b();",
"var a$b = function() {}; var a$b$c = 1; var d = new a$b();");
}
public void testMethodCall() {
test("var a = {}; a.b = function() {}; var d = a.b();",
"var a$b = function() {}; var d = a$b();");
}
public void testObjLitDefinedInLocalScopeIsLeftAlone() {
test("var a = {}; a.b = function() {};"
+ "a.b.prototype.f_ = function() {"
+ " var x = { p: '', q: '', r: ''}; var y = x.q;"
+ "};",
"var a$b = function() {};"
+ "a$b.prototype.f_ = function() {"
+ " var x = { p: '', q: '', r: ''}; var y = x.q;"
+ "};");
}
public void testPropertiesOnBothSidesOfAssignment() {
// This verifies that replacements are done in the right order. Collapsing
// the l-value in an assignment affects the parse tree immediately above
// the r-value, so we update all rvalues before any lvalues.
test("var a = {b: 0}; a.c = a.b;a.c = null",
"var a$b = 0; var a$c = a$b;a$c = null");
}
public void testCallOnUndefinedProperty() {
// The "inherits" property is not explicitly defined on a.b anywhere, but
// it is accessed as though it certainly exists (it is called), so we infer
// that it must be an uncollapsible property that has come into existence
// some other way.
test("var a = {}; a.b = function(){}; a.b.inherits(x);",
"var a$b = function(){}; a$b.inherits(x);");
}
public void testGetPropOnUndefinedProperty() {
// The "superClass_" property is not explicitly defined on a.b anywhere,
// but it is accessed as though it certainly exists (a subproperty of it
// is accessed), so we infer that it must be an uncollapsible property that
// has come into existence some other way.
test("var a = {b: function(){}}; a.b.prototype.c ="
+ "function() { a.b.superClass_.c.call(this); }",
"var a$b = function(){}; a$b.prototype.c ="
+ "function() { a$b.superClass_.c.call(this); }");
}
public void testNonWellformedAlias1() {
testSame("var a = {b: 3}; function f() { f(x); var x = a; f(x.b); }");
}
public void testNonWellformedAlias2() {
testSame("var a = {b: 3}; "
+ "function f() { if (false) { var x = a; f(x.b); } f(x); }");
}
public void testInlineAliasWithModifications() {
testSame("var x = 10; function f() { var y = x; x++; alert(y)} ");
testSame("var x = 10; function f() { var y = x; x+=1; alert(y)} ");
test("var x = {}; x.x = 10; function f() {var y=x.x; x.x++; alert(y)}",
"var x$x = 10; function f() {var y=x$x; x$x++; alert(y)}");
disableNormalize();
test("var x = {}; x.x = 10; function f() {var y=x.x; x.x+=1; alert(y)}",
"var x$x = 10; function f() {var y=x$x; x$x+=1; alert(y)}");
}
public void testDoNotCollapsePropertyOnExternType() {
testSame("String.myFunc = function() {}; String.myFunc()");
}
public void testBug1704733() {
String prelude =
"function protect(x) { return x; }"
+ "function O() {}"
+ "protect(O).m1 = function() {};"
+ "protect(O).m2 = function() {};"
+ "protect(O).m3 = function() {};";
testSame(prelude
+ "alert(O.m1); alert(O.m2()); alert(!O.m3);");
}
public void testBug1956277() {
test("var CONST = {}; CONST.URL = 3;",
"var CONST$URL = 3;");
}
public void testBug1974371() {
test(
"/** @enum {Object} */ var Foo = {A: {c: 2}, B: {c: 3}}; for (var key in Foo) {}",
"var Foo$A = {c: 2}; var Foo$B = {c: 3};"
+ "/** @enum {Object} */ var Foo = {A: Foo$A, B: Foo$B};"
+ "for (var key in Foo) {}");
}
private static final String COMMON_ENUM =
"/** @enum {Object} */ var Foo = {A: {c: 2}, B: {c: 3}};";
public void testEnumOfObjects1() {
test(
COMMON_ENUM
+ "for (var key in Foo.A) {}",
"var Foo$A = {c: 2}; var Foo$B$c = 3; for (var key in Foo$A) {}");
}
public void testEnumOfObjects2() {
test(
COMMON_ENUM
+ "foo(Foo.A.c);",
"var Foo$A$c = 2; var Foo$B$c = 3; foo(Foo$A$c);");
}
public void testEnumOfObjects3() {
test(
"var x = {c: 2}; var y = {c: 3};"
+ "/** @enum {Object} */ var Foo = {A: x, B: y};"
+ "for (var key in Foo) {}",
"var x = {c: 2}; var y = {c: 3};"
+ "var Foo$A = x; var Foo$B = y; /** @enum {Object} */ var Foo = {A: Foo$A, B: Foo$B};"
+ "for (var key in Foo) {}");
}
public void testEnumOfObjects4() {
// Note that this produces bad code, but that's OK, because
// checkConsts will yell at you for reassigning an enum value.
// (enum values have to be constant).
test(
COMMON_ENUM + "for (var key in Foo) {} Foo.A = 3; alert(Foo.A);",
"var Foo$A = {c: 2}; var Foo$B = {c: 3};"
+ "/** @enum {Object} */ var Foo = {A: Foo$A, B: Foo$B};"
+ "for (var key in Foo) {} Foo$A = 3; alert(Foo$A);");
}
public void testObjectOfObjects1() {
// Basically the same as testEnumOfObjects4, but without the
// constant enum values.
testSame("var Foo = {a: {c: 2}, b: {c: 3}}; for (var key in Foo) {} Foo.a = 3; alert(Foo.a);");
}
public void testReferenceInAnonymousObject0() {
test("var a = {};"
+ "a.b = function(){};"
+ "a.b.prototype.c = function(){};"
+ "var d = a.b.prototype.c;",
"var a$b = function(){};"
+ "a$b.prototype.c = function(){};"
+ "var d = a$b.prototype.c;");
}
public void testReferenceInAnonymousObject1() {
test("var a = {};"
+ "a.b = function(){};"
+ "var d = a.b.prototype.c;",
"var a$b = function(){};"
+ "var d = a$b.prototype.c;");
}
public void testReferenceInAnonymousObject2() {
test("var a = {};"
+ "a.b = function(){};"
+ "a.b.prototype.c = function(){};"
+ "var d = {c: a.b.prototype.c};",
"var a$b = function(){};"
+ "a$b.prototype.c = function(){};"
+ "var d$c = a$b.prototype.c;");
}
public void testReferenceInAnonymousObject3() {
test("function CreateClass(a$jscomp$1) {}"
+ "var a = {};"
+ "a.b = function(){};"
+ "a.b.prototype.c = function(){};"
+ "a.d = CreateClass({c: a.b.prototype.c});",
"function CreateClass(a$jscomp$1) {}"
+ "var a$b = function(){};"
+ "a$b.prototype.c = function(){};"
+ "var a$d = CreateClass({c: a$b.prototype.c});");
}
public void testReferenceInAnonymousObject4() {
test("function CreateClass(a) {}"
+ "var a = {};"
+ "a.b = CreateClass({c: function() {}});"
+ "a.d = CreateClass({c: a.b.c});",
"function CreateClass(a$jscomp$1) {}"
+ "var a$b = CreateClass({c: function() {}});"
+ "var a$d = CreateClass({c: a$b.c});");
}
public void testReferenceInAnonymousObject5() {
test("function CreateClass(a) {}"
+ "var a = {};"
+ "a.b = CreateClass({c: function() {}});"
+ "a.d = CreateClass({c: a.b.prototype.c});",
"function CreateClass(a$jscomp$1) {}"
+ "var a$b = CreateClass({c: function() {}});"
+ "var a$d = CreateClass({c: a$b.prototype.c});");
}
public void testCrashInNestedAssign() {
test("var a = {}; if (a.b = function() {}) a.b();",
"var a$b; if (a$b=function() {}) { a$b(); }");
}
public void testTwinReferenceCancelsChildCollapsing() {
test("var a = {}; if (a.b = function() {}) { a.b.c = 3; a.b(a.b.c); }",
"var a$b; if (a$b = function() {}) { a$b.c = 3; a$b(a$b.c); }");
}
public void testPropWithDollarSign() {
test("var a = {$: 3}", "var a$$0 = 3;");
}
public void testPropWithDollarSign2() {
test("var a = {$: function(){}}", "var a$$0 = function(){};");
}
public void testPropWithDollarSign3() {
test("var a = {b: {c: 3}, b$c: function(){}}",
"var a$b$c = 3; var a$b$0c = function(){};");
}
public void testPropWithDollarSign4() {
test("var a = {$$: {$$$: 3}};", "var a$$0$0$$0$0$0 = 3;");
}
public void testPropWithDollarSign5() {
test("var a = {b: {$0c: true}, b$0c: false};",
"var a$b$$00c = true; var a$b$00c = false;");
}
public void testConstKey() {
test("var foo = {A: 3};", "var foo$A = 3;");
}
public void testPropertyOnGlobalCtor() {
test("/** @constructor */ function Map() {} Map.foo = 3; Map;",
"/** @constructor */ function Map() {} var Map$foo = 3; Map;");
}
public void testPropertyOnGlobalInterface() {
test("/** @interface */ function Map() {} Map.foo = 3; Map;",
"/** @interface */ function Map() {} var Map$foo = 3; Map;");
}
public void testPropertyOnGlobalFunction() {
testSame("function Map() {} Map.foo = 3; alert(Map);");
}
public void testIssue389() {
test(
"function alias() {}"
+ "var dojo = {};"
+ "dojo.gfx = {};"
+ "dojo.declare = function() {};"
+ "/** @constructor */"
+ "dojo.gfx.Shape = function() {};"
+ "dojo.gfx.Shape = dojo.declare('dojo.gfx.Shape');"
+ "alias(dojo);",
"function alias() {}"
+ "var dojo = {};"
+ "dojo.gfx = {};"
+ "dojo.declare = function() {};"
+ "/** @constructor */"
+ "var dojo$gfx$Shape = function() {};"
+ "dojo$gfx$Shape = dojo.declare('dojo.gfx.Shape');"
+ "alias(dojo);",
null,
UNSAFE_NAMESPACE_WARNING);
}
public void testAliasedTopLevelName() {
testSame(
"function alias() {}"
+ "var dojo = {};"
+ "dojo.gfx = {};"
+ "dojo.declare = function() {};"
+ "dojo.gfx.Shape = {SQUARE: 2};"
+ "dojo.gfx.Shape = dojo.declare('dojo.gfx.Shape');"
+ "alias(dojo);"
+ "alias(dojo$gfx$Shape$SQUARE);");
}
public void testAliasedTopLevelEnum() {
test(
"function alias() {}"
+ "var dojo = {};"
+ "dojo.gfx = {};"
+ "dojo.declare = function() {};"
+ "/** @enum {number} */"
+ "dojo.gfx.Shape = {SQUARE: 2};"
+ "dojo.gfx.Shape = dojo.declare('dojo.gfx.Shape');"
+ "alias(dojo);"
+ "alias(dojo.gfx.Shape.SQUARE);",
"function alias() {}"
+ "var dojo = {};"
+ "dojo.gfx = {};"
+ "dojo.declare = function() {};"
+ "/** @enum {number} */"
+ "var dojo$gfx$Shape = {SQUARE: 2};"
+ "dojo$gfx$Shape = dojo.declare('dojo.gfx.Shape');"
+ "alias(dojo);"
+ "alias(dojo$gfx$Shape.SQUARE);",
null,
UNSAFE_NAMESPACE_WARNING);
}
public void testAssignFunctionBeforeDefinition() {
testSame(
"f = function() {};"
+ "var f = null;");
}
public void testObjectLitBeforeDefinition() {
testSame(
"a = {b: 3};"
+ "var a = null;"
+ "this.c = a.b;");
}
public void testTypedef1() {
test("var foo = {};"
+ "/** @typedef {number} */ foo.Baz;",
"var foo = {}; var foo$Baz;");
}
public void testTypedef2() {
test("var foo = {};"
+ "/** @typedef {number} */ foo.Bar.Baz;"
+ "foo.Bar = function() {};",
"var foo$Bar$Baz; var foo$Bar = function(){};");
}
public void testDelete1() {
testSame(
"var foo = {};"
+ "foo.bar = 3;"
+ "delete foo.bar;");
}
public void testDelete2() {
test(
"var foo = {};"
+ "foo.bar = 3;"
+ "foo.baz = 3;"
+ "delete foo.bar;",
"var foo = {};"
+ "foo.bar = 3;"
+ "var foo$baz = 3;"
+ "delete foo.bar;");
}
public void testDelete3() {
testSame(
"var foo = {bar: 3};"
+ "delete foo.bar;");
}
public void testDelete4() {
test(
"var foo = {bar: 3, baz: 3};"
+ "delete foo.bar;",
"var foo$baz=3;var foo={bar:3};delete foo.bar");
}
public void testDelete5() {
test(
"var x = {};"
+ "x.foo = {};"
+ "x.foo.bar = 3;"
+ "delete x.foo.bar;",
"var x$foo = {};"
+ "x$foo.bar = 3;"
+ "delete x$foo.bar;");
}
public void testDelete6() {
test(
"var x = {};"
+ "x.foo = {};"
+ "x.foo.bar = 3;"
+ "x.foo.baz = 3;"
+ "delete x.foo.bar;",
"var x$foo = {};"
+ "x$foo.bar = 3;"
+ "var x$foo$baz = 3;"
+ "delete x$foo.bar;");
}
public void testDelete7() {
test(
"var x = {};"
+ "x.foo = {bar: 3};"
+ "delete x.foo.bar;",
"var x$foo = {bar: 3};"
+ "delete x$foo.bar;");
}
public void testDelete8() {
test(
"var x = {};"
+ "x.foo = {bar: 3, baz: 3};"
+ "delete x.foo.bar;",
"var x$foo$baz = 3; var x$foo = {bar: 3};"
+ "delete x$foo.bar;");
}
public void testDelete9() {
testSame(
"var x = {};"
+ "x.foo = {};"
+ "x.foo.bar = 3;"
+ "delete x.foo;");
}
public void testDelete10() {
testSame(
"var x = {};"
+ "x.foo = {bar: 3};"
+ "delete x.foo;");
}
public void testDelete11() {
// Constructors are always collapsed.
test(
"var x = {};"
+ "x.foo = {};"
+ "/** @constructor */ x.foo.Bar = function() {};"
+ "delete x.foo;",
"var x = {};"
+ "x.foo = {};"
+ "/** @constructor */ var x$foo$Bar = function() {};"
+ "delete x.foo;",
null,
NAMESPACE_REDEFINED_WARNING);
}
public void testPreserveConstructorDoc() {
test(
"var foo = {}; /** @constructor */ foo.bar = function() {}",
"/** @constructor */ var foo$bar = function() {}");
}
public void testTypeDefAlias2() {
// TODO(johnlenz): make CollapseProperties safer around aliases of
// functions and object literals. Currently, this pass trades correctness
// for code size. We should able to create a safer compromise by teaching
// the pass about goog.inherits and similiar calls.
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"
+ "if (random) { /** @typedef {D.L} */ M.L = D.L; }\n"
+ "\n"
+ "use(M.L);\n"
+ "use(M.L.A);\n",
"/** @constructor */ var D = function() {};\n"
+ "/** @constructor */ var D$L = function() {};\n"
+ "/** @type {D.L} */ var D$L$A = new D$L();\n"
+ "if (random) { /** @typedef {D.L} */ var M$L = D$L; }\n"
+ "use(M$L);\n"
+ "use(M$L.A);");
}
public void testGlobalCatch() throws Exception {
testSame(
"try {"
+ " throw Error();"
+ "} catch (e) {"
+ " console.log(e.name)"
+ "}");
}
public void testCtorManyAssignmentsDontInlineDontWarn() {
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 new x();\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 new x();\n"
+ "}");
}
public void testExpressionResultReferenceWontPreventCollapse() {
test("var ns = {};\n"
+ "ns.Outer = {};\n"
+ "\n"
+ "ns.Outer;\n"
+ "ns.Outer.Inner = function() {}\n",
"var ns$Outer={};\n"
+ "ns$Outer;\n"
+ "var ns$Outer$Inner=function(){};\n");
}
public void testNoCollapseWithInvalidEnums() {
test(
"/** @enum { { a: { b: number}} } */"
+ "var e = { KEY1: { a: { /** @nocollapse */ b: 123}},\n"
+ " KEY2: { a: { b: 456}}\n"
+ "}",
"var e$KEY1$a={/** @nocollapse */ b:123}; var e$KEY2$a$b=456;");
test(
"/** @enum */ var e = { A: 1, B: 2 };\n"
+ "/** @type {{ c: { d: number } }} */ e.name1 = {"
+ " c: { /** @nocollapse */ d: 123 } };",
"var e$A=1; var e$B=2; var e$name1$c={/** @nocollapse */ /** @nocollapse */ d:123};");
test(
"/** @enum */ var e = { A: 1, B: 2}; /** @nocollapse */ e.foo = { bar: true };",
"var e$A=1; var e$B=2; /** @enum */ var e = {}; /** @nocollapse */ e.foo = { bar: true };");
}
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);
}
public void testCollapsedNameAlreadyTaken() {
test(
LINE_JOINER.join(
"/** @constructor */ function Funny$Name(){};",
"function Funny(){};",
"Funny.Name = 5;",
"var x = new Funny$Name();"),
LINE_JOINER.join(
"/** @constructor */ function Funny$Name(){};",
"function Funny(){};",
"var Funny$Name$1 = 5;",
"var x = new Funny$Name();"));
test("var ns$x = 0; var ns$x$0 = 1; var ns = {}; ns.x = 8;",
"var ns$x = 0; var ns$x$0 = 1; var ns$x$1 = 8;");
test("var ns$x = 0; var ns$x$0 = 1; var ns$x$1 = 2; var ns = {}; ns.x = 8;",
"var ns$x = 0; var ns$x$0 = 1; var ns$x$1 = 2; var ns$x$2 = 8;");
test("var ns$x = {}; ns$x.y = 2; var ns = {}; ns.x = {}; ns.x.y = 8;",
"var ns$x$y = 2; var ns$x$1$y = 8;");
test("var ns$x = {}; ns$x.y = 1; var ns = {}; ns.x$ = {}; ns.x$.y = 2; ns.x = {}; ns.x.y = 3;",
"var ns$x$y = 1; var ns$x$0$y = 2; var ns$x$1$y = 3;");
}
}