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/ProcessDefinesTest.java
github-classroom[bot] e42e547e48
Initial commit
2023-04-25 11:33:41 +00:00

341 lines
11 KiB
Java

/*
* Copyright 2007 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.javascript.jscomp.SyntacticScopeCreator.VAR_MULTIPLY_DECLARED_ERROR;
import com.google.common.collect.Maps;
import com.google.javascript.jscomp.GlobalNamespace.Name;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import java.util.Map;
/**
* @author nicksantos@google.com (Nick Santos)
*/
public class ProcessDefinesTest extends CompilerTestCase {
public ProcessDefinesTest() {
super("var externMethod;");
// ProcessDefines emits warnings if the user tries to re-define a constant,
// but the constant is not defined anywhere in the binary.
allowSourcelessWarnings();
}
private Map<String, Node> overrides = Maps.newHashMap();
private GlobalNamespace namespace;
@Override
public void setUp() throws Exception {
super.setUp();
overrides.clear();
}
@Override
protected CompilerPass getProcessor(Compiler compiler) {
return new ProcessDefinesWithInjectedNamespace(compiler);
}
@Override
protected int getNumRepetitions() {
// Only do one repetition, so that we can make sure the first pass keeps
// GlobalNamespace up to date.
return 1;
}
/**
* Helper for tests that expects definitions to remain unchanged, such
* that {@code definitions+js} is converted to {@code definitions+expected}.
*/
private void testWithPrefix(String definitions, String js, String expected) {
test(definitions + js, definitions + expected);
}
public void testBasicDefine1() {
test("/** @define {boolean} */ var DEF = true", "var DEF=true");
}
public void testBasicDefine2() {
test("/** @define {string} */ var DEF = 'a'", "var DEF=\"a\"");
}
public void testBasicDefine3() {
test("/** @define {number} */ var DEF = 0", "var DEF=0");
}
public void testDefineBadType() {
test("/** @define {Object} */ var DEF = {}",
null, ProcessDefines.INVALID_DEFINE_TYPE_ERROR);
}
public void testDefineWithBadValue1() {
test("/** @define {boolean} */ var DEF = new Boolean(true);", null,
ProcessDefines.INVALID_DEFINE_INIT_ERROR);
}
public void testDefineWithBadValue2() {
test("/** @define {string} */ var DEF = 'x' + y;", null,
ProcessDefines.INVALID_DEFINE_INIT_ERROR);
}
public void testDefineWithDependentValue() {
test("/** @define {boolean} */ var BASE = false;\n" +
"/** @define {boolean} */ var DEF = !BASE;",
"var BASE=false;var DEF=!BASE");
test("var a = {};\n" +
"/** @define {boolean} */ a.BASE = false;\n" +
"/** @define {boolean} */ a.DEF = !a.BASE;",
"var a={};a.BASE=false;a.DEF=!a.BASE");
}
public void testDefineWithInvalidDependentValue() {
test("var BASE = false;\n" +
"/** @define {boolean} */ var DEF = !BASE;",
null,
ProcessDefines.INVALID_DEFINE_INIT_ERROR);
}
public void testOverriding1() {
overrides.put("DEF_OVERRIDE_TO_TRUE", new Node(Token.TRUE));
overrides.put("DEF_OVERRIDE_TO_FALSE", new Node(Token.FALSE));
test(
"/** @define {boolean} */ var DEF_OVERRIDE_TO_TRUE = false;" +
"/** @define {boolean} */ var DEF_OVERRIDE_TO_FALSE = true",
"var DEF_OVERRIDE_TO_TRUE=true;var DEF_OVERRIDE_TO_FALSE=false");
}
public void testOverriding2() {
overrides.put("DEF_OVERRIDE_TO_TRUE", new Node(Token.TRUE));
String normalConst = "var DEF_OVERRIDE_TO_FALSE=true;";
testWithPrefix(
normalConst,
"/** @define {boolean} */ var DEF_OVERRIDE_TO_TRUE = false",
"var DEF_OVERRIDE_TO_TRUE=true");
}
public void testOverriding3() {
overrides.put("DEF_OVERRIDE_TO_TRUE", new Node(Token.TRUE));
test(
"/** @define {boolean} */ var DEF_OVERRIDE_TO_TRUE = true;",
"var DEF_OVERRIDE_TO_TRUE=true");
}
public void testOverridingString0() {
test(
"/** @define {string} */ var DEF_OVERRIDE_STRING = 'x';",
"var DEF_OVERRIDE_STRING=\"x\"");
}
public void testOverridingString1() {
test(
"/** @define {string} */ var DEF_OVERRIDE_STRING = 'x' + 'y';",
"var DEF_OVERRIDE_STRING=\"x\" + \"y\"");
}
public void testOverridingString2() {
overrides.put("DEF_OVERRIDE_STRING", Node.newString("foo"));
test(
"/** @define {string} */ var DEF_OVERRIDE_STRING = 'x';",
"var DEF_OVERRIDE_STRING=\"foo\"");
}
public void testOverridingString3() {
overrides.put("DEF_OVERRIDE_STRING", Node.newString("foo"));
test(
"/** @define {string} */ var DEF_OVERRIDE_STRING = 'x' + 'y';",
"var DEF_OVERRIDE_STRING=\"foo\"");
}
public void testMisspelledOverride() {
overrides.put("DEF_BAD_OVERIDE", new Node(Token.TRUE));
test("/** @define {boolean} */ var DEF_BAD_OVERRIDE = true",
"var DEF_BAD_OVERRIDE=true", null,
ProcessDefines.UNKNOWN_DEFINE_WARNING);
}
public void testCompiledIsKnownDefine() {
overrides.put("COMPILED", new Node(Token.TRUE));
testSame("");
}
public void testSimpleReassign1() {
test("/** @define {boolean} */ var DEF = false; DEF = true;",
"var DEF=true;true");
}
public void testSimpleReassign2() {
test("/** @define {number|boolean} */ var DEF=false;DEF=true;DEF=3",
"var DEF=3;true;3");
Name def = namespace.getNameIndex().get("DEF");
assertEquals(1, def.getRefs().size());
assertEquals(1, def.globalSets);
assertNotNull(def.getDeclaration());
}
public void testSimpleReassign3() {
test("/** @define {boolean} */ var DEF = false;var x;x = DEF = true;",
"var DEF=true;var x;x=true");
}
public void testDuplicateVar() {
test("/** @define {boolean} */ var DEF = false; var DEF = true;",
null, VAR_MULTIPLY_DECLARED_ERROR);
}
public void testAssignBeforeDeclaration1() {
test("DEF=false;var b=false,/** @define {boolean} */DEF=true,c=false",
null, ProcessDefines.INVALID_DEFINE_INIT_ERROR);
}
public void testAssignBeforeDeclaration2() {
overrides.put("DEF_OVERRIDE_TO_TRUE", new Node(Token.TRUE));
test(
"DEF_OVERRIDE_TO_TRUE = 3;" +
"/** @define {boolean|number} */ var DEF_OVERRIDE_TO_TRUE = false;",
null, ProcessDefines.INVALID_DEFINE_INIT_ERROR);
}
public void testEmptyDeclaration() {
test("/** @define {boolean} */ var DEF;",
null, ProcessDefines.INVALID_DEFINE_INIT_ERROR);
}
public void testReassignAfterCall() {
test("/** @define {boolean} */var DEF=true;externMethod();DEF=false",
null, ProcessDefines.DEFINE_NOT_ASSIGNABLE_ERROR);
}
public void testReassignAfterRef() {
test("/** @define {boolean} */var DEF=true;var x = DEF;DEF=false",
null, ProcessDefines.DEFINE_NOT_ASSIGNABLE_ERROR);
}
public void testReassignWithExpr() {
test("/** @define {boolean} */var DEF=true;var x;DEF=x=false",
null, ProcessDefines.INVALID_DEFINE_INIT_ERROR);
}
public void testReassignAfterNonGlobalRef() {
test(
"/** @define {boolean} */var DEF=true;" +
"var x=function(){var y=DEF}; DEF=false",
"var DEF=false;var x=function(){var y=DEF};false");
Name def = namespace.getNameIndex().get("DEF");
assertEquals(2, def.getRefs().size());
assertEquals(1, def.globalSets);
assertNotNull(def.getDeclaration());
}
public void testReassignAfterRefInConditional() {
test(
"/** @define {boolean} */var DEF=true;" +
"if (false) {var x=DEF} DEF=false;",
null, ProcessDefines.DEFINE_NOT_ASSIGNABLE_ERROR);
}
public void testAssignInNonGlobalScope() {
test("/** @define {boolean} */var DEF=true;function foo() {DEF=false};",
null, ProcessDefines.NON_GLOBAL_DEFINE_INIT_ERROR);
}
public void testDeclareInNonGlobalScope() {
test("function foo() {/** @define {boolean} */var DEF=true;};",
null, ProcessDefines.NON_GLOBAL_DEFINE_INIT_ERROR);
}
public void testDefineAssignmentInLoop() {
test("/** @define {boolean} */var DEF=true;var x=0;while (x) {DEF=false;}",
null, ProcessDefines.NON_GLOBAL_DEFINE_INIT_ERROR);
}
public void testWithNoDefines() {
testSame("var DEF=true;var x={};x.foo={}");
}
public void testNamespacedDefine1() {
test("var a = {}; /** @define {boolean} */ a.B = false; a.B = true;",
"var a = {}; a.B = true; true;");
Name aDotB = namespace.getNameIndex().get("a.B");
assertEquals(1, aDotB.getRefs().size());
assertEquals(1, aDotB.globalSets);
assertNotNull(aDotB.getDeclaration());
}
public void testNamespacedDefine2a() {
overrides.put("a.B", new Node(Token.TRUE));
test("var a = {}; /** @define {boolean} */ a.B = false;",
"var a = {}; a.B = true;");
}
public void testNamespacedDefine2b() {
// TODO(johnlenz): We should either reject the define as invalid
// or replace its value.
overrides.put("a.B", new Node(Token.TRUE));
test("var a = { /** @define {boolean} */ B : false };",
"var a = {B : false};",
null, ProcessDefines.UNKNOWN_DEFINE_WARNING);
}
public void testNamespacedDefine2c() {
// TODO(johnlenz): We should either reject the define as invalid
// or replace its value.
overrides.put("a.B", new Node(Token.TRUE));
test("var a = { /** @define {boolean} */ get B() { return false } };",
"var a = {get B() { return false } };",
null, ProcessDefines.UNKNOWN_DEFINE_WARNING);
}
public void testNamespacedDefine3() {
overrides.put("a.B", new Node(Token.TRUE));
test("var a = {};", "var a = {};", null,
ProcessDefines.UNKNOWN_DEFINE_WARNING);
}
public void testNamespacedDefine4() {
overrides.put("a.B", new Node(Token.TRUE));
test("var a = {}; /** @define {boolean} */ a.B = false;",
"var a = {}; a.B = true;");
}
public void testOverrideAfterAlias() {
test("var x; /** @define {boolean} */var DEF=true; x=DEF; DEF=false;",
null, ProcessDefines.DEFINE_NOT_ASSIGNABLE_ERROR);
}
private class ProcessDefinesWithInjectedNamespace implements CompilerPass {
private final Compiler compiler;
public ProcessDefinesWithInjectedNamespace(Compiler compiler) {
this.compiler = compiler;
}
@Override
public void process(Node externs, Node js) {
namespace = new GlobalNamespace(compiler, js);
new ProcessDefines(compiler, overrides)
.injectNamespace(namespace)
.process(externs, js);
}
}
}