/*
* Copyright 2015 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.collect.Lists;
import com.google.javascript.jscomp.CompilerOptions.LanguageMode;
import java.util.List;
/**
* Tests for {@link J2clPass}.
*/
public class J2clPassTest extends CompilerTestCase {
public J2clPassTest() {
this.enableNormalize();
}
private void testDoesntChange(List<SourceFile> js) {
test(js, js);
}
@Override
protected CompilerPass getProcessor(final Compiler compiler) {
return new J2clPass(compiler);
}
@Override
protected CompilerOptions getOptions() {
CompilerOptions options = super.getOptions();
options.setJ2clPass(CompilerOptions.J2clPassMode.ON);
return options;
}
public void testUtilGetDefine() {
String defineAbc = "var a={}; a.b={}; /** @define {boolean} */ a.b.c = true;\n";
test(
defineAbc + "nativebootstrap.Util.$getDefine('a.b.c', 'def');",
defineAbc + "('def', String(a.b.c));");
test(
defineAbc + "nativebootstrap.Util.$getDefine('a.b.c');",
defineAbc + "(null, String(a.b.c));");
}
public void testUtilGetDefine_notDefined() {
test("nativebootstrap.Util.$getDefine('not.defined');", "null;");
test("nativebootstrap.Util.$getDefine('not.defined', 'def');", "'def';");
}
public void testQualifiedInlines() {
// Arrays functions.
test(
Lists.newArrayList(
SourceFile.fromCode(
"j2cl/transpiler/vmbootstrap/Arrays.impl.java.js",
LINE_JOINER.join(
// Function definitions and calls are qualified globals.
"var Arrays = function() {};",
"Arrays.$create = function() { return 1; }",
"Arrays.$init = function() { return 2; }",
"Arrays.$instanceIsOfType = function() { return 3; }",
"Arrays.$castTo = function() { return 4; }",
"Arrays.$stampType = function() { return 5; }",
"",
"alert(Arrays.$create());",
"alert(Arrays.$init());",
"alert(Arrays.$instanceIsOfType());",
"alert(Arrays.$castTo());",
"alert(Arrays.$stampType());"))),
Lists.newArrayList(
SourceFile.fromCode(
"j2cl/transpiler/vmbootstrap/Arrays.impl.java.js",
LINE_JOINER.join(
// Function definitions and calls are qualified globals.
"var Arrays = function() {};",
"Arrays.$create = function() { return 1; }",
"Arrays.$init = function() { return 2; }",
"Arrays.$instanceIsOfType = function() { return 3; }",
"Arrays.$castTo = function() { return 4; }",
"Arrays.$stampType = function() { return 5; }",
"",
"alert(1);",
"alert(2);",
"alert(3);",
"alert(4);",
"alert(5);"))));
// Casts functions.
test(
Lists.newArrayList(
SourceFile.fromCode(
"j2cl/transpiler/vmbootstrap/Casts.impl.java.js",
LINE_JOINER.join(
// Function definitions and calls are qualified globals.
"var Casts = function() {};",
"Casts.$to = function() { return 1; }",
"",
"alert(Casts.$to());"))),
Lists.newArrayList(
SourceFile.fromCode(
"j2cl/transpiler/vmbootstrap/Casts.impl.java.js",
LINE_JOINER.join(
// Function definitions and calls are qualified globals.
"var Casts = function() {};",
"Casts.$to = function() { return 1; }",
"",
"alert(1);"))));
// Interface $markImplementor() functions.
test(
Lists.newArrayList(
SourceFile.fromCode(
"name/doesnt/matter/Foo.impl.java.js",
LINE_JOINER.join(
// Function definitions and calls are qualified globals.
"var FooInterface = function() {};",
"FooInterface.$markImplementor = function(classDef) {",
" classDef.$implements__FooInterface = true;",
"}",
"",
"var Foo = function() {};",
"FooInterface.$markImplementor(Foo);"))),
Lists.newArrayList(
SourceFile.fromCode(
"name/doesnt/matter/Foo.impl.java.js",
LINE_JOINER.join(
// Function definitions and calls are qualified globals.
"var FooInterface = function() {};",
"FooInterface.$markImplementor = function(classDef) {",
" classDef.$implements__FooInterface = true;",
"}",
"",
"var Foo = function() {};",
"{Foo.$implements__FooInterface = true;}"))));
}
public void testRenamedQualifierStillInlines() {
// Arrays functions.
test(
Lists.newArrayList(
SourceFile.fromCode(
"j2cl/transpiler/vmbootstrap/Arrays.impl.java.js",
LINE_JOINER.join(
// Function definitions and calls are qualified globals.
"var $jscomp = {};",
"$jscomp.scope = {};",
"$jscomp.scope.Arrays = {};",
"$jscomp.scope.Arrays.$create = function() { return 1; }",
"$jscomp.scope.Arrays.$init = function() { return 2; }",
"$jscomp.scope.Arrays.$instanceIsOfType = function() { return 3; }",
"$jscomp.scope.Arrays.$castTo = function() { return 4; }",
"",
"alert($jscomp.scope.Arrays.$create());",
"alert($jscomp.scope.Arrays.$init());",
"alert($jscomp.scope.Arrays.$instanceIsOfType());",
"alert($jscomp.scope.Arrays.$castTo());"))),
Lists.newArrayList(
SourceFile.fromCode(
"j2cl/transpiler/vmbootstrap/Arrays.impl.java.js",
LINE_JOINER.join(
// Function definitions and calls are qualified globals.
"var $jscomp = {};",
"$jscomp.scope = {};",
"$jscomp.scope.Arrays = {};",
"$jscomp.scope.Arrays.$create = function() { return 1; }",
"$jscomp.scope.Arrays.$init = function() { return 2; }",
"$jscomp.scope.Arrays.$instanceIsOfType = function() { return 3; }",
"$jscomp.scope.Arrays.$castTo = function() { return 4; }",
"",
"alert(1);",
"alert(2);",
"alert(3);",
"alert(4);"))));
// Casts functions.
test(
Lists.newArrayList(
SourceFile.fromCode(
"j2cl/transpiler/vmbootstrap/Casts.impl.java.js",
LINE_JOINER.join(
// Function definitions and calls are qualified globals.
"var $jscomp = {};",
"$jscomp.scope = {};",
"$jscomp.scope.Casts = function() {};",
"$jscomp.scope.Casts.$to = function() { return 1; }",
"",
"alert($jscomp.scope.Casts.$to());"))),
Lists.newArrayList(
SourceFile.fromCode(
"j2cl/transpiler/vmbootstrap/Casts.impl.java.js",
LINE_JOINER.join(
// Function definitions and calls are qualified globals.
"var $jscomp = {};",
"$jscomp.scope = {};",
"$jscomp.scope.Casts = function() {};",
"$jscomp.scope.Casts.$to = function() { return 1; }",
"",
"alert(1);"))));
// Interface $markImplementor() functions.
test(
Lists.newArrayList(
SourceFile.fromCode(
"name/doesnt/matter/Foo.impl.java.js",
LINE_JOINER.join(
// Function definitions and calls are qualified globals.
"var $jscomp = {};",
"$jscomp.scope = {};",
"$jscomp.scope.FooInterface = function() {};",
"$jscomp.scope.FooInterface.$markImplementor = function(classDef) {",
" classDef.$implements__FooInterface = true;",
"}",
"",
"$jscomp.scope.Foo = function() {};",
"$jscomp.scope.FooInterface.$markImplementor($jscomp.scope.Foo);"))),
Lists.newArrayList(
SourceFile.fromCode(
"name/doesnt/matter/Foo.impl.java.js",
LINE_JOINER.join(
// Function definitions and calls are qualified globals.
"var $jscomp = {};",
"$jscomp.scope = {};",
"$jscomp.scope.FooInterface = function() {};",
"$jscomp.scope.FooInterface.$markImplementor = function(classDef) {",
" classDef.$implements__FooInterface = true;",
"}",
"",
"$jscomp.scope.Foo = function() {};",
"{$jscomp.scope.Foo.$implements__FooInterface = true;}"))));
}
public void testUnexpectedFunctionDoesntInline() {
// Arrays functions.
testDoesntChange(
Lists.newArrayList(
SourceFile.fromCode(
"j2cl/transpiler/vmbootstrap/Arrays.impl.java.js",
LINE_JOINER.join(
// Function definitions and calls are qualified globals.
"var Arrays = function() {};",
"Arrays.fooBar = function() { return 4; }",
"",
"alert(Arrays.fooBar());"))));
// Casts functions.
testDoesntChange(
Lists.newArrayList(
SourceFile.fromCode(
"j2cl/transpiler/vmbootstrap/Casts.impl.java.js",
LINE_JOINER.join(
// Function definitions and calls are qualified globals.
"var Casts = function() {};",
"Casts.fooBar = function() { return 4; }",
"",
"alert(Casts.fooBar());"))));
// No applicable for $markImplementor() inlining since it is not limited to just certain class
// files and so there are no specific files in which "other" functions should be ignored.
}
public void testUnqualifiedDoesntInline() {
// Arrays functions.
testDoesntChange(
Lists.newArrayList(
SourceFile.fromCode(
"j2cl/transpiler/vmbootstrap/Arrays.impl.java.js",
LINE_JOINER.join(
// Function definitions and calls are qualified globals.
"var $create = function() { return 1; }",
"var $init = function() { return 2; }",
"var $instanceIsOfType = function() { return 3; }",
"var $castTo = function() { return 4; }",
"",
"alert($create());",
"alert($init());",
"alert($instanceIsOfType());",
"alert($castTo());"))));
// Casts functions.
testDoesntChange(
Lists.newArrayList(
SourceFile.fromCode(
"j2cl/transpiler/vmbootstrap/Casts.impl.java.js",
LINE_JOINER.join(
// Function definitions and calls are qualified globals.
"var to = function() { return 1; }", "", "alert(to());"))));
// Interface $markImplementor() functions.
testDoesntChange(
Lists.newArrayList(
SourceFile.fromCode(
"name/doesnt/matter/Foo.impl.java.js",
LINE_JOINER.join(
// Function definitions and calls are qualified globals.
"var $markImplementor = function(classDef) {",
" classDef.$implements__FooInterface = true;",
"}",
"",
"var Foo = function() {};",
"$markImplementor(Foo);"))));
}
public void testWrongFileNameDoesntInline() {
// Arrays functions.
testDoesntChange(
Lists.newArrayList(
SourceFile.fromCode(
"Arrays.impl.java.js",
LINE_JOINER.join(
// Function definitions and calls are qualified globals.
"var Arrays = function() {};",
"Arrays.$create = function() { return 1; }",
"Arrays.$init = function() { return 2; }",
"Arrays.$instanceIsOfType = function() { return 3; }",
"Arrays.$castTo = function() { return 4; }",
"",
"alert(Arrays.$create());",
"alert(Arrays.$init());",
"alert(Arrays.$instanceIsOfType());",
"alert(Arrays.$castTo());"))));
// Casts functions.
testDoesntChange(
Lists.newArrayList(
SourceFile.fromCode(
"Casts.impl.java.js",
LINE_JOINER.join(
// Function definitions and calls are qualified globals.
"var Casts = function() {};",
"Casts.to = function() { return 1; }",
"",
"alert(Casts.to());"))));
// No applicable for $markImplementor() inlining since it is not limited to just certain class
// files.
}
public void testInlineNativeAlias() {
test(
LINE_JOINER.join(
"/** @constructor */ var $RegExp = window.RegExp;",
"var foo = new $RegExp('', '');"),
LINE_JOINER.join(
"/** @constructor */ var $RegExp = window.RegExp;",
"var foo = new window.RegExp('', '');"));
}
public void testInlineNativeAlias_const() {
setLanguage(LanguageMode.ECMASCRIPT_2015, LanguageMode.ECMASCRIPT5);
test(
LINE_JOINER.join(
"/** @constructor */ const $RegExp = window.RegExp;",
"const foo = new $RegExp('', '');"),
LINE_JOINER.join(
"/** @constructor */ const $RegExp = window.RegExp;",
"const foo = new window.RegExp('', '');"));
}
public void testInlineNativeAlias_let() {
setLanguage(LanguageMode.ECMASCRIPT_2015, LanguageMode.ECMASCRIPT5);
test(
LINE_JOINER.join(
"/** @constructor */ let $RegExp = window.RegExp;",
"let foo = new $RegExp('', '');"),
LINE_JOINER.join(
"/** @constructor */ let $RegExp = window.RegExp;",
"let foo = new window.RegExp('', '');"));
}
public void testInlineNativeAlias_notTopLevel() {
testSame(
LINE_JOINER.join(
"function x() {",
" /** @constructor */ var $RegExp = window.RegExp;",
" var foo = new $RegExp('', '');",
"}"));
}
public void testInlineNativeAlias_notConstructor() {
testSame(
LINE_JOINER.join(
"var $RegExp = window.RegExp;",
"var foo = new $RegExp('', '');"));
}
public void testInlineNativeAlias_redeclared() {
testSame(
LINE_JOINER.join(
"/** @constructor */ var $RegExp = window.RegExp;",
"function x() {",
" var $RegExp = function() {};",
" var foo = new $RegExp('', '');",
"}"));
}
}