/*
* 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 static com.google.common.truth.Truth.assertThat;
import static com.google.javascript.jscomp.TypeValidator.TYPE_MISMATCH_WARNING;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.primitives.Chars;
import com.google.javascript.jscomp.CompilerOptions.DisposalCheckingPolicy;
import com.google.javascript.jscomp.CompilerOptions.J2clPassMode;
import com.google.javascript.jscomp.CompilerOptions.LanguageMode;
import com.google.javascript.jscomp.CompilerOptions.Reach;
import com.google.javascript.jscomp.deps.ModuleLoader;
import com.google.javascript.jscomp.deps.ModuleLoader.ResolutionMode;
import com.google.javascript.jscomp.testing.NodeSubject;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
/**
* Integration tests for the compiler.
*
* @author nicksantos@google.com (Nick Santos)
*/
public final 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.setCheckTypes(true);
test(
options,
"/** @return {function()} */ var AsyncTestCase = function() {};\n"
+ "/**\n"
+ " * @constructor\n"
+ " */ Foo = /** @type {function(new:Foo)} */ (AyncTestCase());",
RhinoErrorReporter.PARSE_ERROR);
}
// b/27531865
public void testLetInSwitch() {
CompilerOptions options = createCompilerOptions();
options.setLanguageIn(LanguageMode.ECMASCRIPT_2015);
options.setLanguageOut(LanguageMode.ECMASCRIPT3);
options.setWarningLevel(DiagnosticGroups.CHECK_VARIABLES, CheckLevel.ERROR);
String before = LINE_JOINER.join(
"var a = 0;",
"switch (a) {",
" case 0:",
" let x = 1;",
" case 1:",
" x = 2;",
"}");
String after = LINE_JOINER.join(
"var a = 0;",
"switch (a) {",
" case 0:",
" var x = 1;",
" case 1:",
" x = 2;",
"}");
test(options, before, after);
before = LINE_JOINER.join(
"var a = 0;",
"switch (a) {",
" case 0:",
" default:",
" let x = 1;",
"}");
after = LINE_JOINER.join(
"var a = 0;",
"switch (a) {",
" case 0:",
" default:",
" var x = 1;",
"}");
test(options, before, after);
}
public void testExplicitBlocksInSwitch() {
CompilerOptions options = createCompilerOptions();
options.setLanguageIn(LanguageMode.ECMASCRIPT_2015);
options.setLanguageOut(LanguageMode.ECMASCRIPT3);
options.setWarningLevel(DiagnosticGroups.CHECK_VARIABLES, CheckLevel.ERROR);
String before = LINE_JOINER.join(
"var a = 0;",
"switch (a) {",
" case 0:",
" { const x = 3; break; }",
" case 1:",
" { const x = 5; break; }",
"}");
String after = LINE_JOINER.join(
"var a = 0;",
"switch (a) {",
" case 0:",
" { var x = 3; break; }",
" case 1:",
" { var x$0 = 5; break; }",
"}");
test(options, before, after);
}
public void testMultipleAliasesInlined_bug31437418() {
CompilerOptions options = createCompilerOptions();
options.setCollapseProperties(true);
options.setLanguageIn(LanguageMode.ECMASCRIPT_2015);
options.setLanguageOut(LanguageMode.ECMASCRIPT3);
test(
options,
LINE_JOINER.join(
"class A { static z() {} }",
"const B = {};",
" B.A = A;",
" const C = {};",
" C.A = B.A; ",
"const D = {};",
" D.A = C.A;",
" D.A.z();"),
LINE_JOINER.join(
"var A = function(){};",
"var A$z = function(){};",
"var B$A = null;",
"var C$A = null;",
"var D$A = null;",
"A$z();"));
}
public void testBug1949424() {
CompilerOptions options = createCompilerOptions();
options.setCollapseProperties(true);
options.setClosurePass(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.setCollapseProperties(true);
options.setClosurePass(true);
test(
options,
LINE_JOINER.join(
CLOSURE_BOILERPLATE,
"goog.provide('FOO.BAR');",
"FOO.BAR = 3;"),
LINE_JOINER.join(
CLOSURE_COMPILED,
"var FOO$BAR = 3;"));
}
public void testUnresolvedDefine() {
CompilerOptions options = new CompilerOptions();
options.setClosurePass(true);
options.setCheckTypes(true);
DiagnosticType[] warnings = {
ProcessDefines.INVALID_DEFINE_TYPE_ERROR, RhinoErrorReporter.TYPE_PARSE_ERROR};
String[] input = { "var goog = {};" +
"goog.provide('foo.bar');" +
"/** @define{foo.bar} */ foo.bar = {};" };
String[] output = { "var goog = {};" +
"var foo = {};" +
"/** @define{foo.bar} */ foo.bar = {};"};
test(options, input, output, warnings);
}
public void testBug1956277() {
CompilerOptions options = createCompilerOptions();
options.setCollapseProperties(true);
options.setInlineVariables(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.setCollapseProperties(true);
options.setInlineVariables(true);
options.setGenerateExports(true);
test(
options,
CLOSURE_BOILERPLATE + "/** @export */ goog.CONSTANT = 1;"
+ "var x = goog.CONSTANT;",
"(function() {})('goog.CONSTANT', 1);");
}
/**
* Tests that calls to goog.string.Const.from() with non-constant arguments
* are detected with and without collapsed properties.
*/
public void testBug22684459() {
String source =
""
+ "var goog = {};"
+ "goog.string = {};"
+ "goog.string.Const = {};"
+ "goog.string.Const.from = function(x) {};"
+ "var x = window.document.location;"
+ "goog.string.Const.from(x);";
// Without collapsed properties.
CompilerOptions options = createCompilerOptions();
test(options, source, ConstParamCheck.CONST_NOT_ASSIGNED_STRING_LITERAL_ERROR);
// With collapsed properties.
options.setCollapseProperties(true);
test(options, source, ConstParamCheck.CONST_NOT_ASSIGNED_STRING_LITERAL_ERROR);
}
/**
* Tests that calls to goog.string.Const.from() with non-constant arguments
* are detected with and without collapsed properties, even when
* goog.string.Const.from has been aliased.
*/
public void testBug22684459_aliased() {
String source =
""
+ "var goog = {};"
+ "goog.string = {};"
+ "goog.string.Const = {};"
+ "goog.string.Const.from = function(x) {};"
+ "var mkConst = goog.string.Const.from;"
+ "var x = window.document.location;"
+ "mkConst(x);";
// Without collapsed properties.
CompilerOptions options = createCompilerOptions();
test(options, source, ConstParamCheck.CONST_NOT_ASSIGNED_STRING_LITERAL_ERROR);
// With collapsed properties.
options.setCollapseProperties(true);
test(options, source, ConstParamCheck.CONST_NOT_ASSIGNED_STRING_LITERAL_ERROR);
}
public void testBug31301233() {
String source = LINE_JOINER.join(
"function Foo() {",
" var x = window.document.location;",
" goog.string.Const.from(x);",
"};");
CompilerOptions options = createCompilerOptions();
options.setSmartNameRemoval(true);
options.setExtraSmartNameRemoval(true);
test(options, source, ConstParamCheck.CONST_NOT_ASSIGNED_STRING_LITERAL_ERROR);
}
public void testAdvancedModeIncludesExtraSmartNameRemoval() {
CompilerOptions options = new CompilerOptions();
CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
test(
options,
LINE_JOINER.join(
"(function() {",
" /** @constructor} */",
" function Bar() {}",
" var y = Bar;",
" new y();",
"})();"),
"");
}
public void testBug2410122() {
CompilerOptions options = createCompilerOptions();
options.setGenerateExports(true);
options.setClosurePass(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 testBug18078936() {
CompilerOptions options = createCompilerOptions();
options.setClosurePass(true);
WarningLevel.VERBOSE.setOptionsForWarningLevel(options);
test(options,
"var goog = {};" +
"goog.inherits = function(a,b) {};" +
"goog.defineClass = function(a,b) {};" +
"/** @template T */\n" +
"var ClassA = goog.defineClass(null, {\n" +
" constructor: function() {},\n" +
"" +
" /** @param {T} x */\n" +
" fn: function(x) {}\n" +
"});\n" +
"" +
"/** @extends {ClassA.<string>} */\n" +
"var ClassB = goog.defineClass(ClassA, {\n" +
" constructor: function() {},\n" +
"" +
" /** @override */\n" +
" fn: function(x) {}\n" +
"});\n" +
"" +
"(new ClassB).fn(3);\n" +
"",
TypeValidator.TYPE_MISMATCH_WARNING);
}
// http://b/31448683
public void testBug31448683() {
CompilerOptions options = createCompilerOptions();
WarningLevel.QUIET.setOptionsForWarningLevel(options);
options.setInlineFunctions(true);
test(
options,
LINE_JOINER.join(
"function f() {",
" x = x || 1",
" var x;",
" console.log(x);",
"}",
"for (var _ in [1]) {",
" f();",
"}"),
LINE_JOINER.join(
"for(var _ in[1]) {",
" {",
" var x$jscomp$inline_0 = void 0;",
" x$jscomp$inline_0 = x$jscomp$inline_0 || 1;",
" console.log(x$jscomp$inline_0);",
" }",
"}"));
}
public void testBug32660578() {
testSame(createCompilerOptions(), "function f() { alert(x); for (var x in []) {} }");
}
public void testWindowIsTypedEs6() {
CompilerOptions options = createCompilerOptions();
CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
options.setLanguageIn(LanguageMode.ECMASCRIPT_2015);
options.setLanguageOut(LanguageMode.ECMASCRIPT5);
options.setCheckTypes(true);
options.setWarningLevel(DiagnosticGroups.MISSING_PROPERTIES, CheckLevel.OFF);
test(
options,
LINE_JOINER.join(
"for (var x of []);", // Force injection of es6_runtime.js
"var /** number */ y = window;"),
TypeValidator.TYPE_MISMATCH_WARNING);
}
public void testConstPolymerNotAllowed() {
CompilerOptions options = createCompilerOptions();
options.setPolymerVersion(1);
options.setLanguageIn(LanguageMode.ECMASCRIPT_2015);
options.setLanguageOut(LanguageMode.ECMASCRIPT5);
externs = ImmutableList.of(SourceFile.fromCode("<externs>",
"var Polymer = function() {}; var PolymerElement = function() {};"));
test(
options,
"const Foo = Polymer({ is: 'x-foo' });",
PolymerPassErrors.POLYMER_INVALID_DECLARATION);
}
public void testForwardDeclaredTypeInTemplate() {
CompilerOptions options = createCompilerOptions();
WarningLevel.VERBOSE.setOptionsForWarningLevel(options);
options.setClosurePass(true);
options.setWarningLevel(DiagnosticGroups.LINT_CHECKS, CheckLevel.WARNING);
test(
options,
LINE_JOINER.join(
"var goog = {};",
"goog.forwardDeclare = function(/** string */ typeName) {};",
"goog.forwardDeclare('fwd.declared.Type');",
"",
"/** @type {!fwd.declared.Type<string>} */",
"var x;",
"",
"/** @type {!fwd.declared.Type<string, number>} */",
"var y;"),
"var goog={};goog.forwardDeclare=function(typeName){};var x;var y");
}
public void testIssue90() {
CompilerOptions options = createCompilerOptions();
options.setFoldConstants(true);
options.setInlineVariables(true);
options.setRemoveDeadCode(true);
test(options, "var x; x && alert(1);", "");
}
public void testClosurePassOff() {
CompilerOptions options = createCompilerOptions();
options.setClosurePass(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.setClosurePass(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.setClosurePass(true);
options.setCheckMissingGetCssNameLevel(CheckLevel.ERROR);
options.setCheckMissingGetCssNameBlacklist("foo");
test(options, "var x = 'foo';", CheckMissingGetCssName.MISSING_GETCSSNAME);
}
public void testCheckEventfulDisposalWarningLevels() {
CompilerOptions options = createCompilerOptions();
options.setCheckEventfulObjectDisposalPolicy(DisposalCheckingPolicy.ON);
String js = "var goog = {};" + "goog.inherits = function(x, y) {};"
+ "goog.dispose = function(x) {};"
+ "goog.disposeAll = function(var_args) {};"
+ "/** @return {*} */ goog.asserts.assert = function(x) { return x; };"
+ "goog.disposable = {};"
+ "/** @interface */\n"
+ "goog.disposable.IDisposable = function() {};"
+ "goog.disposable.IDisposable.prototype.dispose;"
+ "/** @implements {goog.disposable.IDisposable}\n * @constructor */\n"
+ "goog.Disposable = goog.abstractMethod;"
+ "/** @override */"
+ "goog.Disposable.prototype.dispose = goog.abstractMethod;"
+ "/** @param {goog.Disposable} fn */"
+ "goog.Disposable.prototype.registerDisposable = goog.abstractMethod;"
+ "goog.events = {};"
+ "/** @extends {goog.Disposable}\n * @constructor */"
+ "goog.events.EventHandler = function() {};"
+ "/** @extends {goog.Disposable}\n * @constructor */"
+ "var test = function() { this.eh = new goog.events.EventHandler(); };"
+ "goog.inherits(test, goog.Disposable);"
+ "var testObj = new test();";
test(options, js, CheckEventfulObjectDisposal.EVENTFUL_OBJECT_NOT_DISPOSED);
options.setWarningLevel(DiagnosticGroups.CHECK_EVENTFUL_OBJECT_DISPOSAL,
CheckLevel.OFF);
testSame(options, js);
}
public void testBug2592659() {
CompilerOptions options = createCompilerOptions();
options.setClosurePass(true);
options.setCheckTypes(true);
options.setCheckMissingGetCssNameLevel(CheckLevel.WARNING);
options.setCheckMissingGetCssNameBlacklist("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.setClosurePass(true);
test(
options,
LINE_JOINER.join(
"goog.provide('foo.Bar.Type');",
"goog.provide('foo.Bar');",
"/** @typedef {number} */ foo.Bar.Type;",
"foo.Bar = function() {};"),
LINE_JOINER.join(
"var foo = {};",
"foo.Bar.Type;",
"foo.Bar = function() {};"));
}
public void testTypedefBeforeOwner2() {
CompilerOptions options = createCompilerOptions();
options.setClosurePass(true);
options.setCollapseProperties(true);
test(
options,
LINE_JOINER.join(
"goog.provide('foo.Bar.Type');",
"goog.provide('foo.Bar');",
"/** @typedef {number} */ foo.Bar.Type;",
"foo.Bar = function() {};"),
LINE_JOINER.join(
"var foo$Bar$Type;",
"var foo$Bar = function() {};"));
}
public void testExportedNames() {
CompilerOptions options = createCompilerOptions();
options.setClosurePass(true);
options.setVariableRenaming(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.setCheckSuspiciousCode(true);
options.setCheckGlobalThisLevel(CheckLevel.ERROR);
test(options, "function f() { this.y = 3; }", CheckGlobalThis.GLOBAL_THIS);
}
public void testSusiciousCodeOff() {
CompilerOptions options = createCompilerOptions();
options.setCheckSuspiciousCode(false);
options.setCheckGlobalThisLevel(CheckLevel.ERROR);
test(options, "function f() { this.y = 3; }", CheckGlobalThis.GLOBAL_THIS);
}
public void testCheckGlobalThisOff() {
CompilerOptions options = createCompilerOptions();
options.setCheckSuspiciousCode(true);
options.setCheckGlobalThisLevel(CheckLevel.OFF);
testSame(options, "function f() { this.y = 3; }");
}
public void testCheckRequiresAndCheckProvidesOff() {
testSame(createCompilerOptions(), new String[] {
"/** @constructor */ function Foo() {}",
"new Foo();"
});
}
public void testCheckProvidesOn() {
CompilerOptions options = createCompilerOptions();
options.setWarningLevel(DiagnosticGroups.MISSING_PROVIDE, CheckLevel.ERROR);
test(
options,
new String[] {"goog.require('x'); /** @constructor */ function Foo() {}", "new Foo();"},
CheckProvides.MISSING_PROVIDE_WARNING);
}
public void testGenerateExportsOff() {
testSame(createCompilerOptions(), "/** @export */ function f() {}");
}
public void testExportTestFunctionsOn1() {
CompilerOptions options = createCompilerOptions();
options.exportTestFunctions = true;
test(options, "function testFoo() {}",
"/** @export */ function testFoo() {}"
+ "goog.exportSymbol('testFoo', testFoo);");
}
public void testExportTestFunctionsOn2() {
CompilerOptions options = createCompilerOptions();
options.setExportTestFunctions(true);
options.setClosurePass(true);
options.setRenamingPolicy(
VariableRenamingPolicy.ALL, PropertyRenamingPolicy.ALL_UNQUOTED);
options.setGeneratePseudoNames(true);
options.setCollapseProperties(true);
test(options,
new String[] {
LINE_JOINER.join(
"var goog = {};",
"goog.provide('goog.testing.testSuite');",
"goog.testing.testSuite = function(a) {};"),
LINE_JOINER.join(
"goog.module('testing');",
"var testSuite = goog.require('goog.testing.testSuite');",
"testSuite({testMethod:function(){}});")
},
new String[] {
"var $goog$testing$testSuite$$ = function($a$$) {};",
"$goog$testing$testSuite$$({'testMethod':function(){}})"
});
}
/**
* Check that valid ES6 modules compile to valid goog.require()/goog.provide() statements.
*/
public void testES6Modules() {
CompilerOptions options = createCompilerOptions();
options.setLanguageIn(LanguageMode.ECMASCRIPT_2015);
options.setLanguageOut(LanguageMode.ECMASCRIPT5);
options.setModuleResolutionMode(ResolutionMode.LEGACY);
test(
options,
new String[] {
"import {x} from './i1'; alert(x);", "export var x = 5;",
},
new String[] {
"goog.require('module$i1'); alert(module$i1.x);",
"goog.provide('module$i1'); var x$$module$i1 = 5; module$i1.x = x$$module$i1;",
});
}
/**
* Check that the expected warning is reported when an ES6 module tries to import a nonexistent
* module.
*/
public void testES6Modules_missing() {
CompilerOptions options = createCompilerOptions();
options.setLanguageIn(LanguageMode.ECMASCRIPT_2015);
options.setLanguageOut(LanguageMode.ECMASCRIPT5);
options.setModuleResolutionMode(ResolutionMode.LEGACY);
test(
options,
new String[] {
"import {x} from './i2'; alert(x);", "export var x = 5;",
},
ModuleLoader.LOAD_WARNING);
}
public void testAngularPassOff() {
testSame(createCompilerOptions(),
"/** @ngInject */ function f() {} " +
"/** @ngInject */ function g(a){} " +
"/** @ngInject */ var b = function f(a) {} ");
}
public void testAngularPassOn() {
CompilerOptions options = createCompilerOptions();
options.angularPass = true;
test(options,
"/** @ngInject */ function f() {} " +
"/** @ngInject */ function g(a){} " +
"/** @ngInject */ var b = function f(a, b, c) {} ",
"function f() {} " +
"function g(a) {} g['$inject']=['a'];" +
"var b = function f(a, b, c) {}; b['$inject']=['a', 'b', 'c']");
}
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,
new String[] {"var x = {eeny: 1, /** @expose */ meeny: 2};" +
"/** @constructor */ var Foo = function() {};" +
"/** @expose */ Foo.prototype.miny = 3;" +
"Foo.prototype.moe = 4;" +
"/** @expose */ Foo.prototype.tiger;" +
"function moe(a, b) { return a.meeny + b.miny + a.tiger; }" +
"window['x'] = x;" +
"window['Foo'] = Foo;" +
"window['moe'] = moe;"},
new String[] {"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+b.tiger" +
"}"},
new DiagnosticType[]{
CheckJSDoc.ANNOTATION_DEPRECATED,
CheckJSDoc.ANNOTATION_DEPRECATED,
CheckJSDoc.ANNOTATION_DEPRECATED});
}
public void testCheckSymbolsOff() {
CompilerOptions options = createCompilerOptions();
testSame(options, "x = 3;");
}
public void testCheckSymbolsOn() {
CompilerOptions options = createCompilerOptions();
options.setCheckSymbols(true);
test(options, "x = 3;", VarCheck.UNDEFINED_VAR_ERROR);
}
public void testNoTypeWarningForDupExternNamespace() {
CompilerOptions options = createCompilerOptions();
options.setCheckTypes(true);
externs = ImmutableList.of(SourceFile.fromCode(
"externs",
LINE_JOINER.join(
"/** @const */",
"var ns = {};",
"/** @type {number} */",
"ns.prop1;",
"/** @const */",
"var ns = {};",
"/** @type {number} */",
"ns.prop2;")));
testSame(options, "");
}
public void testCheckReferencesOff() {
CompilerOptions options = createCompilerOptions();
testSame(options, "x = 3; var x = 5;");
}
public void testCheckReferencesOn() {
CompilerOptions options = createCompilerOptions();
options.setCheckSymbols(true);
test(options, "x = 3; var x = 5;", VariableReferenceCheck.EARLY_REFERENCE);
}
public void testInferTypes() {
CompilerOptions options = createCompilerOptions();
options.inferTypes = true;
options.setCheckTypes(false);
options.setClosurePass(true);
test(options,
CLOSURE_BOILERPLATE + "goog.provide('Foo'); /** @enum */ Foo = {a: 3};",
"var COMPILED=true;var goog={};goog.exportSymbol=function(){};var Foo={a:3}");
assertThat(lastCompiler.getErrorManager().getTypedPercent()).isEqualTo(0.0);
// This does not generate a warning.
test(options, "/** @type {number} */ var n = window.name;",
"var n = window.name;");
assertThat(lastCompiler.getErrorManager().getTypedPercent()).isEqualTo(0.0);
}
public void testTypeCheckAndInference() {
CompilerOptions options = createCompilerOptions();
options.setCheckTypes(true);
test(
options, "/** @type {number} */ var n = window.name;", TypeValidator.TYPE_MISMATCH_WARNING);
assertThat(lastCompiler.getErrorManager().getTypedPercent()).isGreaterThan(0.0);
}
public void testTypeNameParser() {
CompilerOptions options = createCompilerOptions();
options.setCheckTypes(true);
test(options, "/** @type {n} */ var n = window.name;",
RhinoErrorReporter.UNRECOGNIZED_TYPE_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.setCheckTypes(true);
options.setAmbiguateProperties(true);
options.setPropertyRenaming(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.setCheckTypes(true);
test(options, "var x = x || {}; x.f = function() {}; x.f(3);", TypeCheck.WRONG_ARGUMENT_COUNT);
}
public void testBothTypeCheckersRunNoDupWarning() {
CompilerOptions options = createCompilerOptions();
options.setCheckTypes(true);
options.setNewTypeInference(true);
test(
options,
LINE_JOINER.join(
"/** @return {number} */",
"function f() {",
" return 'asdf';",
"}"),
NewTypeInference.RETURN_NONDECLARED_TYPE);
test(
options,
"/** @type {ASDF} */ var x;",
GlobalTypeInfo.UNRECOGNIZED_TYPE_NAME);
options.setWarningLevel(
DiagnosticGroups.REPORT_UNKNOWN_TYPES, CheckLevel.WARNING);
test(
options,
"function f(/** ? */ x) { var y = x; }",
NewTypeInference.UNKNOWN_EXPR_TYPE);
options.setWarningLevel(
DiagnosticGroups.REPORT_UNKNOWN_TYPES, CheckLevel.OFF);
testSame(
options,
LINE_JOINER.join(
"(function() {",
" /** @constructor */",
" var Boolean = function() {};",
"})();"));
}
public void testSilenceUnknownTypeWarningFromOTI() {
CompilerOptions options = new CompilerOptions();
options.setCheckTypes(true);
options.setNewTypeInference(true);
test(options,
LINE_JOINER.join(
"/**",
" * @param {T} x",
" * @template T",
" */",
"function f(x) {",
" function g(y) {",
" var w = /** @type {T} */ (y);",
" var /** T */ z = x;",
" };",
"}"),
LINE_JOINER.join(
"/**",
" * @param {T} x",
" * @template T",
" */",
"function f(x) {",
" function g(y) {",
" var w = y;",
" var /** T */ z = x;",
" };",
"}"));
}
public void testNTIConstWarningsOverrideAccessControls() {
CompilerOptions options = createCompilerOptions();
options.setCheckTypes(true);
String js =
LINE_JOINER.join(
"/** @constructor */ function Foo(name) {}",
"/** @const */ Foo.prop = 1;",
"Foo.prop = 2;");
test(options, js, CheckAccessControls.CONST_PROPERTY_REASSIGNED_VALUE);
options.setNewTypeInference(true);
test(options, js, NewTypeInference.CONST_PROPERTY_REASSIGNED);
}
public void testNTInoMaskTypeParseError() {
CompilerOptions options = createCompilerOptions();
options.setCheckTypes(true);
options.setNewTypeInference(true);
test(options, LINE_JOINER.join(
"/** @param {(boolean|number} x */",
"function f(x) {}"),
RhinoErrorReporter.TYPE_PARSE_ERROR);
}
public void testLegacyCompileOverridesStrict() {
CompilerOptions options = new CompilerOptions();
options.setCheckTypes(true);
options.addWarningsGuard(new StrictWarningsGuard());
options.setLegacyCodeCompile(true);
Compiler compiler = compile(options, "123();");
assertThat(compiler.getErrors()).isEmpty();
assertThat(compiler.getWarnings()).hasLength(1);
}
public void testLegacyCompileOverridesExplicitPromotionToError() {
CompilerOptions options = new CompilerOptions();
options.setCheckTypes(true);
options.addWarningsGuard(new DiagnosticGroupWarningsGuard(
DiagnosticGroups.CHECK_TYPES, CheckLevel.ERROR));
options.setLegacyCodeCompile(true);
Compiler compiler = compile(options, "123();");
assertThat(compiler.getErrors()).isEmpty();
assertThat(compiler.getWarnings()).hasLength(1);
}
public void testLegacyCompileTurnsOffDisambiguateProperties() {
CompilerOptions options = new CompilerOptions();
options.setCheckTypes(true);
options.setDisambiguateProperties(true);
options.setLegacyCodeCompile(true);
testSame(options,
LINE_JOINER.join(
"/** @constructor */",
"function Foo() {",
" this.p = 123;",
"}",
"/** @constructor */",
"function Bar() {",
" this.p = 234;",
"}"));
}
public void testReplaceCssNames() {
CompilerOptions options = createCompilerOptions();
options.setClosurePass(true);
options.setGatherCssNames(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", 1), lastCompiler.getPassConfig().getIntermediateState().cssNames);
}
public void testReplaceIdGeneratorsTest() {
CompilerOptions options = createCompilerOptions();
options.replaceIdGenerators = true;
options.setIdGenerators(ImmutableMap.<String, RenamingMap>of(
"xid", new RenamingMap() {
@Override
public String get(String value) {
return ":" + value + ":";
}
}));
test(options, "/** @idGenerator {mapped} */"
+ "var xid = function() {};\n"
+ "function f() {\n"
+ " return xid('foo');\n"
+ "}",
"var xid = function() {};\n"
+ "function f() {\n"
+ " return ':foo:';\n"
+ "}");
}
public void testRemoveClosureAsserts() {
CompilerOptions options = createCompilerOptions();
options.setClosurePass(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.setCheckTypes(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.setCheckTypes(true);
test(options, code, CheckAccessControls.BAD_PRIVATE_GLOBAL_ACCESS);
}
public void testUnreachableCode() {
String code = "function f() { return \n x(); }";
CompilerOptions options = createCompilerOptions();
options.setWarningLevel(DiagnosticGroups.CHECK_USELESS_CODE, CheckLevel.OFF);
testSame(options, code);
options.setWarningLevel(DiagnosticGroups.CHECK_USELESS_CODE, 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.setWarningLevel(DiagnosticGroups.MISSING_RETURN, CheckLevel.ERROR);
testSame(options, code);
options.setCheckTypes(true);
test(options, code, CheckMissingReturn.MISSING_RETURN_STATEMENT);
}
public void testIdGenerators() {
String code = "function f() {} f('id');";
CompilerOptions options = createCompilerOptions();
testSame(options, code);
options.setIdGenerators(ImmutableSet.of("f"));
test(options, code, "function f() {} 'a';");
}
public void testOptimizeArgumentsArray() {
String code = "function f() { return arguments[0]; }";
CompilerOptions options = createCompilerOptions();
testSame(options, code);
options.setOptimizeArgumentsArray(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.setClosurePass(true);
options.setCollapseProperties(true);
options.setRemoveAbstractMethods(true);
test(options, code, CLOSURE_COMPILED + " var x$bar = 3;");
}
public void testGoogDefine1() {
String code = CLOSURE_BOILERPLATE +
"/** @define {boolean} */ goog.define('FLAG', true);";
CompilerOptions options = createCompilerOptions();
options.setClosurePass(true);
options.setCollapseProperties(true);
options.setDefineToBooleanLiteral("FLAG", false);
test(options, code, CLOSURE_COMPILED + " var FLAG = false;");
}
public void testGoogDefine2() {
String code = CLOSURE_BOILERPLATE +
"goog.provide('ns');" +
"/** @define {boolean} */ goog.define('ns.FLAG', true);";
CompilerOptions options = createCompilerOptions();
options.setClosurePass(true);
options.setCollapseProperties(true);
options.setDefineToBooleanLiteral("ns.FLAG", false);
test(options, code, CLOSURE_COMPILED + "var ns$FLAG = false;");
}
public void testCollapseProperties1() {
String code =
"var x = {}; x.FOO = 5; x.bar = 3;";
CompilerOptions options = createCompilerOptions();
testSame(options, code);
options.setCollapseProperties(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.setCollapseProperties(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,
LINE_JOINER.join(
"function f() {",
" var JSCompiler_object_inline_FOO_0 = 5;",
" var JSCompiler_object_inline_bar_1 = 3;",
"}"));
}
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.setDisambiguateProperties(true);
options.setCheckTypes(true);
test(options, code,
"function Foo(){} Foo.prototype.Foo_prototype$bar = 3;"
+ "function Baz(){} Baz.prototype.Baz_prototype$bar = 3;");
}
// When closure-code-removal runs before disambiguate-properties, make sure
// that removing abstract methods doesn't mess up disambiguation.
public void testDisambiguateProperties2() {
CompilerOptions options = createCompilerOptions();
options.setClosurePass(true);
options.setCheckTypes(true);
options.setDisambiguateProperties(true);
options.setRemoveDeadCode(true);
options.setRemoveAbstractMethods(true);
test(options,
LINE_JOINER.join(
"/** @const */ var goog = {};",
"goog.abstractMethod = function() {};",
"/** @interface */ function I() {}",
"I.prototype.a = function(x) {};",
"/** @constructor @implements {I} */ function Foo() {}",
"/** @override */ Foo.prototype.a = goog.abstractMethod;",
"/** @constructor @extends Foo */ function Bar() {}",
"/** @override */ Bar.prototype.a = function(x) {};"),
LINE_JOINER.join(
"var goog={};",
"goog.abstractMethod = function() {};",
"function I(){}",
"I.prototype.a=function(x){};",
"function Foo(){}",
"function Bar(){}",
"Bar.prototype.a=function(x){};"));
}
public void testMarkPureCalls() {
String testCode = "function foo() {} foo();";
CompilerOptions options = createCompilerOptions();
options.setRemoveDeadCode(true);
testSame(options, testCode);
options.setComputeFunctionSideEffects(true);
test(options, testCode, "function foo() {}");
}
public void testMarkNoSideEffects() {
String testCode = "noSideEffects();";
CompilerOptions options = createCompilerOptions();
options.setRemoveDeadCode(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(ImmutableSet.of("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.setDevirtualizePrototypeMethods(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.setInlineConstantVars(true);
test(options, "var FOO = true; FOO = false", ConstCheck.CONST_REASSIGNED_VALUE_ERROR);
}
public void testAllChecksOn() {
CompilerOptions options = createCompilerOptions();
options.setCheckSuspiciousCode(true);
options.setWarningLevel(DiagnosticGroups.MISSING_PROVIDE, CheckLevel.ERROR);
options.setGenerateExports(true);
options.exportTestFunctions = true;
options.setClosurePass(true);
options.setCheckMissingGetCssNameLevel(CheckLevel.ERROR);
options.setCheckMissingGetCssNameBlacklist("goog");
options.syntheticBlockStartMarker = "synStart";
options.syntheticBlockEndMarker = "synEnd";
options.setCheckSymbols(true);
options.processObjectPropertyString = true;
options.setCollapseProperties(true);
test(options, CLOSURE_BOILERPLATE, CLOSURE_COMPILED);
}
public void testTypeCheckingWithSyntheticBlocks() {
CompilerOptions options = createCompilerOptions();
options.syntheticBlockStartMarker = "synStart";
options.syntheticBlockEndMarker = "synEnd";
options.setCheckTypes(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.setCheckSymbols(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.setVariableRenaming(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.setCheckTypes(true);
options.setClosurePass(true);
test(
options,
LINE_JOINER.join(
CLOSURE_BOILERPLATE,
"goog.provide('Foo');",
"/** @constructor */ Foo = function() {};",
"var x = new Foo();"),
LINE_JOINER.join(
"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};",
"var COMPILED=true;var goog={};goog.exportSymbol=function(){};var Foo={a:3}");
}
public void testProvidedNamespaceIsConst() {
CompilerOptions options = createCompilerOptions();
options.setClosurePass(true);
options.setInlineConstantVars(true);
options.setCollapseProperties(true);
test(
options,
LINE_JOINER.join(
"var goog = {};",
"goog.provide('foo');",
"function f() { foo = {};}"),
LINE_JOINER.join(
"var foo = {};",
"function f() { foo = {}; }"),
ConstCheck.CONST_REASSIGNED_VALUE_ERROR);
}
public void testProvidedNamespaceIsConst3() {
CompilerOptions options = createCompilerOptions();
options.setClosurePass(true);
options.setInlineConstantVars(true);
options.setCollapseProperties(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.setClosurePass(true);
options.setInlineConstantVars(true);
options.setCollapseProperties(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.setClosurePass(true);
options.setInlineConstantVars(true);
options.setCollapseProperties(true);
test(
options,
"var goog = {}; goog.provide('foo.Bar'); "
+ "foo = {}; foo.Bar = {};",
"var foo = {}; foo = {}; foo.Bar = {};");
}
public void testAtDefineReassigned() {
test(createCompilerOptions(),
"/** @define {boolean} */ var HI = true; HI = false;",
ConstCheck.CONST_REASSIGNED_VALUE_ERROR);
}
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.setMessageBundle(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.setCheckGlobalNamesLevel(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.setCheckTypes(true);
options.setAmbiguateProperties(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.setInlineVariables(true);
test(options, code, "(function foo() {})(3);");
}
public void testInlineConstants() {
CompilerOptions options = createCompilerOptions();
String code = "function foo() {} var x = 3; foo(x); var YYY = 4; foo(YYY);";
testSame(options, code);
options.setInlineConstantVars(true);
test(options, code, "function foo() {} foo(3); foo(4);");
}
public void testMinimizeExits() {
CompilerOptions options = createCompilerOptions();
String code =
"function f() {" +
" if (window.foo) return; window.h(); " +
"}";
testSame(options, code);
options.setFoldConstants(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.setFoldConstants(true);
test(options, code, "window.foo();");
}
public void testRemoveUnreachableCode() {
CompilerOptions options = createCompilerOptions();
options.setWarningLevel(DiagnosticGroups.CHECK_USELESS_CODE, CheckLevel.OFF);
String code = "function f() { return; f(); }";
testSame(options, code);
options.setRemoveDeadCode(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.setRemoveUnusedPrototypeProperties(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.setRemoveUnusedPrototypeProperties(true);
testSame(options, code);
options.setRemoveUnusedVariables(Reach.ALL);
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.setSmartNameRemoval(true);
test(options, code, "");
}
public void testClassWithGettersIsRemoved() {
CompilerOptions options = createCompilerOptions();
String code =
"class Foo { get xx() {}; set yy(v) {}; static get init() {}; static set prop(v) {} }";
// TODO(radokirov): The compiler should be removing statics too, but in this case they are
// kept. A similar unittest in RemoveUnusedClassPropertiesTest removes everything.
// Investigate why are they kept when ran together with other passes.
String expected =
LINE_JOINER.join(
"('undefined'!=typeof window&&window===this?this:'undefined'!=typeof ",
"global&&null!=global?global:this).",
"c.defineProperties(function() {},",
"{e:{a:!0,b:!0,d:function(){}},", // renamed from init
"f:{a:!0,b:!0,g:function(){}}})"); // renamed from prop
options.setLanguageIn(LanguageMode.ECMASCRIPT_2015);
options.setLanguageOut(LanguageMode.ECMASCRIPT5);
CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
options.setExtraSmartNameRemoval(false);
test(options, code, expected);
}
public void testExportOnClassGetter() {
CompilerOptions options = createCompilerOptions();
CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
options.setGenerateExports(true);
options.setExportLocalPropertyDefinitions(true);
options.setLanguageIn(LanguageMode.ECMASCRIPT_2015);
options.setLanguageOut(LanguageMode.ECMASCRIPT5);
test(options,
"class C { /** @export @return {string} */ get exportedName() {} }; (new C).exportedName;",
""
+ "function a(){}(\"undefined\"!=typeof window&&"
+ "window===this?this:\"undefined\"!=typeof global&&"
+ "null!=global?global:this).a.defineProperties("
+ "a.prototype,{exportedName:{b:!0,c:!0,get:function(){}}});(new a).exportedName");
}
public void testExportOnClassSetter() {
CompilerOptions options = createCompilerOptions();
CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
options.setGenerateExports(true);
options.setExportLocalPropertyDefinitions(true);
options.setLanguageIn(LanguageMode.ECMASCRIPT_2015);
options.setLanguageOut(LanguageMode.ECMASCRIPT5);
test(options,
"class C { /** @export @return {string} */ set exportedName(x) {} }; (new C).exportedName;",
""
+ "function a(){}(\"undefined\"!=typeof window&&"
+ "window===this?this:\"undefined\"!=typeof global&&"
+ "null!=global?global:this).a.defineProperties("
+ "a.prototype,{exportedName:{b:!0,c:!0,set:function(){}}});(new a).exportedName");
}
public void testSmartNamePassBug11163486() {
CompilerOptions options = createCompilerOptions();
options.setCheckTypes(true);
options.setDisambiguateProperties(true);
options.setRemoveDeadCode(true);
options.setRemoveUnusedPrototypeProperties(true);
options.setSmartNameRemoval(true);
options.extraSmartNameRemoval = true;
options.setWarningLevel(DiagnosticGroups.MISSING_PROPERTIES, CheckLevel.OFF);
String code = "/** @constructor */ function A() {} " +
"A.prototype.foo = function() { " +
" window.console.log('A'); " +
"}; " +
"/** @constructor */ function B() {} " +
"B.prototype.foo = function() { " +
" window.console.log('B'); " +
"};" +
"window['main'] = function() { " +
" var a = window['a'] = new A; " +
" a.foo(); " +
" window['b'] = new B; " +
"}; " +
"function notCalled() { " +
" var something = {}; " +
" something.foo(); " +
"}";
String expected = "function A() {} " +
"A.prototype.A_prototype$foo = function() { " +
" window.console.log('A'); " +
"}; " +
"function B() {} " +
"window['main'] = function() { " +
" var a = window['a'] = new A; " +
" a.A_prototype$foo(); " +
" window['b'] = new B; " +
"}";
test(options, code, expected);
}
public void testDeadCodeHasNoDisambiguationSideEffects() {
// This test case asserts that unreachable code does not
// confuse the disambigation process and type inferencing.
CompilerOptions options = createCompilerOptions();
options.setCheckTypes(true);
options.setDisambiguateProperties(true);
options.setRemoveDeadCode(true);
options.setRemoveUnusedPrototypeProperties(true);
options.setSmartNameRemoval(true);
options.extraSmartNameRemoval = true;
options.setFoldConstants(true);
options.setInlineVariables(true);
options.setWarningLevel(DiagnosticGroups.MISSING_PROPERTIES, CheckLevel.OFF);
String code =
"/** @constructor */ function A() {} "
+ "A.prototype.always = function() { "
+ " window.console.log('AA'); "
+ "}; "
+ "A.prototype.sometimes = function() { "
+ " window.console.log('SA'); "
+ "}; "
+ "/** @constructor */ function B() {} "
+ "B.prototype.always = function() { "
+ " window.console.log('AB'); "
+ "};"
+ "B.prototype.sometimes = function() { "
+ " window.console.log('SB'); "
+ "};"
+ "/** @constructor @struct @template T */ function C() {} "
+ "/** @param {!T} x */ C.prototype.touch = function(x) { "
+ " return x.sometimes(); "
+ "}; "
+ "window['main'] = function() { "
+ " var a = window['a'] = new A; "
+ " a.always(); "
+ " a.sometimes(); "
+ " var b = window['b'] = new B; "
+ " b.always(); "
+ "};"
+ "function notCalled() { "
+ " var something = {}; "
+ " something.always(); "
+ " var c = new C; "
+ " c.touch(something);"
+ "}";
// B.prototype.sometimes should be stripped out, as it is not used, and the
// type ambiguity in function notCalled is unreachable.
String expected = "function A() {} " +
"A.prototype.A_prototype$always = function() { " +
" window.console.log('AA'); " +
"}; " +
"A.prototype.A_prototype$sometimes = function(){ " +
" window.console.log('SA'); " +
"}; " +
"function B() {} " +
"B.prototype.B_prototype$always=function(){ " +
" window.console.log('AB'); " +
"};" +
"window['main'] = function() { " +
" var a = window['a'] = new A; " +
" a.A_prototype$always(); " +
" a.A_prototype$sometimes(); " +
" (window['b'] = new B).B_prototype$always(); " +
"}";
test(options, code, expected);
}
public void testQMarkTIsNullable() {
CompilerOptions options = createCompilerOptions();
options.setCheckTypes(true);
String code = LINE_JOINER.join(
"/** @constructor @template T */",
"function F() {}",
"",
"/** @return {?T} */",
"F.prototype.foo = function() {",
" return null;",
"}",
"",
"/** @type {F<string>} */",
"var f = new F;",
"/** @type {string} */",
"var s = f.foo(); // Type error: f.foo() has type {?string}.");
test(options, code, TYPE_MISMATCH_WARNING);
}
public void testTIsNotNullable() {
CompilerOptions options = createCompilerOptions();
options.setCheckTypes(true);
String code = LINE_JOINER.join("/** @constructor @template T */",
"function F() {}",
"",
"/** @param {T} t */",
"F.prototype.foo = function(t) {",
"}",
"",
"/** @type {F<string>} */",
"var f = new F;",
"/** @type {?string} */",
"var s = null;",
"f.foo(s); // Type error: f.foo() takes a {string}, not a {?string}");
test(options, code, TYPE_MISMATCH_WARNING);
}
public void testDeadAssignmentsElimination() {
CompilerOptions options = createCompilerOptions();
String code = "function f() { var x = 3; 4; x = 5; return x; } f(); ";
testSame(options, code);
options.setDeadAssignmentElimination(true);
testSame(options, code);
options.setRemoveUnusedVariables(Reach.ALL);
test(options, code, "function f() { 3; 4; var x = 5; return x; } f();");
}
public void testPreservesCastInformation() {
// Only set the suffix instead of both prefix and suffix, because J2CL pass
// looks for that exact suffix, and IntegrationTestCase adds an input
// id number between prefix and suffix.
inputFileNameSuffix = "vmbootstrap/Arrays.impl.java.js";
CompilerOptions options = new CompilerOptions();
String code = LINE_JOINER.join(
"/** @constructor */",
"var Arrays = function() {};",
"Arrays.$create = function() { return {}; }",
"/** @constructor */",
"function Foo() { this.myprop = 1; }",
"/** @constructor */",
"function Bar() { this.myprop = 2; }",
"var x = /** @type {!Foo} */ (Arrays.$create()).myprop;");
options.setCheckTypes(true);
options.setJ2clPass(J2clPassMode.TRUE);
options.setDisambiguateProperties(true);
test(options, code,
LINE_JOINER.join(
"/** @constructor */",
"var Arrays = function() {};",
"Arrays.$create = function() { return {}; }",
"/** @constructor */",
"function Foo() { this.Foo$myprop = 1; }",
"/** @constructor */",
"function Bar() { this.Bar$myprop = 2; }",
"var x = {}.Foo$myprop;"));
}
public void testInliningLocalVarsPreservesCasts() {
String code = LINE_JOINER.join(
"/** @constructor */",
"function Foo() { this.myprop = 1; }",
"/** @constructor */",
"function Bar() { this.myprop = 2; }",
"/** @return {Object} */",
"function getSomething() {",
" var x = new Bar();",
" return new Foo();",
"}",
"(function someMethod() {",
" var x = getSomething();",
" var y = /** @type {Foo} */ (x).myprop;",
" return 1 != y;",
"})()");
CompilerOptions options = new CompilerOptions();
options.setCheckTypes(true);
options.setSmartNameRemoval(true);
options.setFoldConstants(true);
options.setExtraSmartNameRemoval(true);
options.setInlineVariables(true);
options.setDisambiguateProperties(true);
// Verify that property disambiguation continues to work after the local
// var and cast have been removed by inlining.
test(options, code,
LINE_JOINER.join(
"/** @constructor */",
"function Foo() { this.Foo$myprop = 1; }",
"/** @constructor */",
"function Bar() { this.Bar$myprop = 2; }",
"/** @return {Object} */",
"function getSomething() {",
" var x = new Bar();",
" return new Foo();",
"}",
"(function someMethod() {",
" return 1 != getSomething().Foo$myprop;",
"})()"));
}
/**
* Tests that inlining of local variables doesn't destroy type information
* when the cast is from a non-nullable type to a nullable type.
*/
public void testInliningLocalVarsPreservesCastsNullable() {
String code = LINE_JOINER.join(
"/** @constructor */",
"function Foo() { this.myprop = 1; }",
"/** @constructor */",
"function Bar() { this.myprop = 2; }",
// Note that this method return a non-nullable type.
"/** @return {!Object} */",
"function getSomething() {",
" var x = new Bar();",
" return new Foo();",
"}",
"(function someMethod() {",
" var x = getSomething();",
// Note that this casts from !Object to ?Foo.
" var y = /** @type {Foo} */ (x).myprop;",
" return 1 != y;",
"})()");
CompilerOptions options = new CompilerOptions();
options.setCheckTypes(true);
options.setSmartNameRemoval(true);
options.setFoldConstants(true);
options.setExtraSmartNameRemoval(true);
options.setInlineVariables(true);
options.setDisambiguateProperties(true);
// Verify that property disambiguation continues to work after the local
// var and cast have been removed by inlining.
test(options, code,
LINE_JOINER.join(
"/** @constructor */",
"function Foo() { this.Foo$myprop = 1; }",
"/** @constructor */",
"function Bar() { this.Bar$myprop = 2; }",
"/** @return {Object} */",
"function getSomething() {",
" var x = new Bar();",
" return new Foo();",
"}",
"(function someMethod() {",
" return 1 != getSomething().Foo$myprop;",
"})()"));
}
public void testInlineFunctions() {
CompilerOptions options = createCompilerOptions();
String code = "function f() { return 3; } f(); ";
testSame(options, code);
options.setInlineFunctions(true);
test(options, code, "3;");
}
public void testRemoveUnusedVars1() {
CompilerOptions options = createCompilerOptions();
String code = "function f(x) {} f();";
testSame(options, code);
options.setRemoveUnusedVariables(Reach.ALL);
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.setRemoveUnusedVariables(Reach.ALL);
test(options, code, "(function() {})();var g = function() {}; g();");
options.setAnonymousFunctionNaming(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.setCrossModuleCodeMotion(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.setCrossModuleMethodMotion(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 testCrossModuleDepCheck() {
CompilerOptions options = createCompilerOptions();
String[] code = new String[] {
"var goog = {}; new goog.Foo();",
"/** @constructor */ goog.Foo = function() {};",
};
testSame(options, code);
WarningLevel.VERBOSE.setOptionsForWarningLevel(options);
test(options, code, code, CheckGlobalNames.STRICT_MODULE_DEP_QNAME);
}
public void testFlowSensitiveInlineVariables1() {
CompilerOptions options = createCompilerOptions();
String code = "function f() { var x = 3; x = 5; return x; }";
testSame(options, code);
options.setInlineVariables(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; })()");
options.setRemoveUnusedVariables(Reach.ALL);
test(options, unusedVar, "(function () { return 5; })()");
}
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" +
"}");
}
// Github issue #1540: https://github.com/google/closure-compiler/issues/1540
public void testFlowSensitiveInlineVariablesUnderAdvanced() {
CompilerOptions options = createCompilerOptions();
CompilationLevel level = CompilationLevel.ADVANCED_OPTIMIZATIONS;
level.setOptionsForCompilationLevel(options);
options.setCheckSymbols(false);
test(options,
LINE_JOINER.join(
"function f(x) {",
" var a = x + 1;",
" var b = x + 1;",
" window.c = x > 5 ? a : b;",
"}",
"f(g);"),
"window.a = g + 1;");
}
public void testCollapseAnonymousFunctions() {
CompilerOptions options = createCompilerOptions();
String code = "var f = function() {};";
testSame(options, code);
options.setCollapseAnonymousFunctions(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, "var f = function() { return 3; }; var x = f();");
}
public void testNameAnonymousFunctions() {
CompilerOptions options = createCompilerOptions();
String code = "var f = function() {};";
testSame(options, code);
options.setAnonymousFunctionNaming(AnonymousFunctionNamingPolicy.MAPPED);
test(options, code, "var f = function $() {}");
assertNotNull(lastCompiler.getResult().namedAnonFunctionMap);
options.setAnonymousFunctionNaming(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.setAnonymousFunctionNaming(AnonymousFunctionNamingPolicy.MAPPED);
test(options, code,
"var f = function longName() {}; var g = function $() {};"
+ "var i = function longerName(){};");
assertNotNull(lastCompiler.getResult().namedAnonFunctionMap);
options.setAnonymousFunctionNaming(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.setExtractPrototypeMemberDeclarations(true);
options.setVariableRenaming(VariableRenamingPolicy.ALL);
test(options, code, expected);
}
public void testDevirtualizationAndExtractPrototypeMemberDeclarations() {
CompilerOptions options = createCompilerOptions();
options.setDevirtualizePrototypeMethods(true);
options.setCollapseAnonymousFunctions(true);
options.setExtractPrototypeMemberDeclarations(true);
options.setVariableRenaming(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);
// skip i,j,o (reserved)
if (letter >= 'i') {
letter++;
}
if (letter >= 'j') {
letter++;
}
if (letter >= 'o') {
letter++;
}
expected += "a.argz = function() {arguments};";
expected += "function " + letter + "(c){}";
}
code += "var F = new f(); F.argz();";
expected += "var q = new b(); q.argz();";
for (int i = 0; i < 10; i++) {
code += "F.devir" + i + "();";
char letter = (char) ('d' + i);
// skip i,j,o (reserved)
if (letter >= 'i') {
letter++;
}
if (letter >= 'j') {
letter++;
}
if (letter >= 'o') {
letter++;
}
expected += letter + "(q);";
}
test(options, code, expected);
}
public void testPropertyRenaming() {
CompilerOptions options = createCompilerOptions();
String code =
"function f() { return this.foo + this['bar'] + this.Baz; }" +
"f.prototype.bar = 3; f.prototype.Baz = 3;";
String all =
"function f() { return this.c + this['bar'] + this.a; }"
+ "f.prototype.b = 3; f.prototype.a = 3;";
testSame(options, code);
options.setPropertyRenaming(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.setPropertyRenaming(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.setRewriteFunctionExpressions(true);
test(options, code, expected);
}
public void testAliasAllStrings() {
CompilerOptions options = createCompilerOptions();
String code =
"function f() {" + " return 'aaaaaaaaaaaaaaaaaaaa' + 'aaaaaaaaaaaaaaaaaaaa';" + "}";
String expected =
"var $$S_aaaaaaaaaaaaaaaaaaaa = 'aaaaaaaaaaaaaaaaaaaa';"
+ "function f() {"
+ " return $$S_aaaaaaaaaaaaaaaaaaaa + $$S_aaaaaaaaaaaaaaaaaaaa;"
+ "}";
testSame(options, code);
options.setAliasAllStrings(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.setVariableRenaming(VariableRenamingPolicy.LOCAL);
test(options, code, local);
options.setVariableRenaming(VariableRenamingPolicy.ALL);
test(options, code, all);
options.reserveRawExports = true;
}
public void testRenameVars2() {
CompilerOptions options = createCompilerOptions();
options.setVariableRenaming(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.setVariableRenaming(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.setIdeMode(true);
options.setCheckTypes(true);
options.setWarningLevel(DiagnosticGroups.CHECK_USELESS_CODE, CheckLevel.OFF);
test(options,
"function f() { try { } catch(e) { break; } }",
RhinoErrorReporter.PARSE_ERROR);
}
// https://github.com/google/closure-compiler/issues/2388
public void testNoCrash_varInCatch() {
CompilerOptions options = createCompilerOptions();
options.setInlineFunctions(true);
test(
options,
LINE_JOINER.join(
"(function() {",
" try {",
" x = 2;",
" } catch (e) {",
" var x = 1;",
" }",
"})();"),
LINE_JOINER.join(
"{",
" var x$jscomp$inline_0;",
" try {",
" x$jscomp$inline_0 = 2;",
" } catch (e) {",
" x$jscomp$inline_0 = 1;",
" }",
"}"));
}
// https://github.com/google/closure-compiler/issues/2364
public void testNoCrash_varInCatch2() {
CompilerOptions options = createCompilerOptions();
options.setWarningLevel(DiagnosticGroups.CHECK_USELESS_CODE, CheckLevel.OFF);
test(
options,
LINE_JOINER.join(
"function foo() {",
" var msg;",
"}",
"",
"function bar() {",
" msg;",
" try {}",
" catch(err) {",
" var msg;",
" }",
"}"),
LINE_JOINER.join(
"function foo() {",
" var msg;",
"}",
"function bar(){",
" var msg;",
" msg;",
" try{}",
" catch(err){}",
"}"));
}
public void testIssue63SourceMap() {
CompilerOptions options = createCompilerOptions();
String code = "var a;";
options.skipNonTranspilationPasses = true;
options.sourceMapOutputPath = "./src.map";
Compiler compiler = compile(options, code);
compiler.toSource();
}
public void testRegExp1() {
CompilerOptions options = createCompilerOptions();
options.setFoldConstants(true);
String code = "/(a)/.test(\"a\");";
testSame(options, code);
options.setComputeFunctionSideEffects(true);
String expected = "";
test(options, code, expected);
}
public void testRegExp2() {
CompilerOptions options = createCompilerOptions();
options.setFoldConstants(true);
String code = "/(a)/.test(\"a\");var a = RegExp.$1";
testSame(options, code);
options.setComputeFunctionSideEffects(true);
test(options, code, CheckRegExp.REGEXP_REFERENCE);
options.setWarningLevel(DiagnosticGroups.CHECK_REGEXP, CheckLevel.OFF);
testSame(options, code);
}
public void testFoldLocals1() {
CompilerOptions options = createCompilerOptions();
options.setFoldConstants(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.setComputeFunctionSideEffects(true);
test(options, code, "");
}
public void testFoldLocals2() {
CompilerOptions options = createCompilerOptions();
options.setFoldConstants(true);
options.setCheckTypes(true);
options.setWarningLevel(DiagnosticGroups.MISSING_PROPERTIES, CheckLevel.OFF);
// 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.setComputeFunctionSideEffects(true);
test(options, code, "widgetToken()");
}
public void testFoldLocals3() {
CompilerOptions options = createCompilerOptions();
options.setFoldConstants(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.setComputeFunctionSideEffects(true);
// BROKEN
//test(options, code, definition);
testSame(options, code);
}
public void testFoldLocals4() {
CompilerOptions options = createCompilerOptions();
options.setFoldConstants(true);
String code =
"/** @constructor */\n"
+ "function InternalWidget(){this.x = 1;}"
+ "InternalWidget.prototype.internalGo = function (){this.x = 2};"
+ "new InternalWidget().internalGo();";
testSame(options, code);
options.setComputeFunctionSideEffects(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.setFoldConstants(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.setComputeFunctionSideEffects(true);
test(options, code, result);
}
public void testFoldLocals6() {
CompilerOptions options = createCompilerOptions();
options.setFoldConstants(true);
String code =
""
+ "function fn(){return {}}"
+ "fn().x.y = 1;";
testSame(options, code);
options.setComputeFunctionSideEffects(true);
testSame(options, code);
}
public void testFoldLocals7() {
CompilerOptions options = createCompilerOptions();
options.setFoldConstants(true);
String code =
""
+ "function InternalWidget(){return [];}"
+ "Array.prototype.internalGo = function (){this.x = 2};"
+ "InternalWidget().internalGo();";
testSame(options, code);
options.setComputeFunctionSideEffects(true);
String optimized =
""
+ "function InternalWidget(){return [];}"
+ "Array.prototype.internalGo = function (){this.x = 2};";
test(options, code, optimized);
}
public void testVarDeclarationsIntoFor() {
CompilerOptions options = createCompilerOptions();
options.setCollapseVariableDeclarations(false);
String code = "var a = 1; for (var b = 2; ;) {}";
testSame(options, code);
options.setCollapseVariableDeclarations(true);
test(options, code, "for (var a = 1, b = 2; ;) {}");
}
public void testExploitAssigns() {
CompilerOptions options = createCompilerOptions();
options.setCollapseVariableDeclarations(false);
String code = "a = 1; b = a; c = b";
testSame(options, code);
options.setCollapseVariableDeclarations(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();
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.setCheckSymbols(true);
externs = ImmutableList.of(SourceFile.fromCode(
"externs", "var externs = {}; /** @suppress {duplicate} */ var externs = {};"));
testSame(options, "");
}
public void testLanguageMode() {
CompilerOptions options = createCompilerOptions();
String code = "var a = {get f(){}}";
options.setLanguageIn(LanguageMode.ECMASCRIPT3);
Compiler compiler = compile(options, code);
checkUnexpectedErrorsOrWarnings(compiler, 1);
assertEquals(
"JSC_PARSE_ERROR. Parse error."
+ " getters are not supported in older versions of JavaScript."
+ " If you are targeting newer versions of JavaScript,"
+ " set the appropriate language_in option."
+ " at i0.js 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.setWarningLevel(DiagnosticGroups.ES5_STRICT, CheckLevel.OFF);
String code = "var a = 2; delete a;";
options.setLanguageIn(LanguageMode.ECMASCRIPT3);
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);
options.setLanguageOut(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" +
"};";
testSame(options, code);
}
public void testCheckStrictMode() {
CompilerOptions options = createCompilerOptions();
CompilationLevel.ADVANCED_OPTIMIZATIONS
.setOptionsForCompilationLevel(options);
WarningLevel.VERBOSE.setOptionsForWarningLevel(options);
externs = ImmutableList.of(
SourceFile.fromCode(
"externs", "var use; var arguments; arguments.callee;"));
String code =
"function App() {}\n" +
"App.prototype.method = function(){\n" +
" use(arguments.callee)\n" +
"};";
test(options, code, "", StrictModeCheck.ARGUMENTS_CALLEE_FORBIDDEN);
}
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);
options.setCheckSymbols(false);
options.setCheckTypes(false);
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.setRemoveUnusedClassProperties(true);
// This is still not a problem when removeUnusedClassProperties is enabled.
test(options,
code,
"function a(){this.b=0;Object.seal(this)}" +
"(new function(){this.a=new a}).a.b++;" +
"alert(\"hi\")");
}
public void testAddFunctionProperties1() throws Exception {
String source =
"var Foo = {};" +
"var addFuncProp = function(o) {" +
" o.f = function() {}" +
"};" +
"addFuncProp(Foo);" +
"alert(Foo.f());";
String expected =
"alert(void 0);";
CompilerOptions options = createCompilerOptions();
CompilationLevel.ADVANCED_OPTIMIZATIONS
.setOptionsForCompilationLevel(options);
options.setRenamingPolicy(
VariableRenamingPolicy.OFF, PropertyRenamingPolicy.OFF);
test(options, source, expected);
}
public void testAddFunctionProperties2a() throws Exception {
String source =
""
+ "/** @constructor */ function F() {}"
+ "var x = new F();"
+ "/** @this {F} */"
+ "function g() { this.bar = function() { alert(3); }; }"
+ "g.call(x);"
+ "x.bar();";
String expected =
""
+ "var x = new function() {};"
+ "/** @this {F} */"
+ "(function () { this.bar = function() { alert(3); }; }).call(x);"
+ "x.bar();";
CompilerOptions options = createCompilerOptions();
CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
options.setStrictModeInput(false);
options.setAssumeStrictThis(false);
options.setRenamingPolicy(VariableRenamingPolicy.OFF, PropertyRenamingPolicy.OFF);
test(options, source, expected);
}
public void testAddFunctionProperties2b() throws Exception {
String source =
""
+ "/** @constructor */ function F() {}"
+ "var x = new F();"
+ "/** @this {F} */"
+ "function g() { this.bar = function() { alert(3); }; }"
+ "g.call(x);"
+ "x.bar();";
String expected =
""
+ "var x = new function() {};"
+ "x.bar=function(){alert(3)};x.bar()";
CompilerOptions options = createCompilerOptions();
CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
options.setRenamingPolicy(
VariableRenamingPolicy.OFF, PropertyRenamingPolicy.OFF);
test(options, source, expected);
}
public void testAddFunctionProperties3() throws Exception {
String source =
"/** @constructor */ function F() {}" +
"var x = new F();" +
"/** @this {F} */" +
"function g(y) { y.bar = function() { alert(3); }; }" +
"g(x);" +
"x.bar();";
String expected =
"var x = new function() {};" +
"/** @this {F} */" +
"(function (y) { y.bar = function() { alert(3); }; })(x);" +
"x.bar();";
CompilerOptions options = createCompilerOptions();
CompilationLevel.ADVANCED_OPTIMIZATIONS
.setOptionsForCompilationLevel(options);
options.setRenamingPolicy(
VariableRenamingPolicy.OFF, PropertyRenamingPolicy.OFF);
options.setCheckTypes(false);
test(options, source, expected);
}
public void testAddFunctionProperties4() throws Exception {
String source =
"/** @constructor */" +
"var Foo = function() {};" +
"var goog = {};" +
"goog.addSingletonGetter = function(o) {" +
" o.f = function() {" +
" o.i = new o;" +
" };" +
"};" +
"goog.addSingletonGetter(Foo);" +
"alert(Foo.f());";
String expected = "function Foo(){} Foo.f=function(){Foo.i=new Foo}; alert(Foo.f());";
CompilerOptions options = createCompilerOptions();
CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
options.setRenamingPolicy(VariableRenamingPolicy.OFF, PropertyRenamingPolicy.OFF);
test(options, source, 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.setCoalesceVariableNames(true);
test(options, code, "function f() {var x = 3; x = x; x = x; return x;}");
}
public void testCoalesceVariables() {
CompilerOptions options = createCompilerOptions();
options.setWarningLevel(DiagnosticGroups.CHECK_USELESS_CODE, CheckLevel.OFF);
options.setFoldConstants(false);
options.setCoalesceVariableNames(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.setFoldConstants(true);
options.setCoalesceVariableNames(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.setFoldConstants(true);
options.setCoalesceVariableNames(true);
expected =
"function f(a) {"
+ " return a;"
+ "}";
test(options, code, expected);
}
public void testLateStatementFusion() {
CompilerOptions options = createCompilerOptions();
options.setFoldConstants(true);
test(options, "while(a){a();if(b){b();b()}}", "for(;a;)a(),b&&(b(),b())");
}
public void testLateConstantReordering() {
CompilerOptions options = createCompilerOptions();
options.setFoldConstants(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.setDeadAssignmentElimination(true);
options.setRemoveUnusedVariables(Reach.ALL);
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.setFoldConstants(true);
options.syntheticBlockStartMarker = "START";
options.syntheticBlockEndMarker = "END";
test(options, "START();END()", "{START();{}END()}");
}
public void testNoFuseIntoSyntheticBlock() {
CompilerOptions options = createCompilerOptions();
options.setFoldConstants(true);
options.syntheticBlockStartMarker = "START";
options.syntheticBlockEndMarker = "END";
options.aggressiveFusion = false;
testSame(options, "for(;;) { x = 1; {START(); {z = 3} END()} }");
testSame(options, "x = 1; y = 2; {START(); {z = 3} END()} f()");
options.aggressiveFusion = true;
testSame(options, "x = 1; {START(); {z = 3} END()} f()");
test(options, "x = 1; y = 3; {START(); {z = 3} END()} f()",
"x = 1, y = 3; {START(); {z = 3} END()} f()");
}
public void testBug5786871() {
CompilerOptions options = createCompilerOptions();
options.setIdeMode(true);
testParseError(options, "function () {}");
}
public void testIssue378() {
CompilerOptions options = createCompilerOptions();
options.setInlineVariables(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.setFoldConstants(true);
options.setInlineVariables(true);
test(
options,
"function f(h) {\n"
+ " var a = h;\n"
+ " a = a + 'x';\n"
+ " a = a + 'y';\n"
+ " return a;\n"
+ "}",
"function f(a) { return a += 'xy'; }");
}
public void testIssue1168() {
CompilerOptions options = createCompilerOptions();
CompilationLevel.SIMPLE_OPTIMIZATIONS
.setOptionsForCompilationLevel(options);
options.setWarningLevel(DiagnosticGroups.CHECK_USELESS_CODE, CheckLevel.OFF);
test(
options,
"while (function () {\n" + " function f(){};\n" + " L: while (void(f += 3)) {}\n" + "}) {}",
"for( ; ; );");
}
public void testIssue1198() throws Exception {
CompilerOptions options = createCompilerOptions();
CompilationLevel.SIMPLE_OPTIMIZATIONS
.setOptionsForCompilationLevel(options);
options.setWarningLevel(DiagnosticGroups.CHECK_USELESS_CODE, CheckLevel.OFF);
test(options,
"function f(x) { return 1; do { x(); } while (true); }",
"function f(a) { return 1; }");
}
public void testIssue1131() {
CompilerOptions options = createCompilerOptions();
CompilationLevel.ADVANCED_OPTIMIZATIONS
.setOptionsForCompilationLevel(options);
test(options,
"function f(k) { return k(k); } alert(f(f));",
"function a(b) { return b(b); } alert(a(a));");
}
public void testIssue284() {
CompilerOptions options = createCompilerOptions();
options.setSmartNameRemoval(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.setClosurePass(true);
options.setCheckTypes(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 testSuppressBadGoogRequire() throws Exception {
CompilerOptions options = createCompilerOptions();
options.setClosurePass(true);
options.setCheckTypes(true);
test(
options,
"/** @suppress {closureDepMethodUsageChecks} */\n"
+ "function f() { goog.require('foo'); }\n"
+ "f();",
"function f() { goog.require('foo'); }\n"
+ "f();");
}
public void testIssue1204() {
CompilerOptions options = createCompilerOptions();
CompilationLevel.ADVANCED_OPTIMIZATIONS
.setOptionsForCompilationLevel(options);
WarningLevel.VERBOSE
.setOptionsForWarningLevel(options);
test(options,
"goog.scope(function () {" +
" /** @constructor */ function F(x) { this.x = x; }" +
" alert(new F(1));" +
"});",
"alert(new function(){}(1));");
}
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.setFoldConstants(true);
test(options, "var x=['1','2','3','4','5','6','7']", "var x='1234567'.split('')");
options = createCompilerOptions();
options.setFoldConstants(true);
options.setComputeFunctionSideEffects(false);
options.setRemoveUnusedVariables(Reach.ALL);
// 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.setCheckSymbols(false);
options.addCustomPass(
CustomPassExecutionTime.BEFORE_OPTIMIZATIONS,
new CompilerPass() {
@Override
public void process(Node externs, Node root) {
Node var = root.getLastChild().getFirstChild();
assertEquals(Token.VAR, var.getToken());
var.detach();
}
});
try {
test(options,
"var x = 3; function f() { return x + z; }",
"function f() { return x + z; }");
fail("Expected run-time exception");
} catch (RuntimeException e) {
assertThat(e.getMessage()).contains("Unexpected variable x");
}
}
public void testSuppressEs5StrictWarning() {
CompilerOptions options = createCompilerOptions();
CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
options.setWarningLevel(DiagnosticGroups.ES5_STRICT, CheckLevel.WARNING);
test(options,
"/** @suppress{es5Strict} */ function f() { var arguments; }",
"");
}
public void testCheckProvidesWarning() {
CompilerOptions options = createCompilerOptions();
options.setWarningLevel(DiagnosticGroups.MISSING_PROVIDE, CheckLevel.WARNING);
options.setWarningLevel(DiagnosticGroups.ES5_STRICT, CheckLevel.OFF);
test(options,
"goog.require('x'); /** @constructor */ function f() { var arguments; }",
CheckProvides.MISSING_PROVIDE_WARNING);
}
public void testSuppressCheckProvidesWarning() {
CompilerOptions options = createCompilerOptions();
options.setWarningLevel(DiagnosticGroups.MISSING_PROVIDE,
CheckLevel.WARNING);
options.setWarningLevel(DiagnosticGroups.MISSING_PROVIDE, CheckLevel.WARNING);
testSame(options,
"/** @constructor\n" +
" * @suppress{missingProvide} */\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 {invalidCasts} */\n" +
"function f() { var xyz = /** @type {string} */ (0); }");
testSame(options,
"/** @const */ var g = {};" +
"/** @suppress {invalidCasts} */" +
"g.a = g.b = function() { var xyz = /** @type {string} */ (0); }");
}
public void testLhsCast() {
CompilerOptions options = createCompilerOptions();
test(
options,
"/** @const */ var g = {};" +
"/** @type {number} */ (g.foo) = 3;",
"/** @const */ var g = {};" +
"g.foo = 3;");
}
public void testRenamePrefix() {
String code = "var x = {}; function f(y) {}";
CompilerOptions options = createCompilerOptions();
options.setRenamePrefix("G_");
options.setVariableRenaming(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.setCollapseProperties(true);
options.setRenamePrefixNamespace("_");
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.setRenamePrefixNamespace("_");
test(options, code, "_.x = null; try { +_.x.FOO; } catch (a) {}");
}
// https://github.com/google/closure-compiler/issues/1875
public void testNoProtectSideEffectsInChecksOnly() {
String code = "x;";
CompilerOptions options = createCompilerOptions();
options.setChecksOnly(true);
options.setProtectHiddenSideEffects(true);
testSame(options, code);
}
public void testRenameCollision() {
String code = "" +
"/**\n" +
" * @fileoverview\n" +
" * @suppress {uselessCode}\n" +
" */" +
"var x = {};\ntry {\n(0,use)(x.FOO);\n} catch (e) {}";
CompilerOptions options = createCompilerOptions();
testSame(options, code);
CompilationLevel.SIMPLE_OPTIMIZATIONS.setOptionsForCompilationLevel(
options);
options.setRenamePrefixNamespace("a");
options.setVariableRenaming(VariableRenamingPolicy.ALL);
options.setRenamePrefixNamespaceAssumeCrossModuleNames(false);
WarningLevel.DEFAULT.setOptionsForWarningLevel(options);
test(options, code,
"var b = {}; try { (0,window.use)(b.FOO); } catch (c) {}");
}
public void testRenamePrefixNamespaceActivatesMoveFunctionDeclarations() {
CompilerOptions options = createCompilerOptions();
String code = "var x = f; function f() { return 3; }";
testSame(options, code);
assertFalse(options.moveFunctionDeclarations);
options.setRenamePrefixNamespace("_");
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.setClosurePass(true);
options.setCollapseProperties(true);
options.setSmartNameRemoval(true);
test(options, code, "");
}
public void testNamelessParameter() {
CompilerOptions options = createCompilerOptions();
CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
options.setCheckTypes(false);
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);
String code =
"window.offsetWidth;";
String result =
"window.offsetWidth;";
test(options, code, result);
}
public void testNegativeZero() {
CompilerOptions options = createCompilerOptions();
CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
options.setCheckSymbols(false);
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.setIdeMode(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.setIdeMode(true);
testParseError(options, "function hi", "function hi() {}");
}
public void testSortingOff() {
CompilerOptions options = new CompilerOptions();
options.setClosurePass(true);
options.setCodingConvention(new ClosureCodingConvention());
test(options,
new String[] {
"goog.require('goog.beer');",
"goog.provide('goog.beer');"
},
ProcessClosurePrimitives.LATE_PROVIDE_ERROR);
}
public void testGoogModuleOuterLegacyInner() {
CompilerOptions options = new CompilerOptions();
options.setClosurePass(true);
options.setCodingConvention(new ClosureCodingConvention());
test(
options,
new String[] {
// googModuleOuter
LINE_JOINER.join(
"goog.module('foo.Outer');",
"/** @constructor */ function Outer() {}",
"exports = Outer;"),
// legacyInner
LINE_JOINER.join(
"goog.module('foo.Outer.Inner');",
"goog.module.declareLegacyNamespace();",
"/** @constructor */ function Inner() {}",
"exports = Inner;"),
// legacyUse
LINE_JOINER.join(
"goog.provide('legacy.Use');",
"goog.require('foo.Outer');",
"goog.require('foo.Outer.Inner');",
"goog.scope(function() {",
"var Outer = goog.module.get('foo.Outer');",
"new Outer;",
"new foo.Outer.Inner;",
"});")
},
ClosureRewriteModule.QUALIFIED_REFERENCE_TO_GOOG_MODULE);
}
public void testLegacyGoogModuleExport() {
CompilerOptions options = new CompilerOptions();
options.setClosurePass(true);
options.setCodingConvention(new ClosureCodingConvention());
options.setGenerateExports(true);
test(
options,
new String[] {
LINE_JOINER.join(
"var goog = {};",
"goog.exportSymbol = function(path, symbol) {};"),
LINE_JOINER.join(
"goog.module('foo.example.ClassName');",
"goog.module.declareLegacyNamespace();",
"",
"/** @constructor */ function ClassName() {}",
"",
"/** @export */",
"exports = ClassName;"),
},
new String[] {
LINE_JOINER.join(
"var goog = {};",
"goog.exportSymbol = function(path, symbol) {};"),
LINE_JOINER.join(
"var foo = {};",
"foo.example = {};",
"function module$contents$foo$example$ClassName_ClassName() {}",
"foo.example.ClassName = module$contents$foo$example$ClassName_ClassName;",
"goog.exportSymbol('foo.example.ClassName', foo.example.ClassName);"),
});
test(
options,
new String[] {
LINE_JOINER.join(
"var goog = {};",
"goog.exportSymbol = function(path, symbol) {};"),
LINE_JOINER.join(
"goog.module('foo.ns');",
"goog.module.declareLegacyNamespace();",
"",
"/** @constructor */ function ClassName() {}",
"",
"/** @export */",
"exports.ExportedName = ClassName;"),
},
new String[] {
LINE_JOINER.join(
"var goog = {};",
"goog.exportSymbol = function(path, symbol) {};"),
LINE_JOINER.join(
"var foo = {};",
"foo.ns = {};",
"function module$contents$foo$ns_ClassName() {}",
"foo.ns.ExportedName = module$contents$foo$ns_ClassName;",
"goog.exportSymbol('foo.ns.ExportedName', foo.ns.ExportedName);"),
});
}
public void testUnboundedArrayLiteralInfiniteLoop() {
CompilerOptions options = createCompilerOptions();
options.setIdeMode(true);
testParseError(options, "var x = [1, 2", "var x = [1, 2]");
}
public void testProvideRequireSameFile() throws Exception {
CompilerOptions options = createCompilerOptions();
options.setDependencyOptions(
new DependencyOptions()
.setDependencySorting(true));
options.setClosurePass(true);
test(
options,
"goog.provide('x'); goog.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.setCheckTypes(true);
options.addWarningsGuard(new StrictWarningsGuard());
Compiler compiler = compile(options,
"/** @return {number} */ function f() { return true; }");
assertThat(compiler.getErrors()).hasLength(1);
assertThat(compiler.getWarnings()).isEmpty();
}
public void testStrictWarningsGuardEmergencyMode() throws Exception {
CompilerOptions options = createCompilerOptions();
options.setCheckTypes(true);
options.addWarningsGuard(new StrictWarningsGuard());
options.useEmergencyFailSafe();
Compiler compiler = compile(options,
"/** @return {number} */ function f() { return true; }");
assertThat(compiler.getErrors()).isEmpty();
assertThat(compiler.getWarnings()).hasLength(1);
}
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.shouldInlineProperties());
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;
options.setCheckTypes(true);
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.shouldInlineProperties());
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;
options.setCheckTypes(true);
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.shouldInlineProperties());
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.shouldInlineProperties());
assertTrue(options.collapseProperties);
// CollapseProperties used to prevent inlining this property.
test(options, code, TypeValidator.TYPE_MISMATCH_WARNING);
}
public void testGoogDefineClass4() {
CompilerOptions options = createCompilerOptions();
CompilationLevel level = CompilationLevel.ADVANCED_OPTIMIZATIONS;
level.setOptionsForCompilationLevel(options);
WarningLevel warnings = WarningLevel.VERBOSE;
warnings.setOptionsForWarningLevel(options);
options.setWarningLevel(
DiagnosticGroups.GLOBAL_THIS, CheckLevel.WARNING);
String code = "" +
"var C = goog.defineClass(null, {\n" +
" /** @param {string} a */\n" +
" constructor: function (a) {this.someProperty = 1}\n" +
"});\n";
test(options, code, "");
}
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 testIssue937() {
CompilerOptions options = createCompilerOptions();
CompilationLevel level = CompilationLevel.SIMPLE_OPTIMIZATIONS;
level.setOptionsForCompilationLevel(options);
WarningLevel warnings = WarningLevel.DEFAULT;
warnings.setOptionsForWarningLevel(options);
String code = "" +
"console.log(" +
"/** @type {function():!string} */ ((new x())['abc'])());";
String result = "" +
"console.log((new x()).abc());";
test(options, code, result);
}
public void testES5toES6() throws Exception {
CompilerOptions options = createCompilerOptions();
options.setLanguageIn(LanguageMode.ECMASCRIPT5_STRICT);
options.setLanguageOut(LanguageMode.ECMASCRIPT_2015);
options.setSkipTranspilationAndCrash(true);
CompilationLevel.SIMPLE_OPTIMIZATIONS
.setOptionsForCompilationLevel(options);
String code = "f = function(c) { for (var i = 0; i < c.length; i++) {} };";
compile(options, code);
}
// Tests that unused classes are removed, even if they are passed to $jscomp.inherits.
private void testES6UnusedClassesAreRemoved(CodingConvention convention) {
CompilerOptions options = createCompilerOptions();
options.setLanguageIn(LanguageMode.ECMASCRIPT_2015);
options.setLanguageOut(LanguageMode.ECMASCRIPT3);
options.setCodingConvention(convention);
CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
Compiler compiler = compile(options, LINE_JOINER.join(
"class Base {}",
"class Sub extends Base {}",
"alert(1);"));
String result = compiler.toSource(compiler.getJsRoot());
assertThat(result).isEqualTo("alert(1)");
}
public void testES6UnusedClassesAreRemoved() {
testES6UnusedClassesAreRemoved(CodingConventions.getDefault());
testES6UnusedClassesAreRemoved(new ClosureCodingConvention());
testES6UnusedClassesAreRemoved(new GoogleCodingConvention());
}
/**
* @param js A snippet of JavaScript in which alert('No one ever calls me'); is called
* in a method which is never called. Verifies that the method is stripped out by
* asserting that the result does not contain the string 'No one ever calls me'.
*/
private void testES6StaticsAreRemoved(String js) {
CompilerOptions options = createCompilerOptions();
options.setLanguageIn(LanguageMode.ECMASCRIPT_2015);
options.setLanguageOut(LanguageMode.ECMASCRIPT3);
CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
Compiler compiler = compile(options, js);
assertThat(compiler.getErrors()).isEmpty();
String result = compiler.toSource(compiler.getJsRoot());
assertThat(result).isNotEmpty();
assertThat(result).doesNotContain("No one ever calls me");
}
public void testES6StaticsAreRemoved1() {
testES6StaticsAreRemoved(LINE_JOINER.join(
"class Base {",
" static called() { alert('I am called'); }",
" static notCalled() { alert('No one ever calls me'); }",
"}",
"class Sub extends Base {",
" static called() { super.called(); alert('I am called too'); }",
" static notCalled() { alert('No one ever calls me'); }",
"}",
"Sub.called();"));
}
public void failing_testES6StaticsAreRemoved2() {
testES6StaticsAreRemoved(LINE_JOINER.join(
"class Base {",
" static calledInSubclassOnly() { alert('No one ever calls me'); }",
"}",
"class Sub extends Base {",
" static calledInSubclassOnly() { alert('I am called'); }",
"}",
"Sub.calledInSubclassOnly();"));
}
public void testIssue787() {
CompilerOptions options = createCompilerOptions();
CompilationLevel level = CompilationLevel.SIMPLE_OPTIMIZATIONS;
level.setOptionsForCompilationLevel(options);
WarningLevel warnings = WarningLevel.DEFAULT;
warnings.setOptionsForWarningLevel(options);
String code = LINE_JOINER.join(
"function some_function() {",
" var fn1;",
" var fn2;",
"",
" if (any_expression) {",
" fn2 = external_ref;",
" fn1 = function (content) {",
" return fn2();",
" }",
" }",
"",
" return {",
" method1: function () {",
" if (fn1) fn1();",
" return true;",
" },",
" method2: function () {",
" return false;",
" }",
" }",
"}");
String result =
LINE_JOINER.join(
"function some_function() {",
" if (any_expression) {",
" var b = external_ref;",
" var a = function(a) {",
" return b()",
" };",
" }",
" return {",
" method1:function() {",
" a && a();",
" return !0",
" },",
" method2: function() {",
" return !1",
" }",
" }",
"}");
test(options, code, result);
}
public void testClosureDefines() {
CompilerOptions options = createCompilerOptions();
CompilationLevel level = CompilationLevel.SIMPLE_OPTIMIZATIONS;
level.setOptionsForCompilationLevel(options);
WarningLevel warnings = WarningLevel.DEFAULT;
warnings.setOptionsForWarningLevel(options);
String code = "" +
"var CLOSURE_DEFINES = {\n" +
" 'FOO': 1,\n" +
" 'BAR': true\n" +
"};\n" +
"\n" +
"/** @define {number} */ var FOO = 0;\n" +
"/** @define {boolean} */ var BAR = false;\n" +
"";
String result = "" +
"var CLOSURE_DEFINES = {\n" +
" FOO: 1,\n" +
" BAR: !0\n" +
"}," +
"FOO = 1," +
"BAR = !0" +
"";
test(options, code, result);
}
public void testExports() {
CompilerOptions options = createCompilerOptions();
CompilationLevel level = CompilationLevel.ADVANCED_OPTIMIZATIONS;
level.setOptionsForCompilationLevel(options);
WarningLevel warnings = WarningLevel.DEFAULT;
warnings.setOptionsForWarningLevel(options);
options.setRemoveUnusedPrototypePropertiesInExterns(true);
String code =
""
+ "/** @constructor */ var X = function() {"
+ "/** @export */ this.abc = 1;};\n"
+ "/** @constructor */ var Y = function() {"
+ "/** @export */ this.abc = 1;};\n"
+ "alert(new X().abc + new Y().abc);";
// no export enabled, property name not preserved
test(options, code,
"alert((new function(){this.a = 1}).a + " +
"(new function(){this.a = 1}).a);");
options.setGenerateExports(true);
// exports enabled, but not local exports
test(options,
"/** @constructor */ var X = function() {" +
"/** @export */ this.abc = 1;};\n",
FindExportableNodes.NON_GLOBAL_ERROR);
options.exportLocalPropertyDefinitions = true;
options.setRemoveUnusedPrototypePropertiesInExterns(false);
// property name preserved due to export
test(options, code,
"alert((new function(){this.abc = 1}).abc + " +
"(new function(){this.abc = 1}).abc);");
// unreferenced property not removed due to export.
test(options, "" +
"/** @constructor */ var X = function() {" +
"/** @export */ this.abc = 1;};\n" +
"/** @constructor */ var Y = function() {" +
"/** @export */ this.abc = 1;};\n" +
"alert(new X() + new Y());",
"alert((new function(){this.abc = 1}) + " +
"(new function(){this.abc = 1}));");
// disambiguate and ambiguate properties respect the exports.
options.setCheckTypes(true);
options.setDisambiguateProperties(true);
options.setAmbiguateProperties(true);
options.propertyInvalidationErrors = ImmutableMap.of("abc", CheckLevel.ERROR);
test(options, code,
"alert((new function(){this.abc = 1}).abc + " +
"(new function(){this.abc = 1}).abc);");
// unreferenced property not removed due to export.
test(options, "" +
"/** @constructor */ var X = function() {" +
"/** @export */ this.abc = 1;};\n" +
"/** @constructor */ var Y = function() {" +
"/** @export */ this.abc = 1;};\n" +
"alert(new X() + new Y());",
"alert((new function(){this.abc = 1}) + " +
"(new function(){this.abc = 1}));");
}
public void testGatherExternPropsWithNTI() {
CompilerOptions options = new CompilerOptions();
options.setNewTypeInference(true);
options.setGenerateExports(true);
options.setExportLocalPropertyDefinitions(true);
options.setPropertyRenaming(PropertyRenamingPolicy.ALL_UNQUOTED);
testSame(options,
LINE_JOINER.join(
"(function exportedTokensFromTemplate() {",
" function unusedFn(a) {}",
" var UNUSED = 1;",
" unusedFn({ /** @export */ foo: UNUSED });",
"})();",
"/** @constructor */",
"function Bar() {",
" // Should not be renamed",
" this.foo = 1;",
"}"));
}
public void testRmUnusedProtoPropsInExternsUsage() {
CompilerOptions options = new CompilerOptions();
options.setRemoveUnusedPrototypePropertiesInExterns(true);
options.setRemoveUnusedPrototypeProperties(false);
try {
test(options, "", "");
fail("Expected CompilerOptionsPreprocessor.InvalidOptionsException");
} catch (RuntimeException e) {
if (!(e instanceof CompilerOptionsPreprocessor.InvalidOptionsException)) {
fail("Expected CompilerOptionsPreprocessor.InvalidOptionsException");
}
}
}
public void testMaxFunSizeAfterInliningUsage() {
CompilerOptions options = new CompilerOptions();
options.setInlineFunctions(false);
options.setMaxFunctionSizeAfterInlining(1);
try {
test(options, "", "");
fail("Expected CompilerOptionsPreprocessor.InvalidOptionsException");
} catch (RuntimeException e) {
if (!(e instanceof CompilerOptionsPreprocessor.InvalidOptionsException)) {
fail("Expected CompilerOptionsPreprocessor.InvalidOptionsException");
}
}
}
// isEquivalentTo returns false for alpha-equivalent nodes
public void testIsEquivalentTo() {
String[] input1 = {"function f(z) { return z; }"};
String[] input2 = {"function f(y) { return y; }"};
CompilerOptions options = new CompilerOptions();
Node out1 = parse(input1, options, false);
Node out2 = parse(input2, options, false);
assertFalse(out1.isEquivalentTo(out2));
}
public void testEs6OutDoesntCrash() {
CompilerOptions options = new CompilerOptions();
options.setLanguageIn(LanguageMode.ECMASCRIPT_2015);
options.setSkipTranspilationAndCrash(true);
test(options, "function f(x) { if (x) var x=5; }", "function f(x) { if (x) x=5; }");
}
public void testExternsProvideIsAllowed() {
CompilerOptions options = createCompilerOptions();
options.setIncrementalChecks(CompilerOptions.IncrementalCheckMode.CHECK_IJS);
options.setClosurePass(true);
options.setCheckTypes(true);
externs = ImmutableList.of(SourceFile.fromCode("<externs>",
"goog.provide('foo.bar'); /** @type {!Array<number>} */ foo.bar;"));
test(options, "", "");
}
// GitHub issue #250: https://github.com/google/closure-compiler/issues/250
public void testInlineStringConcat() {
CompilerOptions options = createCompilerOptions();
CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
test(options, LINE_JOINER.join(
"function f() {",
" var x = '';",
" x += '1';",
" x += '2';",
" x += '3';",
" x += '4';",
" x += '5';",
" x += '6';",
" x += '7';",
" return x;",
"}",
"window.f = f;"),
"window.a = function() { return '1234567'; }");
}
// GitHub issue #1234: https://github.com/google/closure-compiler/issues/1234
public void testOptimizeSwitchGithubIssue1234() {
CompilerOptions options = createCompilerOptions();
CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
options.setCheckSymbols(false);
test(
options,
LINE_JOINER.join(
"var exit;",
"switch ('a') {",
" case 'a':",
" break;",
" default:",
" exit = 21;",
" break;",
"}",
"switch(exit) {",
" case 21: throw 'x';",
" default : console.log('good');",
"}"),
"console.a('good');");
}
// GitHub issue #2079: https://github.com/google/closure-compiler/issues/2079
public void testStrictEqualityComparisonAgainstUndefined() {
CompilerOptions options = createCompilerOptions();
options.setFoldConstants(true);
options.setCheckTypes(true);
options.setUseTypesForLocalOptimization(true);
test(
options,
LINE_JOINER.join(
"if (/** @type {Array|undefined} */ (window['c']) === null) {",
" window['d'] = 12;",
"}"),
"null===window['c']&&(window['d']=12)");
}
// GitHub issue #2122: https://github.com/google/closure-compiler/issues/2122
public void testNoRemoveSuperCallWithWrongArgumentCount() {
CompilerOptions options = createCompilerOptions();
options.setCheckTypes(true);
CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
test(options,
LINE_JOINER.join(
"var goog = {}",
"goog.inherits = function(childCtor, parentCtor) {",
" childCtor.superClass_ = parentCtor.prototype;",
"};",
"/** @constructor */",
"var Foo = function() {}",
"/**",
" * @param {number=} width",
" * @param {number=} height",
" */",
"Foo.prototype.resize = function(width, height) {",
" window.size = width * height;",
"}",
"/**",
" * @constructor",
" * @extends {Foo}",
" */",
"var Bar = function() {}",
"goog.inherits(Bar, Foo);",
"/** @override */",
"Bar.prototype.resize = function(width, height) {",
" goog.base(this, 'resize', width);",
"};",
"(new Bar).resize(100, 200);"),
LINE_JOINER.join(
"function a(){}a.prototype.a=function(b,e){window.c=b*e};",
"function d(){}d.b=a.prototype;d.prototype.a=function(b){d.b.a.call(this,b)};",
"(new d).a(100, 200);"));
}
// GitHub issue #2203: https://github.com/google/closure-compiler/issues/2203
public void testPureFunctionIdentifierWorksWithMultipleNames() {
CompilerOptions options = createCompilerOptions();
CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
test(options,
LINE_JOINER.join(
"var SetCustomData1 = function SetCustomData2(element, dataName, dataValue) {",
" var x = element['_customData'];",
" x[dataName] = dataValue;",
"}",
"SetCustomData1(window, \"foo\", \"bar\");"),
"window._customData.foo=\"bar\";");
}
public void testUnnecessaryBackslashInStringLiteral() {
CompilerOptions options = createCompilerOptions();
test(options,
"var str = '\\q';",
"var str = 'q';");
}
public void testWarnUnnecessaryBackslashInStringLiteral() {
CompilerOptions options = createCompilerOptions();
options.setWarningLevel(DiagnosticGroups.LINT_CHECKS, CheckLevel.WARNING);
test(options,
"var str = '\\q';",
"var str = 'q';",
RhinoErrorReporter.UNNECESSARY_ESCAPE);
}
public void testAngularPropertyNameRestrictions() {
CompilerOptions options = createCompilerOptions();
options.setLanguageIn(LanguageMode.ECMASCRIPT5);
options.setLanguageOut(LanguageMode.ECMASCRIPT5);
CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
options.setAngularPass(true);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 250; i++) {
sb.append("window.foo").append(i).append("=true;\n");
}
Compiler compiler = compile(options, sb.toString());
assertEquals(
"Expected no warnings or errors\n"
+ "Errors: \n"
+ Joiner.on("\n").join(compiler.getErrors())
+ "\n"
+ "Warnings: \n"
+ Joiner.on("\n").join(compiler.getWarnings()),
0,
compiler.getErrors().length + compiler.getWarnings().length);
Node root = compiler.getRoot().getLastChild();
assertNotNull(root);
Node script = root.getFirstChild();
assertNotNull(script);
ImmutableSet<Character> restrictedChars =
ImmutableSet.copyOf(Chars.asList(CompilerOptions.ANGULAR_PROPERTY_RESERVED_FIRST_CHARS));
for (Node expr : script.children()) {
NodeSubject.assertNode(expr).hasType(Token.EXPR_RESULT);
NodeSubject.assertNode(expr.getFirstChild()).hasType(Token.ASSIGN);
NodeSubject.assertNode(expr.getFirstFirstChild()).hasType(Token.GETPROP);
Node getProp = expr.getFirstFirstChild();
NodeSubject.assertNode(getProp.getSecondChild()).hasType(Token.STRING);
String propName = getProp.getSecondChild().getString();
assertThat(restrictedChars).doesNotContain(propName.charAt(0));
}
}
public void testCheckVarsOnInAdvancedMode() {
CompilerOptions options = new CompilerOptions();
CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(options);
test(options, "var x = foobar;", VarCheck.UNDEFINED_VAR_ERROR);
}
/** Creates a CompilerOptions object with google coding conventions. */
@Override
protected CompilerOptions createCompilerOptions() {
CompilerOptions options = new CompilerOptions();
options.setCodingConvention(new GoogleCodingConvention());
options.setRenamePrefixNamespaceAssumeCrossModuleNames(true);
options.declaredGlobalExternsOnWindow = false;
return options;
}
}