2399 lines
77 KiB
Java
2399 lines
77 KiB
Java
/*
|
|
* Copyright 2009 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 com.google.common.collect.ArrayListMultimap;
|
|
import com.google.common.collect.ImmutableList;
|
|
import com.google.common.collect.ImmutableMap;
|
|
import com.google.common.collect.Sets;
|
|
import com.google.javascript.jscomp.CompilerOptions.LanguageMode;
|
|
import com.google.javascript.rhino.Node;
|
|
import com.google.javascript.rhino.Token;
|
|
|
|
/**
|
|
* Tests for {@link PassFactory}.
|
|
*
|
|
* @author nicksantos@google.com (Nick Santos)
|
|
*/
|
|
public class IntegrationTest extends IntegrationTestCase {
|
|
|
|
private static final String CLOSURE_BOILERPLATE =
|
|
"/** @define {boolean} */ var COMPILED = false; var goog = {};" +
|
|
"goog.exportSymbol = function() {};";
|
|
|
|
private static final String CLOSURE_COMPILED =
|
|
"var COMPILED = true; var goog$exportSymbol = function() {};";
|
|
|
|
public void testConstructorCycle() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.checkTypes = true;
|
|
test(options,
|
|
"/** @return {function()} */ var AsyncTestCase = function() {};\n" +
|
|
"/**\n" +
|
|
" * @constructor\n" +
|
|
" */ Foo = /** @type {function(new:Foo)} */ (AyncTestCase());",
|
|
RhinoErrorReporter.PARSE_ERROR);
|
|
}
|
|
|
|
public void testBug1949424() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.collapseProperties = true;
|
|
options.closurePass = true;
|
|
test(options, CLOSURE_BOILERPLATE + "goog.provide('FOO'); FOO.bar = 3;",
|
|
CLOSURE_COMPILED + "var FOO$bar = 3;");
|
|
}
|
|
|
|
public void testBug1949424_v2() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.collapseProperties = true;
|
|
options.closurePass = true;
|
|
test(options, CLOSURE_BOILERPLATE + "goog.provide('FOO.BAR'); FOO.BAR = 3;",
|
|
CLOSURE_COMPILED + "var FOO$BAR = 3;");
|
|
}
|
|
|
|
public void testBug1956277() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.collapseProperties = true;
|
|
options.inlineVariables = true;
|
|
test(options, "var CONST = {}; CONST.bar = null;" +
|
|
"function f(url) { CONST.bar = url; }",
|
|
"var CONST$bar = null; function f(url) { CONST$bar = url; }");
|
|
}
|
|
|
|
public void testBug1962380() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.collapseProperties = true;
|
|
options.inlineVariables = true;
|
|
options.generateExports = true;
|
|
test(options,
|
|
CLOSURE_BOILERPLATE + "/** @export */ goog.CONSTANT = 1;" +
|
|
"var x = goog.CONSTANT;",
|
|
"(function() {})('goog.CONSTANT', 1);" +
|
|
"var x = 1;");
|
|
}
|
|
|
|
public void testBug2410122() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.generateExports = true;
|
|
options.closurePass = true;
|
|
test(options,
|
|
"var goog = {};" +
|
|
"function F() {}" +
|
|
"/** @export */ function G() { goog.base(this); } " +
|
|
"goog.inherits(G, F);",
|
|
"var goog = {};" +
|
|
"function F() {}" +
|
|
"function G() { F.call(this); } " +
|
|
"goog.inherits(G, F); goog.exportSymbol('G', G);");
|
|
}
|
|
|
|
public void testIssue90() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.foldConstants = true;
|
|
options.inlineVariables = true;
|
|
options.removeDeadCode = true;
|
|
test(options,
|
|
"var x; x && alert(1);",
|
|
"");
|
|
}
|
|
|
|
public void testClosurePassOff() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.closurePass = false;
|
|
testSame(
|
|
options,
|
|
"var goog = {}; goog.require = function(x) {}; goog.require('foo');");
|
|
testSame(
|
|
options,
|
|
"var goog = {}; goog.getCssName = function(x) {};" +
|
|
"goog.getCssName('foo');");
|
|
}
|
|
|
|
public void testClosurePassOn() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.closurePass = true;
|
|
test(
|
|
options,
|
|
"var goog = {}; goog.require = function(x) {}; goog.require('foo');",
|
|
ProcessClosurePrimitives.MISSING_PROVIDE_ERROR);
|
|
test(
|
|
options,
|
|
"/** @define {boolean} */ var COMPILED = false;" +
|
|
"var goog = {}; goog.getCssName = function(x) {};" +
|
|
"goog.getCssName('foo');",
|
|
"var COMPILED = true;" +
|
|
"var goog = {}; goog.getCssName = function(x) {};" +
|
|
"'foo';");
|
|
}
|
|
|
|
public void testCssNameCheck() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.closurePass = true;
|
|
options.checkMissingGetCssNameLevel = CheckLevel.ERROR;
|
|
options.checkMissingGetCssNameBlacklist = "foo";
|
|
test(options, "var x = 'foo';",
|
|
CheckMissingGetCssName.MISSING_GETCSSNAME);
|
|
}
|
|
|
|
public void testBug2592659() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.closurePass = true;
|
|
options.checkTypes = true;
|
|
options.checkMissingGetCssNameLevel = CheckLevel.WARNING;
|
|
options.checkMissingGetCssNameBlacklist = "foo";
|
|
test(options,
|
|
"var goog = {};\n" +
|
|
"/**\n" +
|
|
" * @param {string} className\n" +
|
|
" * @param {string=} opt_modifier\n" +
|
|
" * @return {string}\n" +
|
|
"*/\n" +
|
|
"goog.getCssName = function(className, opt_modifier) {}\n" +
|
|
"var x = goog.getCssName(123, 'a');",
|
|
TypeValidator.TYPE_MISMATCH_WARNING);
|
|
}
|
|
|
|
public void testTypedefBeforeOwner1() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.closurePass = true;
|
|
test(options,
|
|
"goog.provide('foo.Bar.Type');\n" +
|
|
"goog.provide('foo.Bar');\n" +
|
|
"/** @typedef {number} */ foo.Bar.Type;\n" +
|
|
"foo.Bar = function() {};",
|
|
"var foo = {}; foo.Bar.Type; foo.Bar = function() {};");
|
|
}
|
|
|
|
public void testTypedefBeforeOwner2() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.closurePass = true;
|
|
options.collapseProperties = true;
|
|
test(options,
|
|
"goog.provide('foo.Bar.Type');\n" +
|
|
"goog.provide('foo.Bar');\n" +
|
|
"/** @typedef {number} */ foo.Bar.Type;\n" +
|
|
"foo.Bar = function() {};",
|
|
"var foo$Bar$Type; var foo$Bar = function() {};");
|
|
}
|
|
|
|
public void testExportedNames() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.closurePass = true;
|
|
options.variableRenaming = VariableRenamingPolicy.ALL;
|
|
test(options,
|
|
"/** @define {boolean} */ var COMPILED = false;" +
|
|
"var goog = {}; goog.exportSymbol('b', goog);",
|
|
"var a = true; var c = {}; c.exportSymbol('b', c);");
|
|
test(options,
|
|
"/** @define {boolean} */ var COMPILED = false;" +
|
|
"var goog = {}; goog.exportSymbol('a', goog);",
|
|
"var b = true; var c = {}; c.exportSymbol('a', c);");
|
|
}
|
|
|
|
public void testCheckGlobalThisOn() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.checkSuspiciousCode = true;
|
|
options.checkGlobalThisLevel = CheckLevel.ERROR;
|
|
test(options, "function f() { this.y = 3; }", CheckGlobalThis.GLOBAL_THIS);
|
|
}
|
|
|
|
public void testSusiciousCodeOff() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.checkSuspiciousCode = false;
|
|
options.checkGlobalThisLevel = CheckLevel.ERROR;
|
|
test(options, "function f() { this.y = 3; }", CheckGlobalThis.GLOBAL_THIS);
|
|
}
|
|
|
|
public void testCheckGlobalThisOff() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.checkSuspiciousCode = true;
|
|
options.checkGlobalThisLevel = CheckLevel.OFF;
|
|
testSame(options, "function f() { this.y = 3; }");
|
|
}
|
|
|
|
public void testCheckRequiresAndCheckProvidesOff() {
|
|
testSame(createCompilerOptions(), new String[] {
|
|
"/** @constructor */ function Foo() {}",
|
|
"new Foo();"
|
|
});
|
|
}
|
|
|
|
public void testCheckRequiresOn() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.checkRequires = CheckLevel.ERROR;
|
|
test(options, new String[] {
|
|
"/** @constructor */ function Foo() {}",
|
|
"new Foo();"
|
|
}, CheckRequiresForConstructors.MISSING_REQUIRE_WARNING);
|
|
}
|
|
|
|
public void testCheckProvidesOn() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.checkProvides = CheckLevel.ERROR;
|
|
test(options, new String[] {
|
|
"/** @constructor */ function Foo() {}",
|
|
"new Foo();"
|
|
}, CheckProvides.MISSING_PROVIDE_WARNING);
|
|
}
|
|
|
|
public void testGenerateExportsOff() {
|
|
testSame(createCompilerOptions(), "/** @export */ function f() {}");
|
|
}
|
|
|
|
public void testGenerateExportsOn() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.generateExports = true;
|
|
test(options, "/** @export */ function f() {}",
|
|
"/** @export */ function f() {} goog.exportSymbol('f', f);");
|
|
}
|
|
|
|
public void testExportTestFunctionsOff() {
|
|
testSame(createCompilerOptions(), "function testFoo() {}");
|
|
}
|
|
|
|
public void testExportTestFunctionsOn() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.exportTestFunctions = true;
|
|
test(options, "function testFoo() {}",
|
|
"/** @export */ function testFoo() {}" +
|
|
"goog.exportSymbol('testFoo', testFoo);");
|
|
}
|
|
|
|
public void testExpose() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
CompilationLevel.ADVANCED_OPTIMIZATIONS
|
|
.setOptionsForCompilationLevel(options);
|
|
test(options,
|
|
"var x = {eeny: 1, /** @expose */ meeny: 2};" +
|
|
"/** @constructor */ var Foo = function() {};" +
|
|
"/** @expose */ Foo.prototype.miny = 3;" +
|
|
"Foo.prototype.moe = 4;" +
|
|
"function moe(a, b) { return a.meeny + b.miny; }" +
|
|
"window['x'] = x;" +
|
|
"window['Foo'] = Foo;" +
|
|
"window['moe'] = moe;",
|
|
"function a(){}" +
|
|
"a.prototype.miny=3;" +
|
|
"window.x={a:1,meeny:2};" +
|
|
"window.Foo=a;" +
|
|
"window.moe=function(b,c){" +
|
|
" return b.meeny+c.miny" +
|
|
"}");
|
|
}
|
|
|
|
public void testCheckSymbolsOff() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
testSame(options, "x = 3;");
|
|
}
|
|
|
|
public void testCheckSymbolsOn() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.checkSymbols = true;
|
|
test(options, "x = 3;", VarCheck.UNDEFINED_VAR_ERROR);
|
|
}
|
|
|
|
public void testCheckReferencesOff() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
testSame(options, "x = 3; var x = 5;");
|
|
}
|
|
|
|
public void testCheckReferencesOn() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.aggressiveVarCheck = CheckLevel.ERROR;
|
|
test(options, "x = 3; var x = 5;",
|
|
VariableReferenceCheck.UNDECLARED_REFERENCE);
|
|
}
|
|
|
|
public void testInferTypes() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.inferTypes = true;
|
|
options.checkTypes = false;
|
|
options.closurePass = true;
|
|
|
|
test(options,
|
|
CLOSURE_BOILERPLATE +
|
|
"goog.provide('Foo'); /** @enum */ Foo = {a: 3};",
|
|
TypeCheck.ENUM_NOT_CONSTANT);
|
|
assertTrue(lastCompiler.getErrorManager().getTypedPercent() == 0);
|
|
|
|
// This does not generate a warning.
|
|
test(options, "/** @type {number} */ var n = window.name;",
|
|
"var n = window.name;");
|
|
assertTrue(lastCompiler.getErrorManager().getTypedPercent() == 0);
|
|
}
|
|
|
|
public void testTypeCheckAndInference() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.checkTypes = true;
|
|
test(options, "/** @type {number} */ var n = window.name;",
|
|
TypeValidator.TYPE_MISMATCH_WARNING);
|
|
assertTrue(lastCompiler.getErrorManager().getTypedPercent() > 0);
|
|
}
|
|
|
|
public void testTypeNameParser() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.checkTypes = true;
|
|
test(options, "/** @type {n} */ var n = window.name;",
|
|
RhinoErrorReporter.TYPE_PARSE_ERROR);
|
|
}
|
|
|
|
// This tests that the TypedScopeCreator is memoized so that it only creates a
|
|
// Scope object once for each scope. If, when type inference requests a scope,
|
|
// it creates a new one, then multiple JSType objects end up getting created
|
|
// for the same local type, and ambiguate will rename the last statement to
|
|
// o.a(o.a, o.a), which is bad.
|
|
public void testMemoizedTypedScopeCreator() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.checkTypes = true;
|
|
options.ambiguateProperties = true;
|
|
options.propertyRenaming = PropertyRenamingPolicy.ALL_UNQUOTED;
|
|
test(options, "function someTest() {\n"
|
|
+ " /** @constructor */\n"
|
|
+ " function Foo() { this.instProp = 3; }\n"
|
|
+ " Foo.prototype.protoProp = function(a, b) {};\n"
|
|
+ " /** @constructor\n @extends Foo */\n"
|
|
+ " function Bar() {}\n"
|
|
+ " goog.inherits(Bar, Foo);\n"
|
|
+ " var o = new Bar();\n"
|
|
+ " o.protoProp(o.protoProp, o.instProp);\n"
|
|
+ "}",
|
|
"function someTest() {\n"
|
|
+ " function Foo() { this.b = 3; }\n"
|
|
+ " function Bar() {}\n"
|
|
+ " Foo.prototype.a = function(a, b) {};\n"
|
|
+ " goog.c(Bar, Foo);\n"
|
|
+ " var o = new Bar();\n"
|
|
+ " o.a(o.a, o.b);\n"
|
|
+ "}");
|
|
}
|
|
|
|
public void testCheckTypes() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.checkTypes = true;
|
|
test(options, "var x = x || {}; x.f = function() {}; x.f(3);",
|
|
TypeCheck.WRONG_ARGUMENT_COUNT);
|
|
}
|
|
|
|
public void testReplaceCssNames() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.closurePass = true;
|
|
options.gatherCssNames = true;
|
|
test(options, "/** @define {boolean} */\n"
|
|
+ "var COMPILED = false;\n"
|
|
+ "goog.setCssNameMapping({'foo':'bar'});\n"
|
|
+ "function getCss() {\n"
|
|
+ " return goog.getCssName('foo');\n"
|
|
+ "}",
|
|
"var COMPILED = true;\n"
|
|
+ "function getCss() {\n"
|
|
+ " return \"bar\";"
|
|
+ "}");
|
|
assertEquals(
|
|
ImmutableMap.of("foo", new Integer(1)),
|
|
lastCompiler.getPassConfig().getIntermediateState().cssNames);
|
|
}
|
|
|
|
public void testRemoveClosureAsserts() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.closurePass = true;
|
|
testSame(options,
|
|
"var goog = {};"
|
|
+ "goog.asserts.assert(goog);");
|
|
options.removeClosureAsserts = true;
|
|
test(options,
|
|
"var goog = {};"
|
|
+ "goog.asserts.assert(goog);",
|
|
"var goog = {};");
|
|
}
|
|
|
|
public void testDeprecation() {
|
|
String code = "/** @deprecated */ function f() { } function g() { f(); }";
|
|
|
|
CompilerOptions options = createCompilerOptions();
|
|
testSame(options, code);
|
|
|
|
options.setWarningLevel(DiagnosticGroups.DEPRECATED, CheckLevel.ERROR);
|
|
testSame(options, code);
|
|
|
|
options.checkTypes = true;
|
|
test(options, code, CheckAccessControls.DEPRECATED_NAME);
|
|
}
|
|
|
|
public void testVisibility() {
|
|
String[] code = {
|
|
"/** @private */ function f() { }",
|
|
"function g() { f(); }"
|
|
};
|
|
|
|
CompilerOptions options = createCompilerOptions();
|
|
testSame(options, code);
|
|
|
|
options.setWarningLevel(DiagnosticGroups.VISIBILITY, CheckLevel.ERROR);
|
|
testSame(options, code);
|
|
|
|
options.checkTypes = true;
|
|
test(options, code, CheckAccessControls.BAD_PRIVATE_GLOBAL_ACCESS);
|
|
}
|
|
|
|
public void testUnreachableCode() {
|
|
String code = "function f() { return \n 3; }";
|
|
|
|
CompilerOptions options = createCompilerOptions();
|
|
testSame(options, code);
|
|
|
|
options.checkUnreachableCode = CheckLevel.ERROR;
|
|
test(options, code, CheckUnreachableCode.UNREACHABLE_CODE);
|
|
}
|
|
|
|
public void testMissingReturn() {
|
|
String code =
|
|
"/** @return {number} */ function f() { if (f) { return 3; } }";
|
|
|
|
CompilerOptions options = createCompilerOptions();
|
|
testSame(options, code);
|
|
|
|
options.checkMissingReturn = CheckLevel.ERROR;
|
|
testSame(options, code);
|
|
|
|
options.checkTypes = true;
|
|
test(options, code, CheckMissingReturn.MISSING_RETURN_STATEMENT);
|
|
}
|
|
|
|
public void testIdGenerators() {
|
|
String code = "function f() {} f('id');";
|
|
|
|
CompilerOptions options = createCompilerOptions();
|
|
testSame(options, code);
|
|
|
|
options.idGenerators = Sets.newHashSet("f");
|
|
test(options, code, "function f() {} 'a';");
|
|
}
|
|
|
|
public void testOptimizeArgumentsArray() {
|
|
String code = "function f() { return arguments[0]; }";
|
|
|
|
CompilerOptions options = createCompilerOptions();
|
|
testSame(options, code);
|
|
|
|
options.optimizeArgumentsArray = true;
|
|
String argName = "JSCompiler_OptimizeArgumentsArray_p0";
|
|
test(options, code,
|
|
"function f(" + argName + ") { return " + argName + "; }");
|
|
}
|
|
|
|
public void testOptimizeParameters() {
|
|
String code = "function f(a) { return a; } f(true);";
|
|
|
|
CompilerOptions options = createCompilerOptions();
|
|
testSame(options, code);
|
|
|
|
options.optimizeParameters = true;
|
|
test(options, code, "function f() { var a = true; return a;} f();");
|
|
}
|
|
|
|
public void testOptimizeReturns() {
|
|
String code = "function f(a) { return a; } f(true);";
|
|
|
|
CompilerOptions options = createCompilerOptions();
|
|
testSame(options, code);
|
|
|
|
options.optimizeReturns = true;
|
|
test(options, code, "function f(a) {return;} f(true);");
|
|
}
|
|
|
|
public void testRemoveAbstractMethods() {
|
|
String code = CLOSURE_BOILERPLATE +
|
|
"var x = {}; x.foo = goog.abstractMethod; x.bar = 3;";
|
|
|
|
CompilerOptions options = createCompilerOptions();
|
|
testSame(options, code);
|
|
|
|
options.closurePass = true;
|
|
options.collapseProperties = true;
|
|
test(options, code, CLOSURE_COMPILED + " var x$bar = 3;");
|
|
}
|
|
|
|
public void testCollapseProperties1() {
|
|
String code =
|
|
"var x = {}; x.FOO = 5; x.bar = 3;";
|
|
|
|
CompilerOptions options = createCompilerOptions();
|
|
testSame(options, code);
|
|
|
|
options.collapseProperties = true;
|
|
test(options, code, "var x$FOO = 5; var x$bar = 3;");
|
|
}
|
|
|
|
public void testCollapseProperties2() {
|
|
String code =
|
|
"var x = {}; x.FOO = 5; x.bar = 3;";
|
|
|
|
CompilerOptions options = createCompilerOptions();
|
|
testSame(options, code);
|
|
|
|
options.collapseProperties = true;
|
|
options.collapseObjectLiterals = true;
|
|
test(options, code, "var x$FOO = 5; var x$bar = 3;");
|
|
}
|
|
|
|
public void testCollapseObjectLiteral1() {
|
|
// Verify collapseObjectLiterals does nothing in global scope
|
|
String code = "var x = {}; x.FOO = 5; x.bar = 3;";
|
|
|
|
CompilerOptions options = createCompilerOptions();
|
|
testSame(options, code);
|
|
|
|
options.collapseObjectLiterals = true;
|
|
testSame(options, code);
|
|
}
|
|
|
|
public void testCollapseObjectLiteral2() {
|
|
String code =
|
|
"function f() {var x = {}; x.FOO = 5; x.bar = 3;}";
|
|
|
|
CompilerOptions options = createCompilerOptions();
|
|
testSame(options, code);
|
|
|
|
options.collapseObjectLiterals = true;
|
|
test(options, code,
|
|
"function f(){" +
|
|
"var JSCompiler_object_inline_FOO_0;" +
|
|
"var JSCompiler_object_inline_bar_1;" +
|
|
"JSCompiler_object_inline_FOO_0=5;" +
|
|
"JSCompiler_object_inline_bar_1=3}");
|
|
}
|
|
|
|
public void testTightenTypesWithoutTypeCheck() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.tightenTypes = true;
|
|
test(options, "", DefaultPassConfig.TIGHTEN_TYPES_WITHOUT_TYPE_CHECK);
|
|
}
|
|
|
|
public void testDisambiguateProperties() {
|
|
String code =
|
|
"/** @constructor */ function Foo(){} Foo.prototype.bar = 3;" +
|
|
"/** @constructor */ function Baz(){} Baz.prototype.bar = 3;";
|
|
|
|
CompilerOptions options = createCompilerOptions();
|
|
testSame(options, code);
|
|
|
|
options.disambiguateProperties = true;
|
|
options.checkTypes = true;
|
|
test(options, code,
|
|
"function Foo(){} Foo.prototype.Foo_prototype$bar = 3;" +
|
|
"function Baz(){} Baz.prototype.Baz_prototype$bar = 3;");
|
|
}
|
|
|
|
public void testMarkPureCalls() {
|
|
String testCode = "function foo() {} foo();";
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.removeDeadCode = true;
|
|
|
|
testSame(options, testCode);
|
|
|
|
options.computeFunctionSideEffects = true;
|
|
test(options, testCode, "function foo() {}");
|
|
}
|
|
|
|
public void testMarkNoSideEffects() {
|
|
String testCode = "noSideEffects();";
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.removeDeadCode = true;
|
|
|
|
testSame(options, testCode);
|
|
|
|
options.markNoSideEffectCalls = true;
|
|
test(options, testCode, "");
|
|
}
|
|
|
|
public void testChainedCalls() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.chainCalls = true;
|
|
test(
|
|
options,
|
|
"/** @constructor */ function Foo() {} " +
|
|
"Foo.prototype.bar = function() { return this; }; " +
|
|
"var f = new Foo();" +
|
|
"f.bar(); " +
|
|
"f.bar(); ",
|
|
"function Foo() {} " +
|
|
"Foo.prototype.bar = function() { return this; }; " +
|
|
"var f = new Foo();" +
|
|
"f.bar().bar();");
|
|
}
|
|
|
|
public void testExtraAnnotationNames() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.setExtraAnnotationNames(Sets.newHashSet("TagA", "TagB"));
|
|
test(
|
|
options,
|
|
"/** @TagA */ var f = new Foo(); /** @TagB */ f.bar();",
|
|
"var f = new Foo(); f.bar();");
|
|
}
|
|
|
|
public void testDevirtualizePrototypeMethods() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.devirtualizePrototypeMethods = true;
|
|
test(
|
|
options,
|
|
"/** @constructor */ var Foo = function() {}; " +
|
|
"Foo.prototype.bar = function() {};" +
|
|
"(new Foo()).bar();",
|
|
"var Foo = function() {};" +
|
|
"var JSCompiler_StaticMethods_bar = " +
|
|
" function(JSCompiler_StaticMethods_bar$self) {};" +
|
|
"JSCompiler_StaticMethods_bar(new Foo());");
|
|
}
|
|
|
|
public void testCheckConsts() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.inlineConstantVars = true;
|
|
test(options, "var FOO = true; FOO = false",
|
|
ConstCheck.CONST_REASSIGNED_VALUE_ERROR);
|
|
}
|
|
|
|
public void testAllChecksOn() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.checkSuspiciousCode = true;
|
|
options.checkControlStructures = true;
|
|
options.checkRequires = CheckLevel.ERROR;
|
|
options.checkProvides = CheckLevel.ERROR;
|
|
options.generateExports = true;
|
|
options.exportTestFunctions = true;
|
|
options.closurePass = true;
|
|
options.checkMissingGetCssNameLevel = CheckLevel.ERROR;
|
|
options.checkMissingGetCssNameBlacklist = "goog";
|
|
options.syntheticBlockStartMarker = "synStart";
|
|
options.syntheticBlockEndMarker = "synEnd";
|
|
options.checkSymbols = true;
|
|
options.aggressiveVarCheck = CheckLevel.ERROR;
|
|
options.processObjectPropertyString = true;
|
|
options.collapseProperties = true;
|
|
test(options, CLOSURE_BOILERPLATE, CLOSURE_COMPILED);
|
|
}
|
|
|
|
public void testTypeCheckingWithSyntheticBlocks() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.syntheticBlockStartMarker = "synStart";
|
|
options.syntheticBlockEndMarker = "synEnd";
|
|
options.checkTypes = true;
|
|
|
|
// We used to have a bug where the CFG drew an
|
|
// edge straight from synStart to f(progress).
|
|
// If that happens, then progress will get type {number|undefined}.
|
|
testSame(
|
|
options,
|
|
"/** @param {number} x */ function f(x) {}" +
|
|
"function g() {" +
|
|
" synStart('foo');" +
|
|
" var progress = 1;" +
|
|
" f(progress);" +
|
|
" synEnd('foo');" +
|
|
"}");
|
|
}
|
|
|
|
public void testCompilerDoesNotBlowUpIfUndefinedSymbols() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.checkSymbols = true;
|
|
|
|
// Disable the undefined variable check.
|
|
options.setWarningLevel(
|
|
DiagnosticGroup.forType(VarCheck.UNDEFINED_VAR_ERROR),
|
|
CheckLevel.OFF);
|
|
|
|
// The compiler used to throw an IllegalStateException on this.
|
|
testSame(options, "var x = {foo: y};");
|
|
}
|
|
|
|
// Make sure that if we change variables which are constant to have
|
|
// $$constant appended to their names, we remove that tag before
|
|
// we finish.
|
|
public void testConstantTagsMustAlwaysBeRemoved() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
|
|
options.variableRenaming = VariableRenamingPolicy.LOCAL;
|
|
String originalText = "var G_GEO_UNKNOWN_ADDRESS=1;\n" +
|
|
"function foo() {" +
|
|
" var localVar = 2;\n" +
|
|
" if (G_GEO_UNKNOWN_ADDRESS == localVar) {\n" +
|
|
" alert(\"A\"); }}";
|
|
String expectedText = "var G_GEO_UNKNOWN_ADDRESS=1;" +
|
|
"function foo(){var a=2;if(G_GEO_UNKNOWN_ADDRESS==a){alert(\"A\")}}";
|
|
|
|
test(options, originalText, expectedText);
|
|
}
|
|
|
|
public void testClosurePassPreservesJsDoc() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.checkTypes = true;
|
|
options.closurePass = true;
|
|
|
|
test(options,
|
|
CLOSURE_BOILERPLATE +
|
|
"goog.provide('Foo'); /** @constructor */ Foo = function() {};" +
|
|
"var x = new Foo();",
|
|
"var COMPILED=true;var goog={};goog.exportSymbol=function(){};" +
|
|
"var Foo=function(){};var x=new Foo");
|
|
test(options,
|
|
CLOSURE_BOILERPLATE +
|
|
"goog.provide('Foo'); /** @enum */ Foo = {a: 3};",
|
|
TypeCheck.ENUM_NOT_CONSTANT);
|
|
}
|
|
|
|
public void testProvidedNamespaceIsConst() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.closurePass = true;
|
|
options.inlineConstantVars = true;
|
|
options.collapseProperties = true;
|
|
test(options,
|
|
"var goog = {}; goog.provide('foo'); " +
|
|
"function f() { foo = {};}",
|
|
"var foo = {}; function f() { foo = {}; }",
|
|
ConstCheck.CONST_REASSIGNED_VALUE_ERROR);
|
|
}
|
|
|
|
public void testProvidedNamespaceIsConst2() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.closurePass = true;
|
|
options.inlineConstantVars = true;
|
|
options.collapseProperties = true;
|
|
test(options,
|
|
"var goog = {}; goog.provide('foo.bar'); " +
|
|
"function f() { foo.bar = {};}",
|
|
"var foo$bar = {};" +
|
|
"function f() { foo$bar = {}; }",
|
|
ConstCheck.CONST_REASSIGNED_VALUE_ERROR);
|
|
}
|
|
|
|
public void testProvidedNamespaceIsConst3() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.closurePass = true;
|
|
options.inlineConstantVars = true;
|
|
options.collapseProperties = true;
|
|
test(options,
|
|
"var goog = {}; " +
|
|
"goog.provide('foo.bar'); goog.provide('foo.bar.baz'); " +
|
|
"/** @constructor */ foo.bar = function() {};" +
|
|
"/** @constructor */ foo.bar.baz = function() {};",
|
|
"var foo$bar = function(){};" +
|
|
"var foo$bar$baz = function(){};");
|
|
}
|
|
|
|
public void testProvidedNamespaceIsConst4() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.closurePass = true;
|
|
options.inlineConstantVars = true;
|
|
options.collapseProperties = true;
|
|
test(options,
|
|
"var goog = {}; goog.provide('foo.Bar'); " +
|
|
"var foo = {}; foo.Bar = {};",
|
|
"var foo = {}; foo = {}; foo.Bar = {};");
|
|
}
|
|
|
|
public void testProvidedNamespaceIsConst5() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.closurePass = true;
|
|
options.inlineConstantVars = true;
|
|
options.collapseProperties = true;
|
|
test(options,
|
|
"var goog = {}; goog.provide('foo.Bar'); " +
|
|
"foo = {}; foo.Bar = {};",
|
|
"var foo = {}; foo = {}; foo.Bar = {};");
|
|
}
|
|
|
|
public void testProcessDefinesAlwaysOn() {
|
|
test(createCompilerOptions(),
|
|
"/** @define {boolean} */ var HI = true; HI = false;",
|
|
"var HI = false;false;");
|
|
}
|
|
|
|
public void testProcessDefinesAdditionalReplacements() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.setDefineToBooleanLiteral("HI", false);
|
|
test(options,
|
|
"/** @define {boolean} */ var HI = true;",
|
|
"var HI = false;");
|
|
}
|
|
|
|
public void testReplaceMessages() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String prefix = "var goog = {}; goog.getMsg = function() {};";
|
|
testSame(options, prefix + "var MSG_HI = goog.getMsg('hi');");
|
|
|
|
options.messageBundle = new EmptyMessageBundle();
|
|
test(options,
|
|
prefix + "/** @desc xyz */ var MSG_HI = goog.getMsg('hi');",
|
|
prefix + "var MSG_HI = 'hi';");
|
|
}
|
|
|
|
public void testCheckGlobalNames() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.checkGlobalNamesLevel = CheckLevel.ERROR;
|
|
test(options, "var x = {}; var y = x.z;",
|
|
CheckGlobalNames.UNDEFINED_NAME_WARNING);
|
|
}
|
|
|
|
public void testInlineGetters() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code =
|
|
"function Foo() {} Foo.prototype.bar = function() { return 3; };" +
|
|
"var x = new Foo(); x.bar();";
|
|
|
|
testSame(options, code);
|
|
options.inlineGetters = true;
|
|
|
|
test(options, code,
|
|
"function Foo() {} Foo.prototype.bar = function() { return 3 };" +
|
|
"var x = new Foo(); 3;");
|
|
}
|
|
|
|
public void testInlineGettersWithAmbiguate() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
|
|
String code =
|
|
"/** @constructor */" +
|
|
"function Foo() {}" +
|
|
"/** @type {number} */ Foo.prototype.field;" +
|
|
"Foo.prototype.getField = function() { return this.field; };" +
|
|
"/** @constructor */" +
|
|
"function Bar() {}" +
|
|
"/** @type {string} */ Bar.prototype.field;" +
|
|
"Bar.prototype.getField = function() { return this.field; };" +
|
|
"new Foo().getField();" +
|
|
"new Bar().getField();";
|
|
|
|
testSame(options, code);
|
|
|
|
options.inlineGetters = true;
|
|
|
|
test(options, code,
|
|
"function Foo() {}" +
|
|
"Foo.prototype.field;" +
|
|
"Foo.prototype.getField = function() { return this.field; };" +
|
|
"function Bar() {}" +
|
|
"Bar.prototype.field;" +
|
|
"Bar.prototype.getField = function() { return this.field; };" +
|
|
"new Foo().field;" +
|
|
"new Bar().field;");
|
|
|
|
options.checkTypes = true;
|
|
options.ambiguateProperties = true;
|
|
|
|
// Propagating the wrong type information may cause ambiguate properties
|
|
// to generate bad code.
|
|
testSame(options, code);
|
|
}
|
|
|
|
public void testInlineVariables() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code = "function foo() {} var x = 3; foo(x);";
|
|
testSame(options, code);
|
|
|
|
options.inlineVariables = true;
|
|
test(options, code, "(function foo() {})(3);");
|
|
|
|
options.propertyRenaming = PropertyRenamingPolicy.HEURISTIC;
|
|
test(options, code, DefaultPassConfig.CANNOT_USE_PROTOTYPE_AND_VAR);
|
|
}
|
|
|
|
public void testInlineConstants() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code = "function foo() {} var x = 3; foo(x); var YYY = 4; foo(YYY);";
|
|
testSame(options, code);
|
|
|
|
options.inlineConstantVars = true;
|
|
test(options, code, "function foo() {} var x = 3; foo(x); foo(4);");
|
|
}
|
|
|
|
public void testMinimizeExits() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code =
|
|
"function f() {" +
|
|
" if (window.foo) return; window.h(); " +
|
|
"}";
|
|
testSame(options, code);
|
|
|
|
options.foldConstants = true;
|
|
test(
|
|
options, code,
|
|
"function f() {" +
|
|
" window.foo || window.h(); " +
|
|
"}");
|
|
}
|
|
|
|
public void testFoldConstants() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code = "if (true) { window.foo(); }";
|
|
testSame(options, code);
|
|
|
|
options.foldConstants = true;
|
|
test(options, code, "window.foo();");
|
|
}
|
|
|
|
public void testRemoveUnreachableCode() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code = "function f() { return; f(); }";
|
|
testSame(options, code);
|
|
|
|
options.removeDeadCode = true;
|
|
test(options, code, "function f() {}");
|
|
}
|
|
|
|
public void testRemoveUnusedPrototypeProperties1() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code = "function Foo() {} " +
|
|
"Foo.prototype.bar = function() { return new Foo(); };";
|
|
testSame(options, code);
|
|
|
|
options.removeUnusedPrototypeProperties = true;
|
|
test(options, code, "function Foo() {}");
|
|
}
|
|
|
|
public void testRemoveUnusedPrototypeProperties2() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code = "function Foo() {} " +
|
|
"Foo.prototype.bar = function() { return new Foo(); };" +
|
|
"function f(x) { x.bar(); }";
|
|
testSame(options, code);
|
|
|
|
options.removeUnusedPrototypeProperties = true;
|
|
testSame(options, code);
|
|
|
|
options.removeUnusedVars = true;
|
|
test(options, code, "");
|
|
}
|
|
|
|
public void testSmartNamePass() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code = "function Foo() { this.bar(); } " +
|
|
"Foo.prototype.bar = function() { return Foo(); };";
|
|
testSame(options, code);
|
|
|
|
options.smartNameRemoval = true;
|
|
test(options, code, "");
|
|
}
|
|
|
|
public void testDeadAssignmentsElimination() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code = "function f() { var x = 3; 4; x = 5; return x; } f(); ";
|
|
testSame(options, code);
|
|
|
|
options.deadAssignmentElimination = true;
|
|
testSame(options, code);
|
|
|
|
options.removeUnusedVars = true;
|
|
test(options, code, "function f() { var x = 3; 4; x = 5; return x; } f();");
|
|
}
|
|
|
|
public void testInlineFunctions() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code = "function f() { return 3; } f(); ";
|
|
testSame(options, code);
|
|
|
|
options.inlineFunctions = true;
|
|
test(options, code, "3;");
|
|
}
|
|
|
|
public void testRemoveUnusedVars1() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code = "function f(x) {} f();";
|
|
testSame(options, code);
|
|
|
|
options.removeUnusedVars = true;
|
|
test(options, code, "function f() {} f();");
|
|
}
|
|
|
|
public void testRemoveUnusedVars2() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code = "(function f(x) {})();var g = function() {}; g();";
|
|
testSame(options, code);
|
|
|
|
options.removeUnusedVars = true;
|
|
test(options, code, "(function() {})();var g = function() {}; g();");
|
|
|
|
options.anonymousFunctionNaming = AnonymousFunctionNamingPolicy.UNMAPPED;
|
|
test(options, code, "(function f() {})();var g = function $g$() {}; g();");
|
|
}
|
|
|
|
public void testCrossModuleCodeMotion() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String[] code = new String[] {
|
|
"var x = 1;",
|
|
"x;",
|
|
};
|
|
testSame(options, code);
|
|
|
|
options.crossModuleCodeMotion = true;
|
|
test(options, code, new String[] {
|
|
"",
|
|
"var x = 1; x;",
|
|
});
|
|
}
|
|
|
|
public void testCrossModuleMethodMotion() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String[] code = new String[] {
|
|
"var Foo = function() {}; Foo.prototype.bar = function() {};" +
|
|
"var x = new Foo();",
|
|
"x.bar();",
|
|
};
|
|
testSame(options, code);
|
|
|
|
options.crossModuleMethodMotion = true;
|
|
test(options, code, new String[] {
|
|
CrossModuleMethodMotion.STUB_DECLARATIONS +
|
|
"var Foo = function() {};" +
|
|
"Foo.prototype.bar=JSCompiler_stubMethod(0); var x=new Foo;",
|
|
"Foo.prototype.bar=JSCompiler_unstubMethod(0,function(){}); x.bar()",
|
|
});
|
|
}
|
|
|
|
public void testFlowSensitiveInlineVariables1() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code = "function f() { var x = 3; x = 5; return x; }";
|
|
testSame(options, code);
|
|
|
|
options.flowSensitiveInlineVariables = true;
|
|
test(options, code, "function f() { var x = 3; return 5; }");
|
|
|
|
String unusedVar = "function f() { var x; x = 5; return x; } f()";
|
|
test(options, unusedVar, "function f() { var x; return 5; } f()");
|
|
|
|
options.removeUnusedVars = true;
|
|
test(options, unusedVar, "function f() { return 5; } f()");
|
|
}
|
|
|
|
public void testFlowSensitiveInlineVariables2() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
CompilationLevel.SIMPLE_OPTIMIZATIONS
|
|
.setOptionsForCompilationLevel(options);
|
|
test(options,
|
|
"function f () {\n" +
|
|
" var ab = 0;\n" +
|
|
" ab += '-';\n" +
|
|
" alert(ab);\n" +
|
|
"}",
|
|
"function f () {\n" +
|
|
" alert('0-');\n" +
|
|
"}");
|
|
}
|
|
|
|
public void testCollapseAnonymousFunctions() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code = "var f = function() {};";
|
|
testSame(options, code);
|
|
|
|
options.collapseAnonymousFunctions = true;
|
|
test(options, code, "function f() {}");
|
|
}
|
|
|
|
public void testMoveFunctionDeclarations() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code = "var x = f(); function f() { return 3; }";
|
|
testSame(options, code);
|
|
|
|
options.moveFunctionDeclarations = true;
|
|
test(options, code, "function f() { return 3; } var x = f();");
|
|
}
|
|
|
|
public void testNameAnonymousFunctions() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code = "var f = function() {};";
|
|
testSame(options, code);
|
|
|
|
options.anonymousFunctionNaming = AnonymousFunctionNamingPolicy.MAPPED;
|
|
test(options, code, "var f = function $() {}");
|
|
assertNotNull(lastCompiler.getResult().namedAnonFunctionMap);
|
|
|
|
options.anonymousFunctionNaming = AnonymousFunctionNamingPolicy.UNMAPPED;
|
|
test(options, code, "var f = function $f$() {}");
|
|
assertNull(lastCompiler.getResult().namedAnonFunctionMap);
|
|
}
|
|
|
|
public void testNameAnonymousFunctionsWithVarRemoval() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.setRemoveUnusedVariables(CompilerOptions.Reach.LOCAL_ONLY);
|
|
options.setInlineVariables(true);
|
|
String code = "var f = function longName() {}; var g = function() {};" +
|
|
"function longerName() {} var i = longerName;";
|
|
test(options, code,
|
|
"var f = function() {}; var g = function() {}; " +
|
|
"var i = function() {};");
|
|
|
|
options.anonymousFunctionNaming = AnonymousFunctionNamingPolicy.MAPPED;
|
|
test(options, code,
|
|
"var f = function longName() {}; var g = function $() {};" +
|
|
"var i = function longerName(){};");
|
|
assertNotNull(lastCompiler.getResult().namedAnonFunctionMap);
|
|
|
|
options.anonymousFunctionNaming = AnonymousFunctionNamingPolicy.UNMAPPED;
|
|
test(options, code,
|
|
"var f = function longName() {}; var g = function $g$() {};" +
|
|
"var i = function longerName(){};");
|
|
assertNull(lastCompiler.getResult().namedAnonFunctionMap);
|
|
}
|
|
|
|
public void testExtractPrototypeMemberDeclarations() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code = "var f = function() {};";
|
|
String expected = "var a; var b = function() {}; a = b.prototype;";
|
|
for (int i = 0; i < 10; i++) {
|
|
code += "f.prototype.a = " + i + ";";
|
|
expected += "a.a = " + i + ";";
|
|
}
|
|
testSame(options, code);
|
|
|
|
options.extractPrototypeMemberDeclarations = true;
|
|
options.variableRenaming = VariableRenamingPolicy.ALL;
|
|
test(options, code, expected);
|
|
|
|
options.propertyRenaming = PropertyRenamingPolicy.HEURISTIC;
|
|
options.variableRenaming = VariableRenamingPolicy.OFF;
|
|
testSame(options, code);
|
|
}
|
|
|
|
public void testDevirtualizationAndExtractPrototypeMemberDeclarations() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.devirtualizePrototypeMethods = true;
|
|
options.collapseAnonymousFunctions = true;
|
|
options.extractPrototypeMemberDeclarations = true;
|
|
options.variableRenaming = VariableRenamingPolicy.ALL;
|
|
String code = "var f = function() {};";
|
|
String expected = "var a; function b() {} a = b.prototype;";
|
|
for (int i = 0; i < 10; i++) {
|
|
code += "f.prototype.argz = function() {arguments};";
|
|
code += "f.prototype.devir" + i + " = function() {};";
|
|
|
|
char letter = (char) ('d' + i);
|
|
expected += "a.argz = function() {arguments};";
|
|
expected += "function " + letter + "(c){}";
|
|
}
|
|
|
|
code += "var F = new f(); F.argz();";
|
|
expected += "var n = new b(); n.argz();";
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
code += "F.devir" + i + "();";
|
|
|
|
char letter = (char) ('d' + i);
|
|
expected += letter + "(n);";
|
|
}
|
|
test(options, code, expected);
|
|
}
|
|
|
|
public void testCoalesceVariableNames() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code = "function f() {var x = 3; var y = x; var z = y; return z;}";
|
|
testSame(options, code);
|
|
|
|
options.coalesceVariableNames = true;
|
|
test(options, code,
|
|
"function f() {var x = 3; x = x; x = x; return x;}");
|
|
}
|
|
|
|
public void testPropertyRenaming() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.propertyAffinity = true;
|
|
String code =
|
|
"function f() { return this.foo + this['bar'] + this.Baz; }" +
|
|
"f.prototype.bar = 3; f.prototype.Baz = 3;";
|
|
String heuristic =
|
|
"function f() { return this.foo + this['bar'] + this.a; }" +
|
|
"f.prototype.bar = 3; f.prototype.a = 3;";
|
|
String aggHeuristic =
|
|
"function f() { return this.foo + this['b'] + this.a; } " +
|
|
"f.prototype.b = 3; f.prototype.a = 3;";
|
|
String all =
|
|
"function f() { return this.b + this['bar'] + this.a; }" +
|
|
"f.prototype.c = 3; f.prototype.a = 3;";
|
|
testSame(options, code);
|
|
|
|
options.propertyRenaming = PropertyRenamingPolicy.HEURISTIC;
|
|
test(options, code, heuristic);
|
|
|
|
options.propertyRenaming = PropertyRenamingPolicy.AGGRESSIVE_HEURISTIC;
|
|
test(options, code, aggHeuristic);
|
|
|
|
options.propertyRenaming = PropertyRenamingPolicy.ALL_UNQUOTED;
|
|
test(options, code, all);
|
|
}
|
|
|
|
public void testConvertToDottedProperties() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code =
|
|
"function f() { return this['bar']; } f.prototype.bar = 3;";
|
|
String expected =
|
|
"function f() { return this.bar; } f.prototype.a = 3;";
|
|
testSame(options, code);
|
|
|
|
options.convertToDottedProperties = true;
|
|
options.propertyRenaming = PropertyRenamingPolicy.ALL_UNQUOTED;
|
|
test(options, code, expected);
|
|
}
|
|
|
|
public void testRewriteFunctionExpressions() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code = "var a = function() {};";
|
|
String expected = "function JSCompiler_emptyFn(){return function(){}} " +
|
|
"var a = JSCompiler_emptyFn();";
|
|
for (int i = 0; i < 10; i++) {
|
|
code += "a = function() {};";
|
|
expected += "a = JSCompiler_emptyFn();";
|
|
}
|
|
testSame(options, code);
|
|
|
|
options.rewriteFunctionExpressions = true;
|
|
test(options, code, expected);
|
|
}
|
|
|
|
public void testAliasAllStrings() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code = "function f() { return 'a'; }";
|
|
String expected = "var $$S_a = 'a'; function f() { return $$S_a; }";
|
|
testSame(options, code);
|
|
|
|
options.aliasAllStrings = true;
|
|
test(options, code, expected);
|
|
}
|
|
|
|
public void testAliasExterns() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code = "function f() { return window + window + window + window; }";
|
|
String expected = "var GLOBAL_window = window;" +
|
|
"function f() { return GLOBAL_window + GLOBAL_window + " +
|
|
" GLOBAL_window + GLOBAL_window; }";
|
|
testSame(options, code);
|
|
|
|
options.aliasExternals = true;
|
|
test(options, code, expected);
|
|
}
|
|
|
|
public void testAliasKeywords() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code =
|
|
"function f() { return true + true + true + true + true + true; }";
|
|
String expected = "var JSCompiler_alias_TRUE = true;" +
|
|
"function f() { return JSCompiler_alias_TRUE + " +
|
|
" JSCompiler_alias_TRUE + JSCompiler_alias_TRUE + " +
|
|
" JSCompiler_alias_TRUE + JSCompiler_alias_TRUE + " +
|
|
" JSCompiler_alias_TRUE; }";
|
|
testSame(options, code);
|
|
|
|
options.aliasKeywords = true;
|
|
test(options, code, expected);
|
|
}
|
|
|
|
public void testRenameVars1() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code =
|
|
"var abc = 3; function f() { var xyz = 5; return abc + xyz; }";
|
|
String local = "var abc = 3; function f() { var a = 5; return abc + a; }";
|
|
String all = "var a = 3; function c() { var b = 5; return a + b; }";
|
|
testSame(options, code);
|
|
|
|
options.variableRenaming = VariableRenamingPolicy.LOCAL;
|
|
test(options, code, local);
|
|
|
|
options.variableRenaming = VariableRenamingPolicy.ALL;
|
|
test(options, code, all);
|
|
|
|
options.reserveRawExports = true;
|
|
}
|
|
|
|
public void testRenameVars2() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.variableRenaming = VariableRenamingPolicy.ALL;
|
|
|
|
String code = "var abc = 3; function f() { window['a'] = 5; }";
|
|
String noexport = "var a = 3; function b() { window['a'] = 5; }";
|
|
String export = "var b = 3; function c() { window['a'] = 5; }";
|
|
|
|
options.reserveRawExports = false;
|
|
test(options, code, noexport);
|
|
|
|
options.reserveRawExports = true;
|
|
test(options, code, export);
|
|
}
|
|
|
|
public void testShadowVaribles() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.variableRenaming = VariableRenamingPolicy.LOCAL;
|
|
options.shadowVariables = true;
|
|
String code = "var f = function(x) { return function(y) {}}";
|
|
String expected = "var f = function(a) { return function(a) {}}";
|
|
test(options, code, expected);
|
|
}
|
|
|
|
public void testRenameLabels() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code = "longLabel: for(;true;) { break longLabel; }";
|
|
String expected = "a: for(;true;) { break a; }";
|
|
testSame(options, code);
|
|
|
|
options.labelRenaming = true;
|
|
test(options, code, expected);
|
|
}
|
|
|
|
public void testBadBreakStatementInIdeMode() {
|
|
// Ensure that type-checking doesn't crash, even if the CFG is malformed.
|
|
// This can happen in IDE mode.
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.ideMode = true;
|
|
options.checkTypes = true;
|
|
test(options,
|
|
"function f() { try { } catch(e) { break; } }",
|
|
RhinoErrorReporter.PARSE_ERROR);
|
|
}
|
|
|
|
public void testIssue63SourceMap() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code = "var a;";
|
|
|
|
options.skipAllPasses = true;
|
|
options.sourceMapOutputPath = "./src.map";
|
|
|
|
Compiler compiler = compile(options, code);
|
|
compiler.toSource();
|
|
}
|
|
|
|
public void testRegExp1() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.foldConstants = true;
|
|
|
|
String code = "/(a)/.test(\"a\");";
|
|
|
|
testSame(options, code);
|
|
|
|
options.computeFunctionSideEffects = true;
|
|
|
|
String expected = "";
|
|
|
|
test(options, code, expected);
|
|
}
|
|
|
|
public void testRegExp2() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
|
|
options.foldConstants = true;
|
|
|
|
String code = "/(a)/.test(\"a\");var a = RegExp.$1";
|
|
|
|
testSame(options, code);
|
|
|
|
options.computeFunctionSideEffects = true;
|
|
|
|
test(options, code, CheckRegExp.REGEXP_REFERENCE);
|
|
|
|
options.setWarningLevel(DiagnosticGroups.CHECK_REGEXP, CheckLevel.OFF);
|
|
|
|
testSame(options, code);
|
|
}
|
|
|
|
public void testFoldLocals1() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
|
|
options.foldConstants = true;
|
|
|
|
// An external object, whose constructor has no side-effects,
|
|
// and whose method "go" only modifies the object.
|
|
String code = "new Widget().go();";
|
|
|
|
testSame(options, code);
|
|
|
|
options.computeFunctionSideEffects = true;
|
|
|
|
test(options, code, "");
|
|
}
|
|
|
|
public void testFoldLocals2() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
|
|
options.foldConstants = true;
|
|
options.checkTypes = true;
|
|
|
|
// An external function that returns a local object that the
|
|
// method "go" that only modifies the object.
|
|
String code = "widgetToken().go();";
|
|
|
|
testSame(options, code);
|
|
|
|
options.computeFunctionSideEffects = true;
|
|
|
|
test(options, code, "widgetToken()");
|
|
}
|
|
|
|
|
|
public void testFoldLocals3() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
|
|
options.foldConstants = true;
|
|
|
|
// A function "f" who returns a known local object, and a method that
|
|
// modifies only modifies that.
|
|
String definition = "function f(){return new Widget()}";
|
|
String call = "f().go();";
|
|
String code = definition + call;
|
|
|
|
testSame(options, code);
|
|
|
|
options.computeFunctionSideEffects = true;
|
|
|
|
// BROKEN
|
|
//test(options, code, definition);
|
|
testSame(options, code);
|
|
}
|
|
|
|
public void testFoldLocals4() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
|
|
options.foldConstants = true;
|
|
|
|
String code = "/** @constructor */\n"
|
|
+ "function InternalWidget(){this.x = 1;}"
|
|
+ "InternalWidget.prototype.internalGo = function (){this.x = 2};"
|
|
+ "new InternalWidget().internalGo();";
|
|
|
|
testSame(options, code);
|
|
|
|
options.computeFunctionSideEffects = true;
|
|
|
|
String optimized = ""
|
|
+ "function InternalWidget(){this.x = 1;}"
|
|
+ "InternalWidget.prototype.internalGo = function (){this.x = 2};";
|
|
|
|
test(options, code, optimized);
|
|
}
|
|
|
|
public void testFoldLocals5() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
|
|
options.foldConstants = true;
|
|
|
|
String code = ""
|
|
+ "function fn(){var a={};a.x={};return a}"
|
|
+ "fn().x.y = 1;";
|
|
|
|
// "fn" returns a unescaped local object, we should be able to fold it,
|
|
// but we don't currently.
|
|
String result = ""
|
|
+ "function fn(){var a={x:{}};return a}"
|
|
+ "fn().x.y = 1;";
|
|
|
|
test(options, code, result);
|
|
|
|
options.computeFunctionSideEffects = true;
|
|
|
|
test(options, code, result);
|
|
}
|
|
|
|
public void testFoldLocals6() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
|
|
options.foldConstants = true;
|
|
|
|
String code = ""
|
|
+ "function fn(){return {}}"
|
|
+ "fn().x.y = 1;";
|
|
|
|
testSame(options, code);
|
|
|
|
options.computeFunctionSideEffects = true;
|
|
|
|
testSame(options, code);
|
|
}
|
|
|
|
public void testFoldLocals7() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
|
|
options.foldConstants = true;
|
|
|
|
String code = ""
|
|
+ "function InternalWidget(){return [];}"
|
|
+ "Array.prototype.internalGo = function (){this.x = 2};"
|
|
+ "InternalWidget().internalGo();";
|
|
|
|
testSame(options, code);
|
|
|
|
options.computeFunctionSideEffects = true;
|
|
|
|
String optimized = ""
|
|
+ "function InternalWidget(){return [];}"
|
|
+ "Array.prototype.internalGo = function (){this.x = 2};";
|
|
|
|
test(options, code, optimized);
|
|
}
|
|
|
|
public void testVarDeclarationsIntoFor() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
|
|
options.collapseVariableDeclarations = false;
|
|
|
|
String code = "var a = 1; for (var b = 2; ;) {}";
|
|
|
|
testSame(options, code);
|
|
|
|
options.collapseVariableDeclarations = true;
|
|
|
|
test(options, code, "for (var a = 1, b = 2; ;) {}");
|
|
}
|
|
|
|
public void testExploitAssigns() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
|
|
options.collapseVariableDeclarations = false;
|
|
|
|
String code = "a = 1; b = a; c = b";
|
|
|
|
testSame(options, code);
|
|
|
|
options.collapseVariableDeclarations = true;
|
|
|
|
test(options, code, "c=b=a=1");
|
|
}
|
|
|
|
public void testRecoverOnBadExterns() throws Exception {
|
|
// This test is for a bug in a very narrow set of circumstances:
|
|
// 1) externs validation has to be off.
|
|
// 2) aliasExternals has to be on.
|
|
// 3) The user has to reference a "normal" variable in externs.
|
|
// This case is handled at checking time by injecting a
|
|
// synthetic extern variable, and adding a "@suppress {duplicate}" to
|
|
// the normal code at compile time. But optimizations may remove that
|
|
// annotation, so we need to make sure that the variable declarations
|
|
// are de-duped before that happens.
|
|
CompilerOptions options = createCompilerOptions();
|
|
|
|
options.aliasExternals = true;
|
|
externs = ImmutableList.of(
|
|
SourceFile.fromCode("externs", "extern.foo"));
|
|
|
|
test(options,
|
|
"var extern; " +
|
|
"function f() { return extern + extern + extern + extern; }",
|
|
"var extern; " +
|
|
"function f() { return extern + extern + extern + extern; }",
|
|
VarCheck.UNDEFINED_EXTERN_VAR_ERROR);
|
|
}
|
|
|
|
public void testDuplicateVariablesInExterns() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.checkSymbols = true;
|
|
externs = ImmutableList.of(
|
|
SourceFile.fromCode("externs",
|
|
"var externs = {}; /** @suppress {duplicate} */ var externs = {};"));
|
|
testSame(options, "");
|
|
}
|
|
|
|
public void testLanguageMode() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.setLanguageIn(LanguageMode.ECMASCRIPT3);
|
|
|
|
String code = "var a = {get f(){}}";
|
|
|
|
Compiler compiler = compile(options, code);
|
|
checkUnexpectedErrorsOrWarnings(compiler, 1);
|
|
assertEquals(
|
|
"JSC_PARSE_ERROR. Parse error. " +
|
|
"getters are not supported in older versions of JS. " +
|
|
"If you are targeting newer versions of JS, " +
|
|
"set the appropriate language_in option. " +
|
|
"at i0 line 1 : 0",
|
|
compiler.getErrors()[0].toString());
|
|
|
|
options.setLanguageIn(LanguageMode.ECMASCRIPT5);
|
|
|
|
testSame(options, code);
|
|
|
|
options.setLanguageIn(LanguageMode.ECMASCRIPT5_STRICT);
|
|
|
|
testSame(options, code);
|
|
}
|
|
|
|
public void testLanguageMode2() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.setLanguageIn(LanguageMode.ECMASCRIPT3);
|
|
options.setWarningLevel(DiagnosticGroups.ES5_STRICT, CheckLevel.OFF);
|
|
|
|
String code = "var a = 2; delete a;";
|
|
|
|
testSame(options, code);
|
|
|
|
options.setLanguageIn(LanguageMode.ECMASCRIPT5);
|
|
|
|
testSame(options, code);
|
|
|
|
options.setLanguageIn(LanguageMode.ECMASCRIPT5_STRICT);
|
|
|
|
test(options,
|
|
code,
|
|
code,
|
|
StrictModeCheck.DELETE_VARIABLE);
|
|
}
|
|
|
|
public void testIssue598() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.setLanguageIn(LanguageMode.ECMASCRIPT5_STRICT);
|
|
WarningLevel.VERBOSE.setOptionsForWarningLevel(options);
|
|
|
|
options.setLanguageIn(LanguageMode.ECMASCRIPT5);
|
|
|
|
String code =
|
|
"'use strict';\n" +
|
|
"function App() {}\n" +
|
|
"App.prototype = {\n" +
|
|
" get appData() { return this.appData_; },\n" +
|
|
" set appData(data) { this.appData_ = data; }\n" +
|
|
"};";
|
|
|
|
Compiler compiler = compile(options, code);
|
|
testSame(options, code);
|
|
}
|
|
|
|
public void testIssue701() {
|
|
// Check ASCII art in license comments.
|
|
String ascii = "/**\n" +
|
|
" * @preserve\n" +
|
|
" This\n" +
|
|
" is\n" +
|
|
" ASCII ART\n" +
|
|
"*/";
|
|
String result = "/*\n\n" +
|
|
" This\n" +
|
|
" is\n" +
|
|
" ASCII ART\n" +
|
|
"*/\n";
|
|
testSame(createCompilerOptions(), ascii);
|
|
assertEquals(result, lastCompiler.toSource());
|
|
}
|
|
|
|
public void testIssue724() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
CompilationLevel.ADVANCED_OPTIMIZATIONS
|
|
.setOptionsForCompilationLevel(options);
|
|
String code =
|
|
"isFunction = function(functionToCheck) {" +
|
|
" var getType = {};" +
|
|
" return functionToCheck && " +
|
|
" getType.toString.apply(functionToCheck) === " +
|
|
" '[object Function]';" +
|
|
"};";
|
|
String result =
|
|
"isFunction=function(a){var b={};" +
|
|
"return a&&\"[object Function]\"===b.b.a(a)}";
|
|
|
|
test(options, code, result);
|
|
}
|
|
|
|
public void testIssue730() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
CompilationLevel.ADVANCED_OPTIMIZATIONS
|
|
.setOptionsForCompilationLevel(options);
|
|
|
|
String code =
|
|
"/** @constructor */function A() {this.foo = 0; Object.seal(this);}\n" +
|
|
"/** @constructor */function B() {this.a = new A();}\n" +
|
|
"B.prototype.dostuff = function() {this.a.foo++;alert('hi');}\n" +
|
|
"new B().dostuff();\n";
|
|
|
|
test(options,
|
|
code,
|
|
"function a(){this.b=0;Object.seal(this)}" +
|
|
"(new function(){this.a=new a}).a.b++;" +
|
|
"alert(\"hi\")");
|
|
|
|
options.removeUnusedClassProperties = true;
|
|
|
|
// This is still a problem when removeUnusedClassProperties are enabled.
|
|
test(options,
|
|
code,
|
|
"function a(){Object.seal(this)}" +
|
|
"(new function(){this.a=new a}).a.b++;" +
|
|
"alert(\"hi\")");
|
|
}
|
|
|
|
public void testCoaleseVariables() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
|
|
options.foldConstants = false;
|
|
options.coalesceVariableNames = true;
|
|
|
|
String code =
|
|
"function f(a) {" +
|
|
" if (a) {" +
|
|
" return a;" +
|
|
" } else {" +
|
|
" var b = a;" +
|
|
" return b;" +
|
|
" }" +
|
|
" return a;" +
|
|
"}";
|
|
String expected =
|
|
"function f(a) {" +
|
|
" if (a) {" +
|
|
" return a;" +
|
|
" } else {" +
|
|
" a = a;" +
|
|
" return a;" +
|
|
" }" +
|
|
" return a;" +
|
|
"}";
|
|
|
|
test(options, code, expected);
|
|
|
|
options.foldConstants = true;
|
|
options.coalesceVariableNames = false;
|
|
|
|
code =
|
|
"function f(a) {" +
|
|
" if (a) {" +
|
|
" return a;" +
|
|
" } else {" +
|
|
" var b = a;" +
|
|
" return b;" +
|
|
" }" +
|
|
" return a;" +
|
|
"}";
|
|
expected =
|
|
"function f(a) {" +
|
|
" if (!a) {" +
|
|
" var b = a;" +
|
|
" return b;" +
|
|
" }" +
|
|
" return a;" +
|
|
"}";
|
|
|
|
test(options, code, expected);
|
|
|
|
options.foldConstants = true;
|
|
options.coalesceVariableNames = true;
|
|
|
|
expected =
|
|
"function f(a) {" +
|
|
" return a;" +
|
|
"}";
|
|
|
|
test(options, code, expected);
|
|
}
|
|
|
|
public void testLateStatementFusion() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.foldConstants = true;
|
|
test(options,
|
|
"while(a){a();if(b){b();b()}}",
|
|
"for(;a;)a(),b&&(b(),b())");
|
|
}
|
|
|
|
public void testLateConstantReordering() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.foldConstants = true;
|
|
test(options,
|
|
"if (x < 1 || x > 1 || 1 < x || 1 > x) { alert(x) }",
|
|
" (1 > x || 1 < x || 1 < x || 1 > x) && alert(x) ");
|
|
}
|
|
|
|
public void testsyntheticBlockOnDeadAssignments() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.deadAssignmentElimination = true;
|
|
options.removeUnusedVars = true;
|
|
options.syntheticBlockStartMarker = "START";
|
|
options.syntheticBlockEndMarker = "END";
|
|
test(options, "var x; x = 1; START(); x = 1;END();x()",
|
|
"var x; x = 1;{START();{x = 1}END()}x()");
|
|
}
|
|
|
|
public void testBug4152835() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.foldConstants = true;
|
|
options.syntheticBlockStartMarker = "START";
|
|
options.syntheticBlockEndMarker = "END";
|
|
test(options, "START();END()", "{START();{}END()}");
|
|
}
|
|
|
|
public void testBug5786871() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.ideMode = true;
|
|
test(options, "function () {}", RhinoErrorReporter.PARSE_ERROR);
|
|
}
|
|
|
|
public void testIssue378() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.inlineVariables = true;
|
|
options.flowSensitiveInlineVariables = true;
|
|
testSame(options, "function f(c) {var f = c; arguments[0] = this;" +
|
|
" f.apply(this, arguments); return this;}");
|
|
}
|
|
|
|
public void testIssue550() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
CompilationLevel.SIMPLE_OPTIMIZATIONS
|
|
.setOptionsForCompilationLevel(options);
|
|
options.foldConstants = true;
|
|
options.inlineVariables = true;
|
|
options.flowSensitiveInlineVariables = true;
|
|
test(options,
|
|
"function f(h) {\n" +
|
|
" var a = h;\n" +
|
|
" a = a + 'x';\n" +
|
|
" a = a + 'y';\n" +
|
|
" return a;\n" +
|
|
"}",
|
|
// This should eventually get inlined completely.
|
|
"function f(a) { a += 'x'; return a += 'y'; }");
|
|
}
|
|
|
|
public void testIssue284() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.smartNameRemoval = true;
|
|
test(options,
|
|
"var goog = {};" +
|
|
"goog.inherits = function(x, y) {};" +
|
|
"var ns = {};" +
|
|
"/** @constructor */" +
|
|
"ns.PageSelectionModel = function() {};" +
|
|
"/** @constructor */" +
|
|
"ns.PageSelectionModel.FooEvent = function() {};" +
|
|
"/** @constructor */" +
|
|
"ns.PageSelectionModel.SelectEvent = function() {};" +
|
|
"goog.inherits(ns.PageSelectionModel.ChangeEvent," +
|
|
" ns.PageSelectionModel.FooEvent);",
|
|
"");
|
|
}
|
|
|
|
public void testIssue772() throws Exception {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.closurePass = true;
|
|
options.checkTypes = true;
|
|
test(
|
|
options,
|
|
"/** @const */ var a = {};" +
|
|
"/** @const */ a.b = {};" +
|
|
"/** @const */ a.b.c = {};" +
|
|
"goog.scope(function() {" +
|
|
" var b = a.b;" +
|
|
" var c = b.c;" +
|
|
" /** @typedef {string} */" +
|
|
" c.MyType;" +
|
|
" /** @param {c.MyType} x The variable. */" +
|
|
" c.myFunc = function(x) {};" +
|
|
"});",
|
|
"/** @const */ var a = {};" +
|
|
"/** @const */ a.b = {};" +
|
|
"/** @const */ a.b.c = {};" +
|
|
"a.b.c.MyType;" +
|
|
"a.b.c.myFunc = function(x) {};");
|
|
}
|
|
|
|
public void testCodingConvention() {
|
|
Compiler compiler = new Compiler();
|
|
compiler.initOptions(new CompilerOptions());
|
|
assertEquals(
|
|
compiler.getCodingConvention().getClass().toString(),
|
|
ClosureCodingConvention.class.toString());
|
|
}
|
|
|
|
public void testJQueryStringSplitLoops() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.foldConstants = true;
|
|
test(options,
|
|
"var x=['1','2','3','4','5','6','7']",
|
|
"var x='1234567'.split('')");
|
|
|
|
options = createCompilerOptions();
|
|
options.foldConstants = true;
|
|
options.computeFunctionSideEffects = false;
|
|
options.removeUnusedVars = true;
|
|
|
|
// If we do splits too early, it would add a side-effect to x.
|
|
test(options,
|
|
"var x=['1','2','3','4','5','6','7']",
|
|
"");
|
|
|
|
}
|
|
|
|
public void testAlwaysRunSafetyCheck() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.checkSymbols = false;
|
|
options.customPasses = ArrayListMultimap.create();
|
|
options.customPasses.put(
|
|
CustomPassExecutionTime.BEFORE_OPTIMIZATIONS,
|
|
new CompilerPass() {
|
|
@Override public void process(Node externs, Node root) {
|
|
Node var = root.getLastChild().getFirstChild();
|
|
assertEquals(Token.VAR, var.getType());
|
|
var.detachFromParent();
|
|
}
|
|
});
|
|
try {
|
|
test(options,
|
|
"var x = 3; function f() { return x + z; }",
|
|
"function f() { return x + z; }");
|
|
fail("Expected run-time exception");
|
|
} catch (RuntimeException e) {
|
|
assertTrue(e.getMessage().indexOf("Unexpected variable x") != -1);
|
|
}
|
|
}
|
|
|
|
public void testSuppressEs5StrictWarning() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.setWarningLevel(DiagnosticGroups.ES5_STRICT, CheckLevel.WARNING);
|
|
test(options,
|
|
"/** @suppress{es5Strict} */\n" +
|
|
"function f() { var arguments; }",
|
|
"function f() {}");
|
|
}
|
|
|
|
public void testCheckProvidesWarning() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.setWarningLevel(DiagnosticGroups.CHECK_PROVIDES, CheckLevel.WARNING);
|
|
options.setCheckProvides(CheckLevel.WARNING);
|
|
test(options,
|
|
"/** @constructor */\n" +
|
|
"function f() { var arguments; }",
|
|
DiagnosticType.warning("JSC_MISSING_PROVIDE", "missing goog.provide(''{0}'')"));
|
|
}
|
|
|
|
public void testSuppressCheckProvidesWarning() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.setWarningLevel(DiagnosticGroups.CHECK_PROVIDES, CheckLevel.WARNING);
|
|
options.setCheckProvides(CheckLevel.WARNING);
|
|
testSame(options,
|
|
"/** @constructor\n" +
|
|
" * @suppress{checkProvides} */\n" +
|
|
"function f() {}");
|
|
}
|
|
|
|
public void testSuppressCastWarning() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.setWarningLevel(DiagnosticGroups.CHECK_TYPES, CheckLevel.WARNING);
|
|
|
|
normalizeResults = true;
|
|
|
|
test(options,
|
|
"function f() { var xyz = /** @type {string} */ (0); }",
|
|
DiagnosticType.warning(
|
|
"JSC_INVALID_CAST", "invalid cast"));
|
|
|
|
testSame(options,
|
|
"/** @suppress{cast} */\n" +
|
|
"function f() { var xyz = /** @type {string} */ (0); }");
|
|
}
|
|
|
|
public void testRenamePrefix() {
|
|
String code = "var x = {}; function f(y) {}";
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.renamePrefix = "G_";
|
|
options.variableRenaming = VariableRenamingPolicy.ALL;
|
|
test(options, code, "var G_={}; function G_a(a) {}");
|
|
}
|
|
|
|
public void testRenamePrefixNamespace() {
|
|
String code =
|
|
"var x = {}; x.FOO = 5; x.bar = 3;";
|
|
|
|
CompilerOptions options = createCompilerOptions();
|
|
testSame(options, code);
|
|
|
|
options.collapseProperties = true;
|
|
options.renamePrefixNamespace = "_";
|
|
test(options, code, "_.x$FOO = 5; _.x$bar = 3;");
|
|
}
|
|
|
|
public void testRenamePrefixNamespaceProtectSideEffects() {
|
|
String code = "var x = null; try { +x.FOO; } catch (e) {}";
|
|
|
|
CompilerOptions options = createCompilerOptions();
|
|
testSame(options, code);
|
|
|
|
CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(
|
|
options);
|
|
options.renamePrefixNamespace = "_";
|
|
test(options, code, "_.x = null; try { +_.x.FOO; } catch (e) {}");
|
|
}
|
|
|
|
public void testRenamePrefixNamespaceActivatesMoveFunctionDeclarations() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code = "var x = f; function f() { return 3; }";
|
|
testSame(options, code);
|
|
assertFalse(options.moveFunctionDeclarations);
|
|
options.renamePrefixNamespace = "_";
|
|
test(options, code, "_.f = function() { return 3; }; _.x = _.f;");
|
|
}
|
|
|
|
public void testBrokenNameSpace() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
String code = "var goog; goog.provide('i.am.on.a.Horse');" +
|
|
"i.am.on.a.Horse = function() {};" +
|
|
"i.am.on.a.Horse.prototype.x = function() {};" +
|
|
"i.am.on.a.Boat.prototype.y = function() {}";
|
|
options.closurePass = true;
|
|
options.collapseProperties = true;
|
|
options.smartNameRemoval = true;
|
|
test(options, code, "");
|
|
}
|
|
|
|
public void testNamelessParameter() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
CompilationLevel.ADVANCED_OPTIMIZATIONS
|
|
.setOptionsForCompilationLevel(options);
|
|
String code =
|
|
"var impl_0;" +
|
|
"$load($init());" +
|
|
"function $load(){" +
|
|
" window['f'] = impl_0;" +
|
|
"}" +
|
|
"function $init() {" +
|
|
" impl_0 = {};" +
|
|
"}";
|
|
String result =
|
|
"window.f = {};";
|
|
test(options, code, result);
|
|
}
|
|
|
|
public void testHiddenSideEffect() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
CompilationLevel.ADVANCED_OPTIMIZATIONS
|
|
.setOptionsForCompilationLevel(options);
|
|
options.setAliasExternals(true);
|
|
String code =
|
|
"window.offsetWidth;";
|
|
String result =
|
|
"window.offsetWidth;";
|
|
test(options, code, result);
|
|
}
|
|
|
|
public void testNegativeZero() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
CompilationLevel.ADVANCED_OPTIMIZATIONS
|
|
.setOptionsForCompilationLevel(options);
|
|
test(options,
|
|
"function bar(x) { return x; }\n" +
|
|
"function foo(x) { print(x / bar(0));\n" +
|
|
" print(x / bar(-0)); }\n" +
|
|
"foo(3);",
|
|
"print(3/0);print(3/-0);");
|
|
}
|
|
|
|
public void testSingletonGetter1() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
CompilationLevel.ADVANCED_OPTIMIZATIONS
|
|
.setOptionsForCompilationLevel(options);
|
|
options.setCodingConvention(new ClosureCodingConvention());
|
|
test(options,
|
|
"/** @const */\n" +
|
|
"var goog = goog || {};\n" +
|
|
"goog.addSingletonGetter = function(ctor) {\n" +
|
|
" ctor.getInstance = function() {\n" +
|
|
" return ctor.instance_ || (ctor.instance_ = new ctor());\n" +
|
|
" };\n" +
|
|
"};" +
|
|
"function Foo() {}\n" +
|
|
"goog.addSingletonGetter(Foo);" +
|
|
"Foo.prototype.bar = 1;" +
|
|
"function Bar() {}\n" +
|
|
"goog.addSingletonGetter(Bar);" +
|
|
"Bar.prototype.bar = 1;",
|
|
"");
|
|
}
|
|
|
|
public void testIncompleteFunction1() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.ideMode = true;
|
|
DiagnosticType[] warnings = new DiagnosticType[]{
|
|
RhinoErrorReporter.PARSE_ERROR,
|
|
RhinoErrorReporter.PARSE_ERROR};
|
|
test(options,
|
|
new String[] { "var foo = {bar: function(e) }" },
|
|
new String[] { "var foo = {bar: function(e){}};" },
|
|
warnings
|
|
);
|
|
}
|
|
|
|
public void testIncompleteFunction2() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.ideMode = true;
|
|
DiagnosticType[] warnings = new DiagnosticType[]{
|
|
RhinoErrorReporter.PARSE_ERROR,
|
|
RhinoErrorReporter.PARSE_ERROR,
|
|
RhinoErrorReporter.PARSE_ERROR,
|
|
RhinoErrorReporter.PARSE_ERROR,
|
|
RhinoErrorReporter.PARSE_ERROR,
|
|
RhinoErrorReporter.PARSE_ERROR};
|
|
test(options,
|
|
new String[] { "function hi" },
|
|
new String[] { "function hi() {}" },
|
|
warnings
|
|
);
|
|
}
|
|
|
|
public void testSortingOff() {
|
|
CompilerOptions options = new CompilerOptions();
|
|
options.closurePass = true;
|
|
options.setCodingConvention(new ClosureCodingConvention());
|
|
test(options,
|
|
new String[] {
|
|
"goog.require('goog.beer');",
|
|
"goog.provide('goog.beer');"
|
|
},
|
|
ProcessClosurePrimitives.LATE_PROVIDE_ERROR);
|
|
}
|
|
|
|
public void testUnboundedArrayLiteralInfiniteLoop() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.ideMode = true;
|
|
test(options,
|
|
"var x = [1, 2",
|
|
"var x = [1, 2]",
|
|
RhinoErrorReporter.PARSE_ERROR);
|
|
}
|
|
|
|
public void testProvideRequireSameFile() throws Exception {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.setDependencyOptions(
|
|
new DependencyOptions()
|
|
.setDependencySorting(true));
|
|
options.closurePass = true;
|
|
test(
|
|
options,
|
|
"goog.provide('x');\ngoog.require('x');",
|
|
"var x = {};");
|
|
}
|
|
|
|
public void testDependencySorting() throws Exception {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.setDependencyOptions(
|
|
new DependencyOptions()
|
|
.setDependencySorting(true));
|
|
test(
|
|
options,
|
|
new String[] {
|
|
"goog.require('x');",
|
|
"goog.provide('x');",
|
|
},
|
|
new String[] {
|
|
"goog.provide('x');",
|
|
"goog.require('x');",
|
|
|
|
// For complicated reasons involving modules,
|
|
// the compiler creates a synthetic source file.
|
|
"",
|
|
});
|
|
}
|
|
|
|
public void testStrictWarningsGuard() throws Exception {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.checkTypes = true;
|
|
options.addWarningsGuard(new StrictWarningsGuard());
|
|
|
|
Compiler compiler = compile(options,
|
|
"/** @return {number} */ function f() { return true; }");
|
|
assertEquals(1, compiler.getErrors().length);
|
|
assertEquals(0, compiler.getWarnings().length);
|
|
}
|
|
|
|
public void testStrictWarningsGuardEmergencyMode() throws Exception {
|
|
CompilerOptions options = createCompilerOptions();
|
|
options.checkTypes = true;
|
|
options.addWarningsGuard(new StrictWarningsGuard());
|
|
options.useEmergencyFailSafe();
|
|
|
|
Compiler compiler = compile(options,
|
|
"/** @return {number} */ function f() { return true; }");
|
|
assertEquals(0, compiler.getErrors().length);
|
|
assertEquals(1, compiler.getWarnings().length);
|
|
}
|
|
|
|
public void testInlineProperties() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
CompilationLevel level = CompilationLevel.ADVANCED_OPTIMIZATIONS;
|
|
level.setOptionsForCompilationLevel(options);
|
|
level.setTypeBasedOptimizationOptions(options);
|
|
|
|
String code = "" +
|
|
"var ns = {};\n" +
|
|
"/** @constructor */\n" +
|
|
"ns.C = function () {this.someProperty = 1}\n" +
|
|
"alert(new ns.C().someProperty + new ns.C().someProperty);\n";
|
|
assertTrue(options.inlineProperties);
|
|
assertTrue(options.collapseProperties);
|
|
// CollapseProperties used to prevent inlining this property.
|
|
test(options, code, "alert(2);");
|
|
}
|
|
|
|
public void testGoogDefineClass1() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
CompilationLevel level = CompilationLevel.ADVANCED_OPTIMIZATIONS;
|
|
level.setOptionsForCompilationLevel(options);
|
|
level.setTypeBasedOptimizationOptions(options);
|
|
|
|
String code = "" +
|
|
"var ns = {};\n" +
|
|
"ns.C = goog.defineClass(null, {\n" +
|
|
" /** @constructor */\n" +
|
|
" constructor: function () {this.someProperty = 1}\n" +
|
|
"});\n" +
|
|
"alert(new ns.C().someProperty + new ns.C().someProperty);\n";
|
|
assertTrue(options.inlineProperties);
|
|
assertTrue(options.collapseProperties);
|
|
// CollapseProperties used to prevent inlining this property.
|
|
test(options, code, "alert(2);");
|
|
}
|
|
|
|
public void testGoogDefineClass2() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
CompilationLevel level = CompilationLevel.ADVANCED_OPTIMIZATIONS;
|
|
level.setOptionsForCompilationLevel(options);
|
|
level.setTypeBasedOptimizationOptions(options);
|
|
|
|
String code = "" +
|
|
"var C = goog.defineClass(null, {\n" +
|
|
" /** @constructor */\n" +
|
|
" constructor: function () {this.someProperty = 1}\n" +
|
|
"});\n" +
|
|
"alert(new C().someProperty + new C().someProperty);\n";
|
|
assertTrue(options.inlineProperties);
|
|
assertTrue(options.collapseProperties);
|
|
// CollapseProperties used to prevent inlining this property.
|
|
test(options, code, "alert(2);");
|
|
}
|
|
|
|
public void testGoogDefineClass3() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
CompilationLevel level = CompilationLevel.ADVANCED_OPTIMIZATIONS;
|
|
level.setOptionsForCompilationLevel(options);
|
|
level.setTypeBasedOptimizationOptions(options);
|
|
WarningLevel warnings = WarningLevel.VERBOSE;
|
|
warnings.setOptionsForWarningLevel(options);
|
|
|
|
String code = "" +
|
|
"var C = goog.defineClass(null, {\n" +
|
|
" /** @constructor */\n" +
|
|
" constructor: function () {\n" +
|
|
" /** @type {number} */\n" +
|
|
" this.someProperty = 1},\n" +
|
|
" /** @param {string} a */\n" +
|
|
" someMethod: function (a) {}\n" +
|
|
"});" +
|
|
"var x = new C();\n" +
|
|
"x.someMethod(x.someProperty);\n";
|
|
assertTrue(options.inlineProperties);
|
|
assertTrue(options.collapseProperties);
|
|
// CollapseProperties used to prevent inlining this property.
|
|
test(options, code, TypeValidator.TYPE_MISMATCH_WARNING);
|
|
}
|
|
|
|
public void testCheckConstants1() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
CompilationLevel level = CompilationLevel.SIMPLE_OPTIMIZATIONS;
|
|
level.setOptionsForCompilationLevel(options);
|
|
WarningLevel warnings = WarningLevel.QUIET;
|
|
warnings.setOptionsForWarningLevel(options);
|
|
|
|
String code = "" +
|
|
"var foo; foo();\n" +
|
|
"/** @const */\n" +
|
|
"var x = 1; foo(); x = 2;\n";
|
|
test(options, code, code);
|
|
}
|
|
|
|
public void testCheckConstants2() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
CompilationLevel level = CompilationLevel.SIMPLE_OPTIMIZATIONS;
|
|
level.setOptionsForCompilationLevel(options);
|
|
WarningLevel warnings = WarningLevel.DEFAULT;
|
|
warnings.setOptionsForWarningLevel(options);
|
|
|
|
String code = "" +
|
|
"var foo;\n" +
|
|
"/** @const */\n" +
|
|
"var x = 1; foo(); x = 2;\n";
|
|
test(options, code, ConstCheck.CONST_REASSIGNED_VALUE_ERROR);
|
|
}
|
|
|
|
public void testIssue787() {
|
|
CompilerOptions options = createCompilerOptions();
|
|
CompilationLevel level = CompilationLevel.SIMPLE_OPTIMIZATIONS;
|
|
level.setOptionsForCompilationLevel(options);
|
|
WarningLevel warnings = WarningLevel.DEFAULT;
|
|
warnings.setOptionsForWarningLevel(options);
|
|
|
|
String code = "" +
|
|
"function some_function() {\n" +
|
|
" var fn1;\n" +
|
|
" var fn2;\n" +
|
|
"\n" +
|
|
" if (any_expression) {\n" +
|
|
" fn2 = external_ref;\n" +
|
|
" fn1 = function (content) {\n" +
|
|
" return fn2();\n" +
|
|
" }\n" +
|
|
" }\n" +
|
|
"\n" +
|
|
" return {\n" +
|
|
" method1: function () {\n" +
|
|
" if (fn1) fn1();\n" +
|
|
" return true;\n" +
|
|
" },\n" +
|
|
" method2: function () {\n" +
|
|
" return false;\n" +
|
|
" }\n" +
|
|
" }\n" +
|
|
"}";
|
|
|
|
String result = "" +
|
|
"function some_function() {\n" +
|
|
" var a, b;\n" +
|
|
" any_expression && (b = external_ref, a = function(a) {\n" +
|
|
" return b()\n" +
|
|
" });\n" +
|
|
" return{method1:function() {\n" +
|
|
" a && a();\n" +
|
|
" return !0\n" +
|
|
" }, method2:function() {\n" +
|
|
" return !1\n" +
|
|
" }}\n" +
|
|
"}\n" +
|
|
"";
|
|
|
|
test(options, code, result);
|
|
}
|
|
|
|
public void testManyAdds() {}
|
|
// Defects4J: flaky method
|
|
// public void testManyAdds() {
|
|
// CompilerOptions options = createCompilerOptions();
|
|
// CompilationLevel level = CompilationLevel.SIMPLE_OPTIMIZATIONS;
|
|
// level.setOptionsForCompilationLevel(options);
|
|
// WarningLevel warnings = WarningLevel.VERBOSE;
|
|
// warnings.setOptionsForWarningLevel(options);
|
|
//
|
|
// int numAdds = 4750;
|
|
// StringBuilder original = new StringBuilder("var x = 0");
|
|
// for (int i = 0; i < numAdds; i++) {
|
|
// original.append(" + 1");
|
|
// }
|
|
// original.append(";");
|
|
// test(options, original.toString(), "var x = " + numAdds + ";");
|
|
// }
|
|
|
|
/** Creates a CompilerOptions object with google coding conventions. */
|
|
@Override
|
|
protected CompilerOptions createCompilerOptions() {
|
|
CompilerOptions options = new CompilerOptions();
|
|
options.setCodingConvention(new GoogleCodingConvention());
|
|
return options;
|
|
}
|
|
}
|