/*
* Copyright 2011 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;
/**
* Unit tests for {@link RescopeGlobalSymbols}
*
*/
public final class RescopeGlobalSymbolsTest extends CompilerTestCase {
private static final String NAMESPACE = "_";
private boolean assumeCrossModuleNames = true;
public RescopeGlobalSymbolsTest() {
}
@Override protected CompilerPass getProcessor(Compiler compiler) {
return new RescopeGlobalSymbols(
compiler,
NAMESPACE,
false,
assumeCrossModuleNames);
}
@Override
public void setUp() throws Exception {
super.setUp();
assumeCrossModuleNames = true;
}
@Override
protected int getNumRepetitions() {
return 1;
}
public void testVarDeclarations() {
test("var a = 1;", "_.a = 1;");
test("var a = 1, b = 2, c = 3;", "_.a = 1; _.b = 2; _.c = 3;");
test(
"var a = 'str', b = 1, c = { foo: 'bar' }, d = function() {};",
"_.a = 'str'; _.b = 1; _.c = { foo: 'bar' }; _.d = function() {};");
test("if(1){var x = 1;}", "if(1){_.x = 1;}");
test("var x;", "");
test("var a, b = 1;", "_.b = 1");
}
public void testVarDeclarations_allSameModule() {
assumeCrossModuleNames = false;
testSame("var a = 1;");
testSame("var a = 1, b = 2, c = 3;");
testSame("var a = 'str', b = 1, c = { foo: 'bar' }, d = function() {};");
testSame("if(1){var x = 1;}");
testSame("var x;");
testSame("var a, b = 1;");
}
public void testVarDeclarations_export() {
assumeCrossModuleNames = false;
test("var _dumpException = 1;", "_._dumpException = 1");
}
public void testVarDeclarations_acrossModules() {
assumeCrossModuleNames = false;
test(createModules(
"var a = 1;", "a"),
new String[] {"_.a = 1", "_.a"});
test(createModules(
"var a = 1, b = 2, c = 3;", "a;c;"),
new String[] {"var b;_.a = 1; b = 2; _.c = 3;", "_.a;_.c"});
test(createModules(
"var a = 1, b = 2, c = 3;", "b;c;"),
new String[] {"var a;a = 1; _.b = 2; _.c = 3;", "_.b;_.c"});
test(createModules(
"var a = 1, b = 2, c = 3;b;c;", "a;c;"),
new String[] {"var b;_.a = 1; b = 2; _.c = 3;b;_.c", "_.a;_.c"});
test(createModules(
"var a, b = 1;", "b"),
new String[] {"var a;_.b = 1;", "_.b"});
test(createModules(
"var a, b = 1, c = 2;", "b"),
new String[] {"var a, c;_.b = 1;c = 2", "_.b"});
test(createModules(
"var a, b = 1, c = 2;", "a"),
new String[] {"var b, c;b = 1;c = 2", "_.a"});
test(createModules(
"var a=1; var b=2,c=3;", "a;c;"),
new String[] {"var b;_.a=1;b=2;_.c=3", "_.a;_.c"});
test(createModules(
"1;var a, b = 1, c = 2;", "b"),
new String[] {"var a, c;1;_.b = 1;c = 2", "_.b"});
}
public void testForLoops() {
assumeCrossModuleNames = false;
test(createModules(
"for (var i = 0, c = 2; i < 1000; i++);", "c"),
new String[] {"var i;for (i = 0, _.c = 2; i < 1000; i++);", "_.c"});
test(createModules(
"for (var i = 0, c = 2; i < 1000; i++);", "i"),
new String[] {"var c;for (_.i = 0, c = 2; _.i < 1000; _.i++);",
"_.i"});
}
public void testForLoops_acrossModules() {
test(
"for (var i = 0; i < 1000; i++);",
"for (_.i = 0; _.i < 1000; _.i++);");
test(
"for (var i = 0, c = 2; i < 1000; i++);",
"for (_.i = 0, _.c = 2; _.i < 1000; _.i++);");
test(
"for (var i = 0, c = 2, d = 3; i < 1000; i++);",
"for (_.i = 0, _.c = 2, _.d = 3; _.i < 1000; _.i++);");
test(
"for (var i = 0, c = 2, d = 3, e = 4; i < 1000; i++);",
"for (_.i = 0, _.c = 2, _.d = 3, _.e = 4; _.i < 1000; _.i++);");
test(
"for (var i = 0; i < 1000;)i++;",
"for (_.i = 0; _.i < 1000;)_.i++;");
test(
"for (var i = 0,b; i < 1000;)i++;b++",
"for (_.i = 0,_.b; _.i < 1000;)_.i++;_.b++");
test(
"var o={};for (var i in o)i++;",
"_.o={};for (_.i in _.o)_.i++;");
}
public void testFunctionStatements1() {
test(
"function test(){}",
"_.test=function (){}");
setAcceptedLanguage(CompilerOptions.LanguageMode.ECMASCRIPT_2015);
test(
"if(1)function test(){}",
"if(1)_.test=function (){}");
}
public void testFunctionStatements2() throws Exception {
StringCompare testCase = new StringCompare();
testCase.testFreeCallSemantics();
testCase.tearDown();
}
public void testDeeperScopes() {
test(
"var a = function(b){return b}",
"_.a = function(b){return b}");
test(
"var a = function(b){var a; return a+b}",
"_.a = function(b){var a; return a+b}");
test(
"var a = function(a,b){return a+b}",
"_.a = function(a,b){return a+b}");
test(
"var x=1,a = function(b){var a; return a+b+x}",
"_.x=1;_.a = function(b){var a; return a+b+_.x}");
test(
"var x=1,a = function(b){return function(){var a;return a+b+x}}",
"_.x=1;_.a = function(b){return function(){var a; return a+b+_.x}}");
}
public void testTryCatch() {
test(
"try{var a = 1}catch(e){throw e}",
"try{_.a = 1}catch(e){throw e}");
}
public void testShadow() {
test(
"var _ = 1; (function () { _ = 2 })()",
"_._ = 1; (function () { _._ = 2 })()");
test(
"function foo() { var _ = {}; _.foo = foo; _.bar = 1; }",
"_.foo = function () { var _$ = {}; _$.foo = _.foo; _$.bar = 1}");
test(
"function foo() { var _ = {}; _.foo = foo; _.bar = 1; "
+ "(function() { var _ = 0;})() }",
"_.foo = function () { var _$ = {}; _$.foo = _.foo; _$.bar = 1; "
+ "(function() { var _$ = 0;})() }");
test(
"function foo() { var _ = {}; _.foo = foo; _.bar = 1; "
+ "var _$ = 1; }",
"_.foo = function () { var _$ = {}; _$.foo = _.foo; _$.bar = 1; "
+ "var _$$ = 1; }");
test(
"function foo() { var _ = {}; _.foo = foo; _.bar = 1; "
+ "var _$ = 1; (function() { _ = _$ })() }",
"_.foo = function () { var _$ = {}; _$.foo = _.foo; _$.bar = 1; "
+ "var _$$ = 1; (function() { _$ = _$$ })() }");
test(
"function foo() { var _ = {}; _.foo = foo; _.bar = 1; "
+ "var _$ = 1, _$$ = 2 (function() { _ = _$ = _$$; " +
"var _$, _$$$ })() }",
"_.foo = function () { var _$ = {}; _$.foo = _.foo; _$.bar = 1; "
+ "var _$$ = 1, _$$$ = 2 (function() { _$ = _$$ = _$$$; "
+ "var _$$, _$$$$ })() }");
test(
"function foo() { var _a = 1;}",
"_.foo = function () { var _a = 1;}");
// We accept this unnecessary renaming as acceptable to simplify pattern
// matching in the traversal.
test(
"function foo() { var _$a = 1;}",
"_.foo = function () { var _$a$ = 1;}");
}
public void testExterns() {
test(
"var document;",
"document",
"window.document", null, null);
test(
"var document;",
"document.getElementsByTagName('test')",
"window.document.getElementsByTagName('test')", null, null);
test(
"var document;",
"window.document.getElementsByTagName('test')",
"window.document.getElementsByTagName('test')", null, null);
test(
"var document;document.getElementsByTagName",
"document.getElementsByTagName('test')",
"window.document.getElementsByTagName('test')", null, null);
test(
"var document,navigator",
"document.navigator;navigator",
"window.document.navigator;window.navigator", null, null);
test(
"var iframes",
"function test() { iframes.resize(); }",
"_.test = function() { window.iframes.resize(); }", null, null);
test(
"var iframes",
"var foo = iframes;",
"_.foo = window.iframes;", null, null);
// Special names.
test(
"var arguments, window, eval;",
"arguments;window;eval;",
"arguments;window;eval;", null, null);
// Actually not an extern.
test(
"",
"document",
"window.document", null, null);
// Javascript builtin objects
testSame(
"Object;Function;Array;String;Boolean;Number;Math;"
+ "Date;RegExp;JSON;Error;EvalError;ReferenceError;"
+ "SyntaxError;TypeError;URIError;");
}
public void testSameVarDeclaredInExternsAndSource() {
test(
"/** @const */ var ns = {}; function f() {}",
"/** @const */ var ns = ns || {};",
"/** @const */ window.ns = window.ns || {};", null, null);
test(
"var x;",
"var x = 1; x = 2;",
"window.x = 1; window.x = 2;", null, null);
test(
"var x;",
"var x; x = 1;",
"window.x = 1;", null, null);
test(
"var x;",
"function f() { var x; x = 1; }",
"_.f = function() { var x; x = 1; }", null, null);
test(
"var x, y;",
"var x = 1, y = 2;",
"window.x = 1; window.y = 2;", null, null);
test(
"var x, y;",
"var x, y = 2;",
"window.y = 2;", null, null);
test(
"var x;",
"var x = 1, y = 2;",
"window.x = 1; _.y = 2;", null, null);
test(
"var x;",
"var y = 2, x = 1;",
"_.y = 2; window.x = 1;", null, null);
test(
"var x;",
"var y, x = 1;",
"window.x = 1;", null, null);
test(
"var foo;",
"var foo = function(x) { if (x > 0) { var y = foo; } };",
"window.foo = function(x) { if (x > 0) { var y = window.foo; } };",
null, null);
// The parameter x doesn't conflict with the x in the source
test(
"function f(x) {}",
"var f = 1; var x = 2;",
"window.f = 1; _.x = 2;", null, null);
}
public void testSameVarDeclaredInExternsAndSource2() {
assumeCrossModuleNames = false;
test(createModules(
LINE_JOINER.join(
"Foo = function() { this.b = ns; };",
"var f = function(a) {",
" if (a instanceof Foo && a.b === ns) {}",
"},",
"ns = {},",
"g = function(a) { var b = new Foo; };"),
"f; g;"),
new String[] {
LINE_JOINER.join(
"var ns;",
"window.Foo = function() { this.b = ns; };",
"_.f = function(a) {",
" if (a instanceof window.Foo && a.b === ns) {}",
"};",
"ns = {};",
"_.g = function(a) { var b = new window.Foo; };"),
"_.f; _.g;"
});
test(
"var y;",
"var x = 1, y = 2; function f() { return x + window.y; }",
"var x; x = 1; window.y = 2; var f = function() { return x + window.y; }",
null, null);
}
private class StringCompare extends CompilerTestCase {
StringCompare() {
super("", false);
}
@Override protected CompilerPass getProcessor(Compiler compiler) {
return new RescopeGlobalSymbols(
compiler,
NAMESPACE,
false,
assumeCrossModuleNames);
}
public void testFreeCallSemantics() {
// This triggers free call.
test(
"function x(){this};var y=function(){var val=x()||{}}",
"_.x=function(){this};_.y=function(){var val=(0,_.x)()||{}}");
test(
"function x(){this;x()}",
"_.x=function(){this;(0,_.x)()}");
test(
"var a=function(){this};a()",
"_.a=function(){this};(0,_.a)()");
// Cases where free call forcing through (0, foo)() is not necessary.
test(
"var a=function(){};a()",
"_.a=function(){};_.a()");
test(
"function a(){};a()",
"_.a=function(){};_.a()");
test(
"var a;a=function(){};a()",
"_.a=function(){};_.a()");
// Ambigious cases.
test(
"var a=1;a=function(){};a()",
"_.a=1;_.a=function(){};(0,_.a)()");
test(
"var b;var a=b;a()",
"_.a=_.b;(0,_.a)()");
}
}
}