This repository has been archived on 2023-06-18. You can view files and clone it, but cannot push or open issues or pull requests.
ima02/resources/defects4j-checkout-closure-1f/test/com/google/javascript/jscomp/IntegrationTest.java

2400 lines
77 KiB
Java
Raw Permalink Normal View History

2023-04-25 11:33:41 +00:00
/*
* 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;
}
}