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

546 lines
18 KiB
Java
Raw Normal View History

2023-04-25 11:33:41 +00:00
/*
* Copyright 2008 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.common.collect.Sets;
import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
import com.google.javascript.rhino.Node;
import java.util.Set;
/**
* @author johnlenz@google.com (John Lenz)
*
*/
public class NormalizeTest extends CompilerTestCase {
private static final String EXTERNS = "var window;";
public NormalizeTest() {
super(EXTERNS);
super.enableLineNumberCheck(true);
}
@Override
public CompilerPass getProcessor(final Compiler compiler) {
return new Normalize(compiler, false);
}
@Override
protected int getNumRepetitions() {
// The normalize pass is only run once.
return 1;
}
public void testSplitVar() {
testSame("var a");
test("var a, b",
"var a; var b");
test("var a, b, c",
"var a; var b; var c");
testSame("var a = 0 ");
test("var a = 0 , b = foo()",
"var a = 0; var b = foo()");
test("var a = 0, b = 1, c = 2",
"var a = 0; var b = 1; var c = 2");
test("var a = foo(1), b = foo(2), c = foo(3)",
"var a = foo(1); var b = foo(2); var c = foo(3)");
// Verify vars extracted from FOR nodes are split.
test("for(var a = 0, b = foo(1), c = 1; c < b; c++) foo(2)",
"var a = 0; var b = foo(1); var c = 1; for(; c < b; c++) foo(2)");
// Verify split vars properly introduce blocks when needed.
test("for(;;) var b = foo(1), c = foo(2);",
"for(;;){var b = foo(1); var c = foo(2)}");
test("for(;;){var b = foo(1), c = foo(2);}",
"for(;;){var b = foo(1); var c = foo(2)}");
test("try{var b = foo(1), c = foo(2);} finally foo(3);",
"try{var b = foo(1); var c = foo(2)} finally foo(3);");
test("try{var b = foo(1),c = foo(2);} finally;",
"try{var b = foo(1); var c = foo(2)} finally;");
test("try{foo(0);} finally var b = foo(1), c = foo(2);",
"try{foo(0);} finally {var b = foo(1); var c = foo(2)}");
test("switch(a) {default: var b = foo(1), c = foo(2); break;}",
"switch(a) {default: var b = foo(1); var c = foo(2); break;}");
test("do var a = foo(1), b; while(false);",
"do{var a = foo(1); var b} while(false);");
test("a:var a,b,c;",
"a:{ var a;var b; var c; }");
test("a:for(var a,b,c;;);",
"var a;var b; var c;a:for(;;);");
test("if (true) a:var a,b;",
"if (true)a:{ var a; var b; }");
}
public void testDuplicateVarInExterns() {
test("var extern;",
"/** @suppress {duplicate} */ var extern = 3;", "var extern = 3;",
null, null);
}
public void testUnhandled() {
testSame("var x = y = 1");
}
public void testFor() {
// Verify assignments are extracted from the FOR init node.
test("for(a = 0; a < 2 ; a++) foo();",
"a = 0; for(; a < 2 ; a++) foo()");
// Verify vars are extracted from the FOR init node.
test("for(var a = 0; c < b ; c++) foo()",
"var a = 0; for(; c < b ; c++) foo()");
// Verify vars are extracted from the FOR init before the label node.
test("a:for(var a = 0; c < b ; c++) foo()",
"var a = 0; a:for(; c < b ; c++) foo()");
// Verify vars are extracted from the FOR init before the labels node.
test("a:b:for(var a = 0; c < b ; c++) foo()",
"var a = 0; a:b:for(; c < b ; c++) foo()");
// Verify block are properly introduced for ifs.
test("if(x) for(var a = 0; c < b ; c++) foo()",
"if(x){var a = 0; for(; c < b ; c++) foo()}");
// Any other expression.
test("for(init(); a < 2 ; a++) foo();",
"init(); for(; a < 2 ; a++) foo()");
}
public void testForIn1() {
// Verify nothing happens with simple for-in
testSame("for(a in b) foo();");
// Verify vars are extracted from the FOR-IN node.
test("for(var a in b) foo()",
"var a; for(a in b) foo()");
// Verify vars are extracted from the FOR init before the label node.
test("a:for(var a in b) foo()",
"var a; a:for(a in b) foo()");
// Verify vars are extracted from the FOR init before the labels node.
test("a:b:for(var a in b) foo()",
"var a; a:b:for(a in b) foo()");
// Verify block are properly introduced for ifs.
test("if (x) for(var a in b) foo()",
"if (x) { var a; for(a in b) foo() }");
}
public void testForIn2() {
// Verify vars are extracted from the FOR-IN node.
test("for(var a = foo() in b) foo()",
"var a = foo(); for(a in b) foo()");
}
public void testWhile() {
// Verify while loops are converted to FOR loops.
test("while(c < b) foo()",
"for(; c < b;) foo()");
}
public void testMoveFunctions1() throws Exception {
test("function f() { if (x) return; foo(); function foo() {} }",
"function f() {function foo() {} if (x) return; foo(); }");
test("function f() { " +
"function foo() {} " +
"if (x) return;" +
"foo(); " +
"function bar() {} " +
"}",
"function f() {" +
"function foo() {}" +
"function bar() {}" +
"if (x) return;" +
"foo();" +
"}");
}
public void testMoveFunctions2() throws Exception {
testSame("function f() { function foo() {} }");
test("function f() { f(); a:function bar() {} }",
"function f() { f(); a:{ var bar = function () {} }}");
test("function f() { f(); {function bar() {}}}",
"function f() { f(); {var bar = function () {}}}");
test("function f() { f(); if (true) {function bar() {}}}",
"function f() { f(); if (true) {var bar = function () {}}}");
}
private String inFunction(String code) {
return "(function(){" + code + "})";
}
private void testSameInFunction(String code) {
testSame(inFunction(code));
}
private void testInFunction(String code, String expected) {
test(inFunction(code), inFunction(expected));
}
public void testNormalizeFunctionDeclarations() throws Exception {
testSame("function f() {}");
testSame("var f = function () {}");
test("var f = function f() {}",
"var f = function f$$1() {}");
testSame("var f = function g() {}");
test("a:function g() {}",
"a:{ var g = function () {} }");
test("{function g() {}}",
"{var g = function () {}}");
testSame("if (function g() {}) {}");
test("if (true) {function g() {}}",
"if (true) {var g = function () {}}");
test("if (true) {} else {function g() {}}",
"if (true) {} else {var g = function () {}}");
testSame("switch (function g() {}) {}");
test("switch (1) { case 1: function g() {}}",
"switch (1) { case 1: var g = function () {}}");
testSameInFunction("function f() {}");
testInFunction("f(); a:function g() {}",
"f(); a:{ var g = function () {} }");
testInFunction("f(); {function g() {}}",
"f(); {var g = function () {}}");
testInFunction("f(); if (true) {function g() {}}",
"f(); if (true) {var g = function () {}}");
testInFunction("if (true) {} else {function g() {}}",
"if (true) {} else {var g = function () {}}");
}
public void testMakeLocalNamesUnique() {
if (!Normalize.MAKE_LOCAL_NAMES_UNIQUE) {
return;
}
// Verify global names are untouched.
testSame("var a;");
// Verify global names are untouched.
testSame("a;");
// Local names are made unique.
test("var a;function foo(a){var b;a}",
"var a;function foo(a$$1){var b;a$$1}");
test("var a;function foo(){var b;a}function boo(){var b;a}",
"var a;function foo(){var b;a}function boo(){var b$$1;a}");
test("function foo(a){var b}" +
"function boo(a){var b}",
"function foo(a){var b}" +
"function boo(a$$1){var b$$1}");
// Verify function expressions are renamed.
test("var a = function foo(){foo()};var b = function foo(){foo()};",
"var a = function foo(){foo()};var b = function foo$$1(){foo$$1()};");
// Verify catch exceptions names are made unique
test("try { } catch(e) {e;}",
"try { } catch(e) {e;}");
test("try { } catch(e) {e;}; try { } catch(e) {e;}",
"try { } catch(e) {e;}; try { } catch(e$$1) {e$$1;}");
test("try { } catch(e) {e; try { } catch(e) {e;}};",
"try { } catch(e) {e; try { } catch(e$$1) {e$$1;} }; ");
// Verify the 1st global redefinition of extern definition is not removed.
test("/** @suppress {duplicate} */\nvar window;", "var window;");
// Verify the 2nd global redefinition of extern definition is removed.
test("/** @suppress {duplicate} */\nvar window;" +
"/** @suppress {duplicate} */\nvar window;", "var window;");
// Verify local masking extern made unique.
test("function f() {var window}",
"function f() {var window$$1}");
}
public void testRemoveDuplicateVarDeclarations1() {
test("function f() { var a; var a }",
"function f() { var a; }");
test("function f() { var a = 1; var a = 2 }",
"function f() { var a = 1; a = 2 }");
test("var a = 1; function f(){ var a = 2 }",
"var a = 1; function f(){ var a$$1 = 2 }");
test("function f() { var a = 1; lable1:var a = 2 }",
"function f() { var a = 1; lable1:{a = 2}}");
test("function f() { var a = 1; lable1:var a }",
"function f() { var a = 1; lable1:{} }");
test("function f() { var a = 1; for(var a in b); }",
"function f() { var a = 1; for(a in b); }");
}
public void testRemoveDuplicateVarDeclarations2() {
test("var e = 1; function f(){ try {} catch (e) {} var e = 2 }",
"var e = 1; function f(){ try {} catch (e$$2) {} var e$$1 = 2 }");
}
public void testRemoveDuplicateVarDeclarations3() {
test("var f = 1; function f(){}",
"f = 1; function f(){}");
test("var f; function f(){}",
"function f(){}");
test("if (a) { var f = 1; } else { function f(){} }",
"if (a) { var f = 1; } else { f = function (){} }");
test("function f(){} var f = 1;",
"function f(){} f = 1;");
test("function f(){} var f;",
"function f(){}");
test("if (a) { function f(){} } else { var f = 1; }",
"if (a) { var f = function (){} } else { f = 1; }");
// TODO(johnlenz): Do we need to handle this differently for "third_party"
// mode? Remove the previous function definitions?
test("function f(){} function f(){}",
"function f(){} function f(){}",
SyntacticScopeCreator.VAR_MULTIPLY_DECLARED_ERROR);
test("if (a) { function f(){} } else { function f(){} }",
"if (a) { var f = function (){} } else { f = function (){} }");
}
public void testRenamingConstants() {
test("var ACONST = 4;var b = ACONST;",
"var ACONST = 4; var b = ACONST;");
test("var a, ACONST = 4;var b = ACONST;",
"var a; var ACONST = 4; var b = ACONST;");
test("var ACONST; ACONST = 4; var b = ACONST;",
"var ACONST; ACONST = 4;" +
"var b = ACONST;");
test("var ACONST = new Foo(); var b = ACONST;",
"var ACONST = new Foo(); var b = ACONST;");
test("/** @const */var aa; aa=1;", "var aa;aa=1");
}
public void testSkipRenamingExterns() {
test("var EXTERN; var ext; ext.FOO;", "var b = EXTERN; var c = ext.FOO",
"var b = EXTERN; var c = ext.FOO", null, null);
}
public void testIssue166a() {
test("try { throw 1 } catch(e) { /** @suppress {duplicate} */ var e=2 }",
"try { throw 1 } catch(e) { var e=2 }",
Normalize.CATCH_BLOCK_VAR_ERROR);
}
public void testIssue166b() {
test("function a() {" +
"try { throw 1 } catch(e) { /** @suppress {duplicate} */ var e=2 }" +
"};",
"function a() {" +
"try { throw 1 } catch(e) { var e=2 }" +
"}",
Normalize.CATCH_BLOCK_VAR_ERROR);
}
public void testIssue166c() {
test("var e = 0; try { throw 1 } catch(e) {" +
"/** @suppress {duplicate} */ var e=2 }",
"var e = 0; try { throw 1 } catch(e) { var e=2 }",
Normalize.CATCH_BLOCK_VAR_ERROR);
}
public void testIssue166d() {
test("function a() {" +
"var e = 0; try { throw 1 } catch(e) {" +
"/** @suppress {duplicate} */ var e=2 }" +
"};",
"function a() {" +
"var e = 0; try { throw 1 } catch(e) { var e=2 }" +
"}",
Normalize.CATCH_BLOCK_VAR_ERROR);
}
public void testIssue166e() {
test("var e = 2; try { throw 1 } catch(e) {}",
"var e = 2; try { throw 1 } catch(e$$1) {}");
}
public void testIssue166f() {
test("function a() {" +
"var e = 2; try { throw 1 } catch(e) {}" +
"}",
"function a() {" +
"var e = 2; try { throw 1 } catch(e$$1) {}" +
"}");
}
public void testIssue() {
super.allowExternsChanges(true);
test("var a,b,c; var a,b", "a(), b()", "a(), b()", null, null);
}
public void testNormalizeSyntheticCode() {
Compiler compiler = new Compiler();
compiler.init(
Lists.<SourceFile>newArrayList(),
Lists.<SourceFile>newArrayList(), new CompilerOptions());
Node code = Normalize.parseAndNormalizeSyntheticCode(
compiler, "function f(x) {} function g(x) {}", "prefix_");
assertEquals(
"function f(x$$prefix_0){}function g(x$$prefix_1){}",
compiler.toSource(code));
}
public void testIsConstant() throws Exception {
testSame("var CONST = 3; var b = CONST;");
Node n = getLastCompiler().getRoot();
Set<Node> constantNodes = findNodesWithProperty(n, Node.IS_CONSTANT_NAME);
assertEquals(2, constantNodes.size());
for (Node hasProp : constantNodes) {
assertEquals("CONST", hasProp.getString());
}
}
public void testPropertyIsConstant1() throws Exception {
testSame("var a = {};a.CONST = 3; var b = a.CONST;");
Node n = getLastCompiler().getRoot();
Set<Node> constantNodes = findNodesWithProperty(n, Node.IS_CONSTANT_NAME);
assertEquals(2, constantNodes.size());
for (Node hasProp : constantNodes) {
assertEquals("CONST", hasProp.getString());
}
}
public void testPropertyIsConstant2() throws Exception {
testSame("var a = {CONST: 3}; var b = a.CONST;");
Node n = getLastCompiler().getRoot();
Set<Node> constantNodes = findNodesWithProperty(n, Node.IS_CONSTANT_NAME);
assertEquals(2, constantNodes.size());
for (Node hasProp : constantNodes) {
assertEquals("CONST", hasProp.getString());
}
}
public void testGetterPropertyIsConstant() throws Exception {
testSame("var a = { get CONST() {return 3} }; " +
"var b = a.CONST;");
Node n = getLastCompiler().getRoot();
Set<Node> constantNodes = findNodesWithProperty(n, Node.IS_CONSTANT_NAME);
assertEquals(2, constantNodes.size());
for (Node hasProp : constantNodes) {
assertEquals("CONST", hasProp.getString());
}
}
public void testSetterPropertyIsConstant() throws Exception {
// Verifying that a SET is properly annotated.
testSame("var a = { set CONST(b) {throw 'invalid'} }; " +
"var c = a.CONST;");
Node n = getLastCompiler().getRoot();
Set<Node> constantNodes = findNodesWithProperty(n, Node.IS_CONSTANT_NAME);
assertEquals(2, constantNodes.size());
for (Node hasProp : constantNodes) {
assertEquals("CONST", hasProp.getString());
}
}
public void testExposeSimple() {
test("var x = {}; /** @expose */ x.y = 3; x.y = 5;",
"var x = {}; x['y'] = 3; x['y'] = 5;");
}
public void testExposeComplex() {
test(
"var x = {/** @expose */ a: 1, b: 2};"
+ "x.a = 3; /** @expose */ x.b = 5;",
"var x = {'a': 1, 'b': 2};"
+ "x['a'] = 3; x['b'] = 5;");
}
private Set<Node> findNodesWithProperty(Node root, final int prop) {
final Set<Node> set = Sets.newHashSet();
NodeTraversal.traverse(
getLastCompiler(), root, new AbstractPostOrderCallback() {
@Override
public void visit(NodeTraversal t, Node node, Node parent) {
if (node.getBooleanProp(prop)) {
set.add(node);
}
}
});
return set;
}
public void testRenamingConstantProperties() {
// In order to detect that foo.BAR is a constant, we need collapse
// properties to run first so that we can tell if the initial value is
// non-null and immutable.
new WithCollapse().testConstantProperties();
}
private class WithCollapse extends CompilerTestCase {
WithCollapse() {
enableNormalize();
}
private void testConstantProperties() {
test("var a={}; a.ACONST = 4;var b = a.ACONST;",
"var a$ACONST = 4; var b = a$ACONST;");
test("var a={b:{}}; a.b.ACONST = 4;var b = a.b.ACONST;",
"var a$b$ACONST = 4;var b = a$b$ACONST;");
test("var a = {FOO: 1};var b = a.FOO;",
"var a$FOO = 1; var b = a$FOO;");
test("var EXTERN; var ext; ext.FOO;", "var b = EXTERN; var c = ext.FOO",
"var b = EXTERN; var c = ext.FOO", null, null);
test("var a={}; a.ACONST = 4; var b = a.ACONST;",
"var a$ACONST = 4; var b = a$ACONST;");
test("var a = {}; function foo() { var d = a.CONST; };" +
"(function(){a.CONST=4})();",
"var a$CONST;function foo(){var d = a$CONST;};" +
"(function(){a$CONST = 4})();");
test("var a = {}; a.ACONST = new Foo(); var b = a.ACONST;",
"var a$ACONST = new Foo(); var b = a$ACONST;");
}
@Override
protected int getNumRepetitions() {
// The normalize pass is only run once.
return 1;
}
@Override
public CompilerPass getProcessor(final Compiler compiler) {
return new CompilerPass() {
@Override
public void process(Node externs, Node root) {
new CollapseProperties(compiler, false, true).process(externs, root);
}
};
}
}
}