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/NodeUtilTest.java

1718 lines
64 KiB
Java
Raw Permalink Normal View History

2023-04-25 11:33:41 +00:00
/*
* Copyright 2004 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.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.javascript.jscomp.CompilerOptions.LanguageMode;
import com.google.javascript.rhino.IR;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import com.google.javascript.rhino.jstype.TernaryValue;
import junit.framework.TestCase;
import java.util.Collection;
import java.util.Set;
/**
* Tests for NodeUtil
*/
public class NodeUtilTest extends TestCase {
private static Node parse(String js) {
Compiler compiler = new Compiler();
compiler.initCompilerOptionsIfTesting();
compiler.getOptions().setLanguageIn(LanguageMode.ECMASCRIPT5);
Node n = compiler.parseTestCode(js);
assertEquals(0, compiler.getErrorCount());
return n;
}
static Node getNode(String js) {
Node root = parse("var a=(" + js + ");");
Node expr = root.getFirstChild();
Node var = expr.getFirstChild();
return var.getFirstChild();
}
public void testIsLiteralOrConstValue() {
assertLiteralAndImmutable(getNode("10"));
assertLiteralAndImmutable(getNode("-10"));
assertLiteralButNotImmutable(getNode("[10, 20]"));
assertLiteralButNotImmutable(getNode("{'a': 20}"));
assertLiteralButNotImmutable(getNode("[10, , 1.0, [undefined], 'a']"));
assertLiteralButNotImmutable(getNode("/abc/"));
assertLiteralAndImmutable(getNode("\"string\""));
assertLiteralAndImmutable(getNode("'aaa'"));
assertLiteralAndImmutable(getNode("null"));
assertLiteralAndImmutable(getNode("undefined"));
assertLiteralAndImmutable(getNode("void 0"));
assertNotLiteral(getNode("abc"));
assertNotLiteral(getNode("[10, foo(), 20]"));
assertNotLiteral(getNode("foo()"));
assertNotLiteral(getNode("c + d"));
assertNotLiteral(getNode("{'a': foo()}"));
assertNotLiteral(getNode("void foo()"));
}
public void assertLiteralAndImmutable(Node n) {
assertTrue(NodeUtil.isLiteralValue(n, true));
assertTrue(NodeUtil.isLiteralValue(n, false));
assertTrue(NodeUtil.isImmutableValue(n));
}
public void assertLiteralButNotImmutable(Node n) {
assertTrue(NodeUtil.isLiteralValue(n, true));
assertTrue(NodeUtil.isLiteralValue(n, false));
assertFalse(NodeUtil.isImmutableValue(n));
}
public void assertNotLiteral(Node n) {
assertFalse(NodeUtil.isLiteralValue(n, true));
assertFalse(NodeUtil.isLiteralValue(n, false));
assertFalse(NodeUtil.isImmutableValue(n));
}
public void testGetBooleanValue() {
assertPureBooleanTrue("true");
assertPureBooleanTrue("10");
assertPureBooleanTrue("'0'");
assertPureBooleanTrue("/a/");
assertPureBooleanTrue("{}");
assertPureBooleanTrue("[]");
assertPureBooleanFalse("false");
assertPureBooleanFalse("null");
assertPureBooleanFalse("0");
assertPureBooleanFalse("''");
assertPureBooleanFalse("undefined");
assertPureBooleanFalse("void 0");
assertPureBooleanUnknown("void foo()");
assertPureBooleanUnknown("b");
assertPureBooleanUnknown("-'0.0'");
// Known but getBooleanValue return false for expressions with side-effects
assertPureBooleanUnknown("{a:foo()}");
assertPureBooleanUnknown("[foo()]");
}
private void assertPureBooleanTrue(String val) {
assertEquals(TernaryValue.TRUE, NodeUtil.getPureBooleanValue(getNode(val)));
}
private void assertPureBooleanFalse(String val) {
assertEquals(
TernaryValue.FALSE, NodeUtil.getPureBooleanValue(getNode(val)));
}
private void assertPureBooleanUnknown(String val) {
assertEquals(
TernaryValue.UNKNOWN, NodeUtil.getPureBooleanValue(getNode(val)));
}
public void testGetExpressionBooleanValue() {
assertImpureBooleanTrue("a=true");
assertImpureBooleanFalse("a=false");
assertImpureBooleanTrue("a=(false,true)");
assertImpureBooleanFalse("a=(true,false)");
assertImpureBooleanTrue("a=(false || true)");
assertImpureBooleanFalse("a=(true && false)");
assertImpureBooleanTrue("a=!(true && false)");
assertImpureBooleanTrue("a,true");
assertImpureBooleanFalse("a,false");
assertImpureBooleanTrue("true||false");
assertImpureBooleanFalse("false||false");
assertImpureBooleanTrue("true&&true");
assertImpureBooleanFalse("true&&false");
assertImpureBooleanFalse("!true");
assertImpureBooleanTrue("!false");
assertImpureBooleanTrue("!''");
// Assignment ops other than ASSIGN are unknown.
assertImpureBooleanUnknown("a *= 2");
// Complex expressions that contain anything other then "=", ",", or "!" are
// unknown.
assertImpureBooleanUnknown("2 + 2");
assertImpureBooleanTrue("a=1");
assertImpureBooleanTrue("a=/a/");
assertImpureBooleanTrue("a={}");
assertImpureBooleanTrue("true");
assertImpureBooleanTrue("10");
assertImpureBooleanTrue("'0'");
assertImpureBooleanTrue("/a/");
assertImpureBooleanTrue("{}");
assertImpureBooleanTrue("[]");
assertImpureBooleanFalse("false");
assertImpureBooleanFalse("null");
assertImpureBooleanFalse("0");
assertImpureBooleanFalse("''");
assertImpureBooleanFalse("undefined");
assertImpureBooleanFalse("void 0");
assertImpureBooleanFalse("void foo()");
assertImpureBooleanTrue("a?true:true");
assertImpureBooleanFalse("a?false:false");
assertImpureBooleanUnknown("a?true:false");
assertImpureBooleanUnknown("a?true:foo()");
assertImpureBooleanUnknown("b");
assertImpureBooleanUnknown("-'0.0'");
assertImpureBooleanTrue("{a:foo()}");
assertImpureBooleanTrue("[foo()]");
}
private void assertImpureBooleanTrue(String val) {
assertEquals(TernaryValue.TRUE,
NodeUtil.getImpureBooleanValue(getNode(val)));
}
private void assertImpureBooleanFalse(String val) {
assertEquals(TernaryValue.FALSE,
NodeUtil.getImpureBooleanValue(getNode(val)));
}
private void assertImpureBooleanUnknown(String val) {
assertEquals(TernaryValue.UNKNOWN,
NodeUtil.getImpureBooleanValue(getNode(val)));
}
public void testGetStringValue() {
assertEquals("true", NodeUtil.getStringValue(getNode("true")));
assertEquals("10", NodeUtil.getStringValue(getNode("10")));
assertEquals("1", NodeUtil.getStringValue(getNode("1.0")));
assertEquals("0", NodeUtil.getStringValue(getNode("'0'")));
assertEquals(null, NodeUtil.getStringValue(getNode("/a/")));
assertEquals("[object Object]", NodeUtil.getStringValue(getNode("{}")));
assertEquals("", NodeUtil.getStringValue(getNode("[]")));
assertEquals("false", NodeUtil.getStringValue(getNode("false")));
assertEquals("null", NodeUtil.getStringValue(getNode("null")));
assertEquals("0", NodeUtil.getStringValue(getNode("0")));
assertEquals("", NodeUtil.getStringValue(getNode("''")));
assertEquals("undefined", NodeUtil.getStringValue(getNode("undefined")));
assertEquals("undefined", NodeUtil.getStringValue(getNode("void 0")));
assertEquals("undefined", NodeUtil.getStringValue(getNode("void foo()")));
assertEquals("NaN", NodeUtil.getStringValue(getNode("NaN")));
assertEquals("Infinity", NodeUtil.getStringValue(getNode("Infinity")));
assertEquals(null, NodeUtil.getStringValue(getNode("x")));
}
public void testGetArrayStringValue() {
assertEquals("", NodeUtil.getStringValue(getNode("[]")));
assertEquals("", NodeUtil.getStringValue(getNode("['']")));
assertEquals("", NodeUtil.getStringValue(getNode("[null]")));
assertEquals("", NodeUtil.getStringValue(getNode("[undefined]")));
assertEquals("", NodeUtil.getStringValue(getNode("[void 0]")));
assertEquals("NaN", NodeUtil.getStringValue(getNode("[NaN]")));
assertEquals(",", NodeUtil.getStringValue(getNode("[,'']")));
assertEquals(",,", NodeUtil.getStringValue(getNode("[[''],[''],['']]")));
assertEquals("1,2", NodeUtil.getStringValue(getNode("[[1.0],[2.0]]")));
assertEquals(null, NodeUtil.getStringValue(getNode("[a]")));
assertEquals(null, NodeUtil.getStringValue(getNode("[1,a]")));
}
public void testIsObjectLiteralKey1() throws Exception {
testIsObjectLiteralKey(
parseExpr("({})"), false);
testIsObjectLiteralKey(
parseExpr("a"), false);
testIsObjectLiteralKey(
parseExpr("'a'"), false);
testIsObjectLiteralKey(
parseExpr("1"), false);
testIsObjectLiteralKey(
parseExpr("({a: 1})").getFirstChild(), true);
testIsObjectLiteralKey(
parseExpr("({1: 1})").getFirstChild(), true);
testIsObjectLiteralKey(
parseExpr("({get a(){}})").getFirstChild(), true);
testIsObjectLiteralKey(
parseExpr("({set a(b){}})").getFirstChild(), true);
}
private Node parseExpr(String js) {
Compiler compiler = new Compiler();
CompilerOptions options = new CompilerOptions();
options.setLanguageIn(LanguageMode.ECMASCRIPT5);
compiler.initOptions(options);
Node root = compiler.parseTestCode(js);
return root.getFirstChild().getFirstChild();
}
private void testIsObjectLiteralKey(Node node, boolean expected) {
assertEquals(expected, NodeUtil.isObjectLitKey(node, node.getParent()));
}
public void testGetFunctionName1() throws Exception {
Compiler compiler = new Compiler();
Node parent = compiler.parseTestCode("function name(){}");
testGetFunctionName(parent.getFirstChild(), "name");
}
public void testGetFunctionName2() throws Exception {
Compiler compiler = new Compiler();
Node parent = compiler.parseTestCode("var name = function(){}")
.getFirstChild().getFirstChild();
testGetFunctionName(parent.getFirstChild(), "name");
}
public void testGetFunctionName3() throws Exception {
Compiler compiler = new Compiler();
Node parent = compiler.parseTestCode("qualified.name = function(){}")
.getFirstChild().getFirstChild();
testGetFunctionName(parent.getLastChild(), "qualified.name");
}
public void testGetFunctionName4() throws Exception {
Compiler compiler = new Compiler();
Node parent = compiler.parseTestCode("var name2 = function name1(){}")
.getFirstChild().getFirstChild();
testGetFunctionName(parent.getFirstChild(), "name2");
}
public void testGetFunctionName5() throws Exception {
Compiler compiler = new Compiler();
Node n = compiler.parseTestCode("qualified.name2 = function name1(){}");
Node parent = n.getFirstChild().getFirstChild();
testGetFunctionName(parent.getLastChild(), "qualified.name2");
}
private void testGetFunctionName(Node function, String name) {
assertEquals(Token.FUNCTION, function.getType());
assertEquals(name, NodeUtil.getFunctionName(function));
}
public void testContainsFunctionDeclaration() {
assertTrue(NodeUtil.containsFunction(
getNode("function foo(){}")));
assertTrue(NodeUtil.containsFunction(
getNode("(b?function(){}:null)")));
assertFalse(NodeUtil.containsFunction(
getNode("(b?foo():null)")));
assertFalse(NodeUtil.containsFunction(
getNode("foo()")));
}
private void assertSideEffect(boolean se, String js) {
Node n = parse(js);
assertEquals(se, NodeUtil.mayHaveSideEffects(n.getFirstChild()));
}
private void assertSideEffect(boolean se, String js, boolean globalRegExp) {
Node n = parse(js);
Compiler compiler = new Compiler();
compiler.setHasRegExpGlobalReferences(globalRegExp);
assertEquals(se, NodeUtil.mayHaveSideEffects(n.getFirstChild(), compiler));
}
public void testMayHaveSideEffects() {
assertSideEffect(true, "i++");
assertSideEffect(true, "[b, [a, i++]]");
assertSideEffect(true, "i=3");
assertSideEffect(true, "[0, i=3]");
assertSideEffect(true, "b()");
assertSideEffect(true, "[1, b()]");
assertSideEffect(true, "b.b=4");
assertSideEffect(true, "b.b--");
assertSideEffect(true, "i--");
assertSideEffect(true, "a[0][i=4]");
assertSideEffect(true, "a += 3");
assertSideEffect(true, "a, b, z += 4");
assertSideEffect(true, "a ? c : d++");
assertSideEffect(true, "a + c++");
assertSideEffect(true, "a + c - d()");
assertSideEffect(true, "a + c - d()");
assertSideEffect(true, "function foo() {}");
assertSideEffect(true, "while(true);");
assertSideEffect(true, "if(true){a()}");
assertSideEffect(false, "if(true){a}");
assertSideEffect(false, "(function() { })");
assertSideEffect(false, "(function() { i++ })");
assertSideEffect(false, "[function a(){}]");
assertSideEffect(false, "a");
assertSideEffect(false, "[b, c [d, [e]]]");
assertSideEffect(false, "({a: x, b: y, c: z})");
assertSideEffect(false, "/abc/gi");
assertSideEffect(false, "'a'");
assertSideEffect(false, "0");
assertSideEffect(false, "a + c");
assertSideEffect(false, "'c' + a[0]");
assertSideEffect(false, "a[0][1]");
assertSideEffect(false, "'a' + c");
assertSideEffect(false, "'a' + a.name");
assertSideEffect(false, "1, 2, 3");
assertSideEffect(false, "a, b, 3");
assertSideEffect(false, "(function(a, b) { })");
assertSideEffect(false, "a ? c : d");
assertSideEffect(false, "'1' + navigator.userAgent");
assertSideEffect(false, "new RegExp('foobar', 'i')");
assertSideEffect(true, "new RegExp(SomethingWacky(), 'i')");
assertSideEffect(false, "new Array()");
assertSideEffect(false, "new Array");
assertSideEffect(false, "new Array(4)");
assertSideEffect(false, "new Array('a', 'b', 'c')");
assertSideEffect(true, "new SomeClassINeverHeardOf()");
assertSideEffect(true, "new SomeClassINeverHeardOf()");
assertSideEffect(false, "({}).foo = 4");
assertSideEffect(false, "([]).foo = 4");
assertSideEffect(false, "(function() {}).foo = 4");
assertSideEffect(true, "this.foo = 4");
assertSideEffect(true, "a.foo = 4");
assertSideEffect(true, "(function() { return n; })().foo = 4");
assertSideEffect(true, "([]).foo = bar()");
assertSideEffect(false, "undefined");
assertSideEffect(false, "void 0");
assertSideEffect(true, "void foo()");
assertSideEffect(false, "-Infinity");
assertSideEffect(false, "Infinity");
assertSideEffect(false, "NaN");
assertSideEffect(false, "({}||[]).foo = 2;");
assertSideEffect(false, "(true ? {} : []).foo = 2;");
assertSideEffect(false, "({},[]).foo = 2;");
assertSideEffect(true, "delete a.b");
}
public void testObjectMethodSideEffects() {
// "toString" and "valueOf" are assumed to be side-effect free
assertSideEffect(false, "o.toString()");
assertSideEffect(false, "o.valueOf()");
// other methods depend on the extern definitions
assertSideEffect(true, "o.watch()");
}
public void testRegExpSideEffect() {
// A RegExp Object by itself doesn't have any side-effects
assertSideEffect(false, "/abc/gi", true);
assertSideEffect(false, "/abc/gi", false);
// RegExp instance methods have global side-effects, so whether they are
// considered side-effect free depends on whether the global properties
// are referenced.
assertSideEffect(true, "(/abc/gi).test('')", true);
assertSideEffect(false, "(/abc/gi).test('')", false);
assertSideEffect(true, "(/abc/gi).test(a)", true);
assertSideEffect(false, "(/abc/gi).test(b)", false);
assertSideEffect(true, "(/abc/gi).exec('')", true);
assertSideEffect(false, "(/abc/gi).exec('')", false);
// Some RegExp object method that may have side-effects.
assertSideEffect(true, "(/abc/gi).foo('')", true);
assertSideEffect(true, "(/abc/gi).foo('')", false);
// Try the string RegExp ops.
assertSideEffect(true, "''.match('a')", true);
assertSideEffect(false, "''.match('a')", false);
assertSideEffect(true, "''.match(/(a)/)", true);
assertSideEffect(false, "''.match(/(a)/)", false);
assertSideEffect(true, "''.replace('a')", true);
assertSideEffect(false, "''.replace('a')", false);
assertSideEffect(true, "''.search('a')", true);
assertSideEffect(false, "''.search('a')", false);
assertSideEffect(true, "''.split('a')", true);
assertSideEffect(false, "''.split('a')", false);
// Some non-RegExp string op that may have side-effects.
assertSideEffect(true, "''.foo('a')", true);
assertSideEffect(true, "''.foo('a')", false);
// 'a' might be a RegExp object with the 'g' flag, in which case
// the state might change by running any of the string ops.
// Specifically, using these methods resets the "lastIndex" if used
// in combination with a RegExp instance "exec" method.
assertSideEffect(true, "''.match(a)", true);
assertSideEffect(true, "''.match(a)", false);
}
private void assertMutableState(boolean se, String js) {
Node n = parse(js);
assertEquals(se, NodeUtil.mayEffectMutableState(n.getFirstChild()));
}
public void testMayEffectMutableState() {
assertMutableState(true, "i++");
assertMutableState(true, "[b, [a, i++]]");
assertMutableState(true, "i=3");
assertMutableState(true, "[0, i=3]");
assertMutableState(true, "b()");
assertMutableState(true, "void b()");
assertMutableState(true, "[1, b()]");
assertMutableState(true, "b.b=4");
assertMutableState(true, "b.b--");
assertMutableState(true, "i--");
assertMutableState(true, "a[0][i=4]");
assertMutableState(true, "a += 3");
assertMutableState(true, "a, b, z += 4");
assertMutableState(true, "a ? c : d++");
assertMutableState(true, "a + c++");
assertMutableState(true, "a + c - d()");
assertMutableState(true, "a + c - d()");
assertMutableState(true, "function foo() {}");
assertMutableState(true, "while(true);");
assertMutableState(true, "if(true){a()}");
assertMutableState(false, "if(true){a}");
assertMutableState(true, "(function() { })");
assertMutableState(true, "(function() { i++ })");
assertMutableState(true, "[function a(){}]");
assertMutableState(false, "a");
assertMutableState(true, "[b, c [d, [e]]]");
assertMutableState(true, "({a: x, b: y, c: z})");
// Note: RegExp objects are not immutable, for instance, the exec
// method maintains state for "global" searches.
assertMutableState(true, "/abc/gi");
assertMutableState(false, "'a'");
assertMutableState(false, "0");
assertMutableState(false, "a + c");
assertMutableState(false, "'c' + a[0]");
assertMutableState(false, "a[0][1]");
assertMutableState(false, "'a' + c");
assertMutableState(false, "'a' + a.name");
assertMutableState(false, "1, 2, 3");
assertMutableState(false, "a, b, 3");
assertMutableState(true, "(function(a, b) { })");
assertMutableState(false, "a ? c : d");
assertMutableState(false, "'1' + navigator.userAgent");
assertMutableState(true, "new RegExp('foobar', 'i')");
assertMutableState(true, "new RegExp(SomethingWacky(), 'i')");
assertMutableState(true, "new Array()");
assertMutableState(true, "new Array");
assertMutableState(true, "new Array(4)");
assertMutableState(true, "new Array('a', 'b', 'c')");
assertMutableState(true, "new SomeClassINeverHeardOf()");
}
public void testIsFunctionExpression() {
assertContainsAnonFunc(true, "(function(){})");
assertContainsAnonFunc(true, "[function a(){}]");
assertContainsAnonFunc(false, "{x: function a(){}}");
assertContainsAnonFunc(true, "(function a(){})()");
assertContainsAnonFunc(true, "x = function a(){};");
assertContainsAnonFunc(true, "var x = function a(){};");
assertContainsAnonFunc(true, "if (function a(){});");
assertContainsAnonFunc(true, "while (function a(){});");
assertContainsAnonFunc(true, "do; while (function a(){});");
assertContainsAnonFunc(true, "for (function a(){};;);");
assertContainsAnonFunc(true, "for (;function a(){};);");
assertContainsAnonFunc(true, "for (;;function a(){});");
assertContainsAnonFunc(true, "for (p in function a(){});");
assertContainsAnonFunc(true, "with (function a(){}) {}");
assertContainsAnonFunc(false, "function a(){}");
assertContainsAnonFunc(false, "if (x) function a(){};");
assertContainsAnonFunc(false, "if (x) { function a(){} }");
assertContainsAnonFunc(false, "if (x); else function a(){};");
assertContainsAnonFunc(false, "while (x) function a(){};");
assertContainsAnonFunc(false, "do function a(){} while (0);");
assertContainsAnonFunc(false, "for (;;) function a(){}");
assertContainsAnonFunc(false, "for (p in o) function a(){};");
assertContainsAnonFunc(false, "with (x) function a(){}");
}
private void assertContainsAnonFunc(boolean expected, String js) {
Node funcParent = findParentOfFuncDescendant(parse(js));
assertNotNull("Expected function node in parse tree of: " + js, funcParent);
Node funcNode = getFuncChild(funcParent);
assertEquals(expected, NodeUtil.isFunctionExpression(funcNode));
}
private Node findParentOfFuncDescendant(Node n) {
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
if (c.isFunction()) {
return n;
}
Node result = findParentOfFuncDescendant(c);
if (result != null) {
return result;
}
}
return null;
}
private Node getFuncChild(Node n) {
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
if (c.isFunction()) {
return c;
}
}
return null;
}
public void testContainsType() {
assertTrue(NodeUtil.containsType(
parse("this"), Token.THIS));
assertTrue(NodeUtil.containsType(
parse("function foo(){}(this)"), Token.THIS));
assertTrue(NodeUtil.containsType(
parse("b?this:null"), Token.THIS));
assertFalse(NodeUtil.containsType(
parse("a"), Token.THIS));
assertFalse(NodeUtil.containsType(
parse("function foo(){}"), Token.THIS));
assertFalse(NodeUtil.containsType(
parse("(b?foo():null)"), Token.THIS));
}
public void testReferencesThis() {
assertTrue(NodeUtil.referencesThis(
parse("this")));
// Don't descend into functions (starts at the script node)
assertFalse(NodeUtil.referencesThis(
parse("function foo(){this}")));
// But starting with a function properly check for 'this'
Node n = parse("function foo(){this}").getFirstChild();
assertEquals(n.getType(), Token.FUNCTION);
assertTrue(NodeUtil.referencesThis(n));
assertTrue(NodeUtil.referencesThis(
parse("b?this:null")));
assertFalse(NodeUtil.referencesThis(
parse("a")));
n = parse("function foo(){}").getFirstChild();
assertEquals(n.getType(), Token.FUNCTION);
assertFalse(NodeUtil.referencesThis(n));
assertFalse(NodeUtil.referencesThis(
parse("(b?foo():null)")));
}
public void testGetNodeTypeReferenceCount() {
assertEquals(0, NodeUtil.getNodeTypeReferenceCount(
parse("function foo(){}"), Token.THIS,
Predicates.<Node>alwaysTrue()));
assertEquals(1, NodeUtil.getNodeTypeReferenceCount(
parse("this"), Token.THIS,
Predicates.<Node>alwaysTrue()));
assertEquals(2, NodeUtil.getNodeTypeReferenceCount(
parse("this;function foo(){}(this)"), Token.THIS,
Predicates.<Node>alwaysTrue()));
}
public void testIsNameReferenceCount() {
assertTrue(NodeUtil.isNameReferenced(
parse("function foo(){}"), "foo"));
assertTrue(NodeUtil.isNameReferenced(
parse("var foo = function(){}"), "foo"));
assertFalse(NodeUtil.isNameReferenced(
parse("function foo(){}"), "undefined"));
assertTrue(NodeUtil.isNameReferenced(
parse("undefined"), "undefined"));
assertTrue(NodeUtil.isNameReferenced(
parse("undefined;function foo(){}(undefined)"), "undefined"));
assertTrue(NodeUtil.isNameReferenced(
parse("goo.foo"), "goo"));
assertFalse(NodeUtil.isNameReferenced(
parse("goo.foo"), "foo"));
}
public void testGetNameReferenceCount() {
assertEquals(0, NodeUtil.getNameReferenceCount(
parse("function foo(){}"), "undefined"));
assertEquals(1, NodeUtil.getNameReferenceCount(
parse("undefined"), "undefined"));
assertEquals(2, NodeUtil.getNameReferenceCount(
parse("undefined;function foo(){}(undefined)"), "undefined"));
assertEquals(1, NodeUtil.getNameReferenceCount(
parse("goo.foo"), "goo"));
assertEquals(0, NodeUtil.getNameReferenceCount(
parse("goo.foo"), "foo"));
assertEquals(1, NodeUtil.getNameReferenceCount(
parse("function foo(){}"), "foo"));
assertEquals(1, NodeUtil.getNameReferenceCount(
parse("var foo = function(){}"), "foo"));
}
public void testGetVarsDeclaredInBranch() {
Compiler compiler = new Compiler();
assertNodeNames(Sets.newHashSet("foo"),
NodeUtil.getVarsDeclaredInBranch(
parse("var foo;")));
assertNodeNames(Sets.newHashSet("foo", "goo"),
NodeUtil.getVarsDeclaredInBranch(
parse("var foo,goo;")));
assertNodeNames(Sets.<String>newHashSet(),
NodeUtil.getVarsDeclaredInBranch(
parse("foo();")));
assertNodeNames(Sets.<String>newHashSet(),
NodeUtil.getVarsDeclaredInBranch(
parse("function f(){var foo;}")));
assertNodeNames(Sets.newHashSet("goo"),
NodeUtil.getVarsDeclaredInBranch(
parse("var goo;function f(){var foo;}")));
}
private void assertNodeNames(Set<String> nodeNames, Collection<Node> nodes) {
Set<String> actualNames = Sets.newHashSet();
for (Node node : nodes) {
actualNames.add(node.getString());
}
assertEquals(nodeNames, actualNames);
}
public void testIsControlStructureCodeBlock() {
Node root = parse("if (x) foo(); else boo();");
Node ifNode = root.getFirstChild();
Node ifCondition = ifNode.getFirstChild();
Node ifCase = ifNode.getFirstChild().getNext();
Node elseCase = ifNode.getLastChild();
assertFalse(NodeUtil.isControlStructureCodeBlock(ifNode, ifCondition));
assertTrue(NodeUtil.isControlStructureCodeBlock(ifNode, ifCase));
assertTrue(NodeUtil.isControlStructureCodeBlock(ifNode, elseCase));
}
public void testIsFunctionExpression1() {
Node root = parse("(function foo() {})");
Node statementNode = root.getFirstChild();
assertTrue(statementNode.isExprResult());
Node functionNode = statementNode.getFirstChild();
assertTrue(functionNode.isFunction());
assertTrue(NodeUtil.isFunctionExpression(functionNode));
}
public void testIsFunctionExpression2() {
Node root = parse("function foo() {}");
Node functionNode = root.getFirstChild();
assertTrue(functionNode.isFunction());
assertFalse(NodeUtil.isFunctionExpression(functionNode));
}
public void testRemoveChildBlock() {
// Test removing the inner block.
Node actual = parse("{{x()}}");
Node outerBlockNode = actual.getFirstChild();
Node innerBlockNode = outerBlockNode.getFirstChild();
innerBlockNode.setIsSyntheticBlock(true);
NodeUtil.removeChild(outerBlockNode, innerBlockNode);
String expected = "{{}}";
String difference = parse(expected).checkTreeEquals(actual);
if (difference != null) {
assertTrue("Nodes do not match:\n" + difference, false);
}
}
public void testRemoveTryChild1() {
// Test removing the finally clause.
Node actual = parse("try {foo()} catch(e) {} finally {}");
Node tryNode = actual.getFirstChild();
Node tryBlock = tryNode.getFirstChild();
Node catchBlocks = tryNode.getFirstChild().getNext();
Node finallyBlock = tryNode.getLastChild();
NodeUtil.removeChild(tryNode, finallyBlock);
String expected = "try {foo()} catch(e) {}";
String difference = parse(expected).checkTreeEquals(actual);
if (difference != null) {
assertTrue("Nodes do not match:\n" + difference, false);
}
}
public void testRemoveTryChild2() {
// Test removing the try clause.
Node actual = parse("try {foo()} catch(e) {} finally {}");
Node tryNode = actual.getFirstChild();
Node tryBlock = tryNode.getFirstChild();
Node catchBlocks = tryNode.getFirstChild().getNext();
NodeUtil.removeChild(tryNode, tryBlock);
String expected = "try {} catch(e) {} finally {}";
String difference = parse(expected).checkTreeEquals(actual);
if (difference != null) {
assertTrue("Nodes do not match:\n" + difference, false);
}
}
public void testRemoveTryChild3() {
// Test removing the catch clause.
Node actual = parse("try {foo()} catch(e) {} finally {}");
Node tryNode = actual.getFirstChild();
Node tryBlock = tryNode.getFirstChild();
Node catchBlocks = tryNode.getFirstChild().getNext();
Node catchBlock = catchBlocks.getFirstChild();
Node finallyBlock = tryNode.getLastChild();
NodeUtil.removeChild(catchBlocks, catchBlock);
String expected = "try {foo()} finally {}";
String difference = parse(expected).checkTreeEquals(actual);
if (difference != null) {
assertTrue("Nodes do not match:\n" + difference, false);
}
}
public void testRemoveTryChild4() {
// Test removing the catch clause without a finally.
Node actual = parse("try {foo()} catch(e) {} finally {}");
Node tryNode = actual.getFirstChild();
Node tryBlock = tryNode.getFirstChild();
Node catchBlocks = tryNode.getFirstChild().getNext();
Node catchBlock = catchBlocks.getFirstChild();
Node finallyBlock = tryNode.getLastChild();
NodeUtil.removeChild(tryNode, catchBlocks);
String expected = "try {foo()} finally {}";
String difference = parse(expected).checkTreeEquals(actual);
if (difference != null) {
assertTrue("Nodes do not match:\n" + difference, false);
}
}
public void testRemoveTryChild5() {
Node actual = parse("try {foo()} catch(e) {} finally {}");
Node tryNode = actual.getFirstChild();
Node tryBlock = tryNode.getFirstChild();
Node catchBlocks = tryNode.getFirstChild().getNext();
Node catchBlock = catchBlocks.getFirstChild();
Node finallyBlock = tryNode.getLastChild();
NodeUtil.removeChild(catchBlocks, catchBlock);
String expected = "try {foo()} finally {}";
String difference = parse(expected).checkTreeEquals(actual);
if (difference != null) {
assertTrue("Nodes do not match:\n" + difference, false);
}
}
public void testRemoveVarChild() {
Compiler compiler = new Compiler();
// Test removing the first child.
Node actual = parse("var foo, goo, hoo");
Node varNode = actual.getFirstChild();
Node nameNode = varNode.getFirstChild();
NodeUtil.removeChild(varNode, nameNode);
String expected = "var goo, hoo";
String difference = parse(expected).checkTreeEquals(actual);
if (difference != null) {
assertTrue("Nodes do not match:\n" + difference, false);
}
// Test removing the second child.
actual = parse("var foo, goo, hoo");
varNode = actual.getFirstChild();
nameNode = varNode.getFirstChild().getNext();
NodeUtil.removeChild(varNode, nameNode);
expected = "var foo, hoo";
difference = parse(expected).checkTreeEquals(actual);
if (difference != null) {
assertTrue("Nodes do not match:\n" + difference, false);
}
// Test removing the last child of several children.
actual = parse("var foo, hoo");
varNode = actual.getFirstChild();
nameNode = varNode.getFirstChild().getNext();
NodeUtil.removeChild(varNode, nameNode);
expected = "var foo";
difference = parse(expected).checkTreeEquals(actual);
if (difference != null) {
assertTrue("Nodes do not match:\n" + difference, false);
}
// Test removing the last.
actual = parse("var hoo");
varNode = actual.getFirstChild();
nameNode = varNode.getFirstChild();
NodeUtil.removeChild(varNode, nameNode);
expected = "";
difference = parse(expected).checkTreeEquals(actual);
if (difference != null) {
assertTrue("Nodes do not match:\n" + difference, false);
}
}
public void testRemoveLabelChild1() {
Compiler compiler = new Compiler();
// Test removing the first child.
Node actual = parse("foo: goo()");
Node labelNode = actual.getFirstChild();
Node callExpressNode = labelNode.getLastChild();
NodeUtil.removeChild(labelNode, callExpressNode);
String expected = "";
String difference = parse(expected).checkTreeEquals(actual);
if (difference != null) {
assertTrue("Nodes do not match:\n" + difference, false);
}
}
public void testRemoveLabelChild2() {
// Test removing the first child.
Node actual = parse("achoo: foo: goo()");
Node labelNode = actual.getFirstChild();
Node callExpressNode = labelNode.getLastChild();
NodeUtil.removeChild(labelNode, callExpressNode);
String expected = "";
String difference = parse(expected).checkTreeEquals(actual);
if (difference != null) {
assertTrue("Nodes do not match:\n" + difference, false);
}
}
public void testRemoveForChild() {
Compiler compiler = new Compiler();
// Test removing the initializer.
Node actual = parse("for(var a=0;a<0;a++)foo()");
Node forNode = actual.getFirstChild();
Node child = forNode.getFirstChild();
NodeUtil.removeChild(forNode, child);
String expected = "for(;a<0;a++)foo()";
String difference = parse(expected).checkTreeEquals(actual);
assertNull("Nodes do not match:\n" + difference, difference);
// Test removing the condition.
actual = parse("for(var a=0;a<0;a++)foo()");
forNode = actual.getFirstChild();
child = forNode.getFirstChild().getNext();
NodeUtil.removeChild(forNode, child);
expected = "for(var a=0;;a++)foo()";
difference = parse(expected).checkTreeEquals(actual);
assertNull("Nodes do not match:\n" + difference, difference);
// Test removing the increment.
actual = parse("for(var a=0;a<0;a++)foo()");
forNode = actual.getFirstChild();
child = forNode.getFirstChild().getNext().getNext();
NodeUtil.removeChild(forNode, child);
expected = "for(var a=0;a<0;)foo()";
difference = parse(expected).checkTreeEquals(actual);
assertNull("Nodes do not match:\n" + difference, difference);
// Test removing the body.
actual = parse("for(var a=0;a<0;a++)foo()");
forNode = actual.getFirstChild();
child = forNode.getLastChild();
NodeUtil.removeChild(forNode, child);
expected = "for(var a=0;a<0;a++);";
difference = parse(expected).checkTreeEquals(actual);
assertNull("Nodes do not match:\n" + difference, difference);
// Test removing the body.
actual = parse("for(a in ack)foo();");
forNode = actual.getFirstChild();
child = forNode.getLastChild();
NodeUtil.removeChild(forNode, child);
expected = "for(a in ack);";
difference = parse(expected).checkTreeEquals(actual);
assertNull("Nodes do not match:\n" + difference, difference);
}
public void testMergeBlock1() {
Compiler compiler = new Compiler();
// Test removing the initializer.
Node actual = parse("{{a();b();}}");
Node parentBlock = actual.getFirstChild();
Node childBlock = parentBlock.getFirstChild();
assertTrue(NodeUtil.tryMergeBlock(childBlock));
String expected = "{a();b();}";
String difference = parse(expected).checkTreeEquals(actual);
assertNull("Nodes do not match:\n" + difference, difference);
}
public void testMergeBlock2() {
Compiler compiler = new Compiler();
// Test removing the initializer.
Node actual = parse("foo:{a();}");
Node parentLabel = actual.getFirstChild();
Node childBlock = parentLabel.getLastChild();
assertFalse(NodeUtil.tryMergeBlock(childBlock));
}
public void testMergeBlock3() {
Compiler compiler = new Compiler();
// Test removing the initializer.
String code = "foo:{a();boo()}";
Node actual = parse("foo:{a();boo()}");
Node parentLabel = actual.getFirstChild();
Node childBlock = parentLabel.getLastChild();
assertFalse(NodeUtil.tryMergeBlock(childBlock));
String expected = code;
String difference = parse(expected).checkTreeEquals(actual);
assertNull("Nodes do not match:\n" + difference, difference);
}
public void testGetSourceName() {
Node n = new Node(Token.BLOCK);
Node parent = new Node(Token.BLOCK, n);
parent.setSourceFileForTesting("foo");
assertEquals("foo", NodeUtil.getSourceName(n));
}
public void testLocalValue1() throws Exception {
// Names are not known to be local.
assertFalse(testLocalValue("x"));
assertFalse(testLocalValue("x()"));
assertFalse(testLocalValue("this"));
assertFalse(testLocalValue("arguments"));
// We can't know if new objects are local unless we know
// that they don't alias themselves.
assertFalse(testLocalValue("new x()"));
// property references are assume to be non-local
assertFalse(testLocalValue("(new x()).y"));
assertFalse(testLocalValue("(new x())['y']"));
// Primitive values are local
assertTrue(testLocalValue("null"));
assertTrue(testLocalValue("undefined"));
assertTrue(testLocalValue("Infinity"));
assertTrue(testLocalValue("NaN"));
assertTrue(testLocalValue("1"));
assertTrue(testLocalValue("'a'"));
assertTrue(testLocalValue("true"));
assertTrue(testLocalValue("false"));
assertTrue(testLocalValue("[]"));
assertTrue(testLocalValue("{}"));
// The contents of arrays and objects don't matter
assertTrue(testLocalValue("[x]"));
assertTrue(testLocalValue("{'a':x}"));
// Pre-increment results in primitive number
assertTrue(testLocalValue("++x"));
assertTrue(testLocalValue("--x"));
// Post-increment, the previous value matters.
assertFalse(testLocalValue("x++"));
assertFalse(testLocalValue("x--"));
// The left side of an only assign matters if it is an alias or mutable.
assertTrue(testLocalValue("x=1"));
assertFalse(testLocalValue("x=[]"));
assertFalse(testLocalValue("x=y"));
// The right hand side of assignment opts don't matter, as they force
// a local result.
assertTrue(testLocalValue("x+=y"));
assertTrue(testLocalValue("x*=y"));
// Comparisons always result in locals, as they force a local boolean
// result.
assertTrue(testLocalValue("x==y"));
assertTrue(testLocalValue("x!=y"));
assertTrue(testLocalValue("x>y"));
// Only the right side of a comma matters
assertTrue(testLocalValue("(1,2)"));
assertTrue(testLocalValue("(x,1)"));
assertFalse(testLocalValue("(x,y)"));
// Both the operands of OR matter
assertTrue(testLocalValue("1||2"));
assertFalse(testLocalValue("x||1"));
assertFalse(testLocalValue("x||y"));
assertFalse(testLocalValue("1||y"));
// Both the operands of AND matter
assertTrue(testLocalValue("1&&2"));
assertFalse(testLocalValue("x&&1"));
assertFalse(testLocalValue("x&&y"));
assertFalse(testLocalValue("1&&y"));
// Only the results of HOOK matter
assertTrue(testLocalValue("x?1:2"));
assertFalse(testLocalValue("x?x:2"));
assertFalse(testLocalValue("x?1:x"));
assertFalse(testLocalValue("x?x:y"));
// Results of ops are local values
assertTrue(testLocalValue("!y"));
assertTrue(testLocalValue("~y"));
assertTrue(testLocalValue("y + 1"));
assertTrue(testLocalValue("y + z"));
assertTrue(testLocalValue("y * z"));
assertTrue(testLocalValue("'a' in x"));
assertTrue(testLocalValue("typeof x"));
assertTrue(testLocalValue("x instanceof y"));
assertTrue(testLocalValue("void x"));
assertTrue(testLocalValue("void 0"));
assertFalse(testLocalValue("{}.x"));
assertTrue(testLocalValue("{}.toString()"));
assertTrue(testLocalValue("o.toString()"));
assertFalse(testLocalValue("o.valueOf()"));
assertTrue(testLocalValue("delete a.b"));
}
public void testLocalValue2() {
Node newExpr = getNode("new x()");
assertFalse(NodeUtil.evaluatesToLocalValue(newExpr));
Preconditions.checkState(newExpr.isNew());
Node.SideEffectFlags flags = new Node.SideEffectFlags();
flags.clearAllFlags();
newExpr.setSideEffectFlags(flags.valueOf());
assertTrue(NodeUtil.evaluatesToLocalValue(newExpr));
flags.clearAllFlags();
flags.setMutatesThis();
newExpr.setSideEffectFlags(flags.valueOf());
assertTrue(NodeUtil.evaluatesToLocalValue(newExpr));
flags.clearAllFlags();
flags.setReturnsTainted();
newExpr.setSideEffectFlags(flags.valueOf());
assertTrue(NodeUtil.evaluatesToLocalValue(newExpr));
flags.clearAllFlags();
flags.setThrows();
newExpr.setSideEffectFlags(flags.valueOf());
assertFalse(NodeUtil.evaluatesToLocalValue(newExpr));
flags.clearAllFlags();
flags.setMutatesArguments();
newExpr.setSideEffectFlags(flags.valueOf());
assertFalse(NodeUtil.evaluatesToLocalValue(newExpr));
flags.clearAllFlags();
flags.setMutatesGlobalState();
newExpr.setSideEffectFlags(flags.valueOf());
assertFalse(NodeUtil.evaluatesToLocalValue(newExpr));
}
public void testCallSideEffects() {
Node callExpr = getNode("new x().method()");
assertTrue(NodeUtil.functionCallHasSideEffects(callExpr));
Node newExpr = callExpr.getFirstChild().getFirstChild();
Preconditions.checkState(newExpr.isNew());
Node.SideEffectFlags flags = new Node.SideEffectFlags();
// No side effects, local result
flags.clearAllFlags();
newExpr.setSideEffectFlags(flags.valueOf());
flags.clearAllFlags();
callExpr.setSideEffectFlags(flags.valueOf());
assertTrue(NodeUtil.evaluatesToLocalValue(callExpr));
assertFalse(NodeUtil.functionCallHasSideEffects(callExpr));
assertFalse(NodeUtil.mayHaveSideEffects(callExpr));
// Modifies this, local result
flags.clearAllFlags();
newExpr.setSideEffectFlags(flags.valueOf());
flags.clearAllFlags();
flags.setMutatesThis();
callExpr.setSideEffectFlags(flags.valueOf());
assertTrue(NodeUtil.evaluatesToLocalValue(callExpr));
assertFalse(NodeUtil.functionCallHasSideEffects(callExpr));
assertFalse(NodeUtil.mayHaveSideEffects(callExpr));
// Modifies this, non-local result
flags.clearAllFlags();
newExpr.setSideEffectFlags(flags.valueOf());
flags.clearAllFlags();
flags.setMutatesThis();
flags.setReturnsTainted();
callExpr.setSideEffectFlags(flags.valueOf());
assertFalse(NodeUtil.evaluatesToLocalValue(callExpr));
assertFalse(NodeUtil.functionCallHasSideEffects(callExpr));
assertFalse(NodeUtil.mayHaveSideEffects(callExpr));
// No modifications, non-local result
flags.clearAllFlags();
newExpr.setSideEffectFlags(flags.valueOf());
flags.clearAllFlags();
flags.setReturnsTainted();
callExpr.setSideEffectFlags(flags.valueOf());
assertFalse(NodeUtil.evaluatesToLocalValue(callExpr));
assertFalse(NodeUtil.functionCallHasSideEffects(callExpr));
assertFalse(NodeUtil.mayHaveSideEffects(callExpr));
// The new modifies global state, no side-effect call, non-local result
// This call could be removed, but not the new.
flags.clearAllFlags();
flags.setMutatesGlobalState();
newExpr.setSideEffectFlags(flags.valueOf());
flags.clearAllFlags();
callExpr.setSideEffectFlags(flags.valueOf());
assertTrue(NodeUtil.evaluatesToLocalValue(callExpr));
assertFalse(NodeUtil.functionCallHasSideEffects(callExpr));
assertTrue(NodeUtil.mayHaveSideEffects(callExpr));
}
private boolean testLocalValue(String js) {
return NodeUtil.evaluatesToLocalValue(getNode(js));
}
public void testValidDefine() {
assertTrue(testValidDefineValue("1"));
assertTrue(testValidDefineValue("-3"));
assertTrue(testValidDefineValue("true"));
assertTrue(testValidDefineValue("false"));
assertTrue(testValidDefineValue("'foo'"));
assertFalse(testValidDefineValue("x"));
assertFalse(testValidDefineValue("null"));
assertFalse(testValidDefineValue("undefined"));
assertFalse(testValidDefineValue("NaN"));
assertTrue(testValidDefineValue("!true"));
assertTrue(testValidDefineValue("-true"));
assertTrue(testValidDefineValue("1 & 8"));
assertTrue(testValidDefineValue("1 + 8"));
assertTrue(testValidDefineValue("'a' + 'b'"));
assertFalse(testValidDefineValue("1 & foo"));
}
private boolean testValidDefineValue(String js) {
Node script = parse("var test = " + js + ";");
Node var = script.getFirstChild();
Node name = var.getFirstChild();
Node value = name.getFirstChild();
ImmutableSet<String> defines = ImmutableSet.of();
return NodeUtil.isValidDefineValue(value, defines);
}
public void testGetNumberValue() {
// Strings
assertEquals(1.0, NodeUtil.getNumberValue(getNode("'\\uFEFF1'")));
assertEquals(0.0, NodeUtil.getNumberValue(getNode("''")));
assertEquals(0.0, NodeUtil.getNumberValue(getNode("' '")));
assertEquals(0.0, NodeUtil.getNumberValue(getNode("' \\t'")));
assertEquals(0.0, NodeUtil.getNumberValue(getNode("'+0'")));
assertEquals(-0.0, NodeUtil.getNumberValue(getNode("'-0'")));
assertEquals(2.0, NodeUtil.getNumberValue(getNode("'+2'")));
assertEquals(-1.6, NodeUtil.getNumberValue(getNode("'-1.6'")));
assertEquals(16.0, NodeUtil.getNumberValue(getNode("'16'")));
assertEquals(16.0, NodeUtil.getNumberValue(getNode("' 16 '")));
assertEquals(16.0, NodeUtil.getNumberValue(getNode("' 16 '")));
assertEquals(12300.0, NodeUtil.getNumberValue(getNode("'123e2'")));
assertEquals(12300.0, NodeUtil.getNumberValue(getNode("'123E2'")));
assertEquals(1.23, NodeUtil.getNumberValue(getNode("'123e-2'")));
assertEquals(1.23, NodeUtil.getNumberValue(getNode("'123E-2'")));
assertEquals(-1.23, NodeUtil.getNumberValue(getNode("'-123e-2'")));
assertEquals(-1.23, NodeUtil.getNumberValue(getNode("'-123E-2'")));
assertEquals(1.23, NodeUtil.getNumberValue(getNode("'+123e-2'")));
assertEquals(1.23, NodeUtil.getNumberValue(getNode("'+123E-2'")));
assertEquals(12300.0, NodeUtil.getNumberValue(getNode("'+123e+2'")));
assertEquals(12300.0, NodeUtil.getNumberValue(getNode("'+123E+2'")));
assertEquals(15.0, NodeUtil.getNumberValue(getNode("'0xf'")));
assertEquals(15.0, NodeUtil.getNumberValue(getNode("'0xF'")));
// Chrome and rhino behavior differently from FF and IE. FF and IE
// consider a negative hex number to be invalid
assertEquals(null, NodeUtil.getNumberValue(getNode("'-0xf'")));
assertEquals(null, NodeUtil.getNumberValue(getNode("'-0xF'")));
assertEquals(null, NodeUtil.getNumberValue(getNode("'+0xf'")));
assertEquals(null, NodeUtil.getNumberValue(getNode("'+0xF'")));
assertEquals(16.0, NodeUtil.getNumberValue(getNode("'0X10'")));
assertEquals(Double.NaN, NodeUtil.getNumberValue(getNode("'0X10.8'")));
assertEquals(77.0, NodeUtil.getNumberValue(getNode("'077'")));
assertEquals(-77.0, NodeUtil.getNumberValue(getNode("'-077'")));
assertEquals(-77.5, NodeUtil.getNumberValue(getNode("'-077.5'")));
assertEquals(
Double.NEGATIVE_INFINITY,
NodeUtil.getNumberValue(getNode("'-Infinity'")));
assertEquals(
Double.POSITIVE_INFINITY,
NodeUtil.getNumberValue(getNode("'Infinity'")));
assertEquals(
Double.POSITIVE_INFINITY,
NodeUtil.getNumberValue(getNode("'+Infinity'")));
// Firefox treats "infinity" as "Infinity", IE treats it as NaN
assertEquals(null, NodeUtil.getNumberValue(getNode("'-infinity'")));
assertEquals(null, NodeUtil.getNumberValue(getNode("'infinity'")));
assertEquals(null, NodeUtil.getNumberValue(getNode("'+infinity'")));
assertEquals(Double.NaN, NodeUtil.getNumberValue(getNode("'NaN'")));
assertEquals(
Double.NaN, NodeUtil.getNumberValue(getNode("'some unknown string'")));
assertEquals(Double.NaN, NodeUtil.getNumberValue(getNode("'123 blah'")));
// Literals
assertEquals(1.0, NodeUtil.getNumberValue(getNode("1")));
// "-1" is parsed as a literal
assertEquals(-1.0, NodeUtil.getNumberValue(getNode("-1")));
// "+1" is parse as an op + literal
assertEquals(null, NodeUtil.getNumberValue(getNode("+1")));
assertEquals(22.0, NodeUtil.getNumberValue(getNode("22")));
assertEquals(18.0, NodeUtil.getNumberValue(getNode("022")));
assertEquals(34.0, NodeUtil.getNumberValue(getNode("0x22")));
assertEquals(
1.0, NodeUtil.getNumberValue(getNode("true")));
assertEquals(
0.0, NodeUtil.getNumberValue(getNode("false")));
assertEquals(
0.0, NodeUtil.getNumberValue(getNode("null")));
assertEquals(
Double.NaN, NodeUtil.getNumberValue(getNode("void 0")));
assertEquals(
Double.NaN, NodeUtil.getNumberValue(getNode("void f")));
// values with side-effects are ignored.
assertEquals(
null, NodeUtil.getNumberValue(getNode("void f()")));
assertEquals(
Double.NaN, NodeUtil.getNumberValue(getNode("NaN")));
assertEquals(
Double.POSITIVE_INFINITY,
NodeUtil.getNumberValue(getNode("Infinity")));
assertEquals(
Double.NEGATIVE_INFINITY,
NodeUtil.getNumberValue(getNode("-Infinity")));
// "infinity" is not a known name.
assertEquals(null, NodeUtil.getNumberValue(getNode("infinity")));
assertEquals(null, NodeUtil.getNumberValue(getNode("-infinity")));
// getNumberValue only converts literals
assertEquals(null, NodeUtil.getNumberValue(getNode("x")));
assertEquals(null, NodeUtil.getNumberValue(getNode("x.y")));
assertEquals(null, NodeUtil.getNumberValue(getNode("1/2")));
assertEquals(null, NodeUtil.getNumberValue(getNode("1-2")));
assertEquals(null, NodeUtil.getNumberValue(getNode("+1")));
}
public void testIsNumbericResult() {
assertTrue(NodeUtil.isNumericResult(getNode("1")));
assertFalse(NodeUtil.isNumericResult(getNode("true")));
assertTrue(NodeUtil.isNumericResult(getNode("+true")));
assertTrue(NodeUtil.isNumericResult(getNode("+1")));
assertTrue(NodeUtil.isNumericResult(getNode("-1")));
assertTrue(NodeUtil.isNumericResult(getNode("-Infinity")));
assertTrue(NodeUtil.isNumericResult(getNode("Infinity")));
assertTrue(NodeUtil.isNumericResult(getNode("NaN")));
assertFalse(NodeUtil.isNumericResult(getNode("undefined")));
assertFalse(NodeUtil.isNumericResult(getNode("void 0")));
assertTrue(NodeUtil.isNumericResult(getNode("a << b")));
assertTrue(NodeUtil.isNumericResult(getNode("a >> b")));
assertTrue(NodeUtil.isNumericResult(getNode("a >>> b")));
assertFalse(NodeUtil.isNumericResult(getNode("a == b")));
assertFalse(NodeUtil.isNumericResult(getNode("a != b")));
assertFalse(NodeUtil.isNumericResult(getNode("a === b")));
assertFalse(NodeUtil.isNumericResult(getNode("a !== b")));
assertFalse(NodeUtil.isNumericResult(getNode("a < b")));
assertFalse(NodeUtil.isNumericResult(getNode("a > b")));
assertFalse(NodeUtil.isNumericResult(getNode("a <= b")));
assertFalse(NodeUtil.isNumericResult(getNode("a >= b")));
assertFalse(NodeUtil.isNumericResult(getNode("a in b")));
assertFalse(NodeUtil.isNumericResult(getNode("a instanceof b")));
assertFalse(NodeUtil.isNumericResult(getNode("'a'")));
assertFalse(NodeUtil.isNumericResult(getNode("'a'+b")));
assertFalse(NodeUtil.isNumericResult(getNode("a+'b'")));
assertFalse(NodeUtil.isNumericResult(getNode("a+b")));
assertFalse(NodeUtil.isNumericResult(getNode("a()")));
assertFalse(NodeUtil.isNumericResult(getNode("''.a")));
assertFalse(NodeUtil.isNumericResult(getNode("a.b")));
assertFalse(NodeUtil.isNumericResult(getNode("a.b()")));
assertFalse(NodeUtil.isNumericResult(getNode("a().b()")));
assertFalse(NodeUtil.isNumericResult(getNode("new a()")));
// Definitely not numeric
assertFalse(NodeUtil.isNumericResult(getNode("([1,2])")));
assertFalse(NodeUtil.isNumericResult(getNode("({a:1})")));
// Recurse into the expression when necessary.
assertTrue(NodeUtil.isNumericResult(getNode("1 && 2")));
assertTrue(NodeUtil.isNumericResult(getNode("1 || 2")));
assertTrue(NodeUtil.isNumericResult(getNode("a ? 2 : 3")));
assertTrue(NodeUtil.isNumericResult(getNode("a,1")));
assertTrue(NodeUtil.isNumericResult(getNode("a=1")));
}
public void testIsBooleanResult() {
assertFalse(NodeUtil.isBooleanResult(getNode("1")));
assertTrue(NodeUtil.isBooleanResult(getNode("true")));
assertFalse(NodeUtil.isBooleanResult(getNode("+true")));
assertFalse(NodeUtil.isBooleanResult(getNode("+1")));
assertFalse(NodeUtil.isBooleanResult(getNode("-1")));
assertFalse(NodeUtil.isBooleanResult(getNode("-Infinity")));
assertFalse(NodeUtil.isBooleanResult(getNode("Infinity")));
assertFalse(NodeUtil.isBooleanResult(getNode("NaN")));
assertFalse(NodeUtil.isBooleanResult(getNode("undefined")));
assertFalse(NodeUtil.isBooleanResult(getNode("void 0")));
assertFalse(NodeUtil.isBooleanResult(getNode("a << b")));
assertFalse(NodeUtil.isBooleanResult(getNode("a >> b")));
assertFalse(NodeUtil.isBooleanResult(getNode("a >>> b")));
assertTrue(NodeUtil.isBooleanResult(getNode("a == b")));
assertTrue(NodeUtil.isBooleanResult(getNode("a != b")));
assertTrue(NodeUtil.isBooleanResult(getNode("a === b")));
assertTrue(NodeUtil.isBooleanResult(getNode("a !== b")));
assertTrue(NodeUtil.isBooleanResult(getNode("a < b")));
assertTrue(NodeUtil.isBooleanResult(getNode("a > b")));
assertTrue(NodeUtil.isBooleanResult(getNode("a <= b")));
assertTrue(NodeUtil.isBooleanResult(getNode("a >= b")));
assertTrue(NodeUtil.isBooleanResult(getNode("a in b")));
assertTrue(NodeUtil.isBooleanResult(getNode("a instanceof b")));
assertFalse(NodeUtil.isBooleanResult(getNode("'a'")));
assertFalse(NodeUtil.isBooleanResult(getNode("'a'+b")));
assertFalse(NodeUtil.isBooleanResult(getNode("a+'b'")));
assertFalse(NodeUtil.isBooleanResult(getNode("a+b")));
assertFalse(NodeUtil.isBooleanResult(getNode("a()")));
assertFalse(NodeUtil.isBooleanResult(getNode("''.a")));
assertFalse(NodeUtil.isBooleanResult(getNode("a.b")));
assertFalse(NodeUtil.isBooleanResult(getNode("a.b()")));
assertFalse(NodeUtil.isBooleanResult(getNode("a().b()")));
assertFalse(NodeUtil.isBooleanResult(getNode("new a()")));
assertTrue(NodeUtil.isBooleanResult(getNode("delete a")));
// Definitely not boolean
assertFalse(NodeUtil.isBooleanResult(getNode("([true,false])")));
assertFalse(NodeUtil.isBooleanResult(getNode("({a:true})")));
// These are boolean but aren't handled yet, "false" here means "unknown".
assertTrue(NodeUtil.isBooleanResult(getNode("true && false")));
assertTrue(NodeUtil.isBooleanResult(getNode("true || false")));
assertTrue(NodeUtil.isBooleanResult(getNode("a ? true : false")));
assertTrue(NodeUtil.isBooleanResult(getNode("a,true")));
assertTrue(NodeUtil.isBooleanResult(getNode("a=true")));
assertFalse(NodeUtil.isBooleanResult(getNode("a=1")));
}
public void testMayBeString() {
assertFalse(NodeUtil.mayBeString(getNode("1")));
assertFalse(NodeUtil.mayBeString(getNode("true")));
assertFalse(NodeUtil.mayBeString(getNode("+true")));
assertFalse(NodeUtil.mayBeString(getNode("+1")));
assertFalse(NodeUtil.mayBeString(getNode("-1")));
assertFalse(NodeUtil.mayBeString(getNode("-Infinity")));
assertFalse(NodeUtil.mayBeString(getNode("Infinity")));
assertFalse(NodeUtil.mayBeString(getNode("NaN")));
assertFalse(NodeUtil.mayBeString(getNode("undefined")));
assertFalse(NodeUtil.mayBeString(getNode("void 0")));
assertFalse(NodeUtil.mayBeString(getNode("null")));
assertFalse(NodeUtil.mayBeString(getNode("a << b")));
assertFalse(NodeUtil.mayBeString(getNode("a >> b")));
assertFalse(NodeUtil.mayBeString(getNode("a >>> b")));
assertFalse(NodeUtil.mayBeString(getNode("a == b")));
assertFalse(NodeUtil.mayBeString(getNode("a != b")));
assertFalse(NodeUtil.mayBeString(getNode("a === b")));
assertFalse(NodeUtil.mayBeString(getNode("a !== b")));
assertFalse(NodeUtil.mayBeString(getNode("a < b")));
assertFalse(NodeUtil.mayBeString(getNode("a > b")));
assertFalse(NodeUtil.mayBeString(getNode("a <= b")));
assertFalse(NodeUtil.mayBeString(getNode("a >= b")));
assertFalse(NodeUtil.mayBeString(getNode("a in b")));
assertFalse(NodeUtil.mayBeString(getNode("a instanceof b")));
assertTrue(NodeUtil.mayBeString(getNode("'a'")));
assertTrue(NodeUtil.mayBeString(getNode("'a'+b")));
assertTrue(NodeUtil.mayBeString(getNode("a+'b'")));
assertTrue(NodeUtil.mayBeString(getNode("a+b")));
assertTrue(NodeUtil.mayBeString(getNode("a()")));
assertTrue(NodeUtil.mayBeString(getNode("''.a")));
assertTrue(NodeUtil.mayBeString(getNode("a.b")));
assertTrue(NodeUtil.mayBeString(getNode("a.b()")));
assertTrue(NodeUtil.mayBeString(getNode("a().b()")));
assertTrue(NodeUtil.mayBeString(getNode("new a()")));
// These can't be strings but they aren't handled yet.
assertFalse(NodeUtil.mayBeString(getNode("1 && 2")));
assertFalse(NodeUtil.mayBeString(getNode("1 || 2")));
assertFalse(NodeUtil.mayBeString(getNode("1 ? 2 : 3")));
assertFalse(NodeUtil.mayBeString(getNode("1,2")));
assertFalse(NodeUtil.mayBeString(getNode("a=1")));
assertFalse(NodeUtil.mayBeString(getNode("1+1")));
assertFalse(NodeUtil.mayBeString(getNode("true+true")));
assertFalse(NodeUtil.mayBeString(getNode("null+null")));
assertFalse(NodeUtil.mayBeString(getNode("NaN+NaN")));
// These are not strings but they aren't primitives either
assertTrue(NodeUtil.mayBeString(getNode("([1,2])")));
assertTrue(NodeUtil.mayBeString(getNode("({a:1})")));
assertTrue(NodeUtil.mayBeString(getNode("({}+1)")));
assertTrue(NodeUtil.mayBeString(getNode("(1+{})")));
assertTrue(NodeUtil.mayBeString(getNode("([]+1)")));
assertTrue(NodeUtil.mayBeString(getNode("(1+[])")));
}
public void testValidNames() {
assertTrue(NodeUtil.isValidPropertyName("a"));
assertTrue(NodeUtil.isValidPropertyName("a3"));
assertFalse(NodeUtil.isValidPropertyName("3a"));
assertFalse(NodeUtil.isValidPropertyName("a."));
assertFalse(NodeUtil.isValidPropertyName(".a"));
assertFalse(NodeUtil.isValidPropertyName("a.b"));
assertFalse(NodeUtil.isValidPropertyName("true"));
assertFalse(NodeUtil.isValidPropertyName("a.true"));
assertFalse(NodeUtil.isValidPropertyName("a..b"));
assertTrue(NodeUtil.isValidSimpleName("a"));
assertTrue(NodeUtil.isValidSimpleName("a3"));
assertFalse(NodeUtil.isValidSimpleName("3a"));
assertFalse(NodeUtil.isValidSimpleName("a."));
assertFalse(NodeUtil.isValidSimpleName(".a"));
assertFalse(NodeUtil.isValidSimpleName("a.b"));
assertFalse(NodeUtil.isValidSimpleName("true"));
assertFalse(NodeUtil.isValidSimpleName("a.true"));
assertFalse(NodeUtil.isValidSimpleName("a..b"));
assertTrue(NodeUtil.isValidQualifiedName("a"));
assertTrue(NodeUtil.isValidQualifiedName("a3"));
assertFalse(NodeUtil.isValidQualifiedName("3a"));
assertFalse(NodeUtil.isValidQualifiedName("a."));
assertFalse(NodeUtil.isValidQualifiedName(".a"));
assertTrue(NodeUtil.isValidQualifiedName("a.b"));
assertFalse(NodeUtil.isValidQualifiedName("true"));
assertFalse(NodeUtil.isValidQualifiedName("a.true"));
assertFalse(NodeUtil.isValidQualifiedName("a..b"));
}
public void testGetNearestFunctionName() {
testFunctionName("(function() {})()", null);
testFunctionName("function a() {}", "a");
testFunctionName("(function a() {})", "a");
testFunctionName("({a:function () {}})", "a");
testFunctionName("({get a() {}})", "a");
testFunctionName("({set a(b) {}})", "a");
testFunctionName("({set a(b) {}})", "a");
testFunctionName("({1:function () {}})", "1");
testFunctionName("var a = function a() {}", "a");
testFunctionName("var a;a = function a() {}", "a");
testFunctionName("var o;o.a = function a() {}", "o.a");
testFunctionName("this.a = function a() {}", "this.a");
}
public void testGetBestLValue() {
assertEquals("x", getFunctionLValue("var x = function() {};"));
assertEquals("x", getFunctionLValue("x = function() {};"));
assertEquals("x", getFunctionLValue("function x() {};"));
assertEquals("x", getFunctionLValue("var x = y ? z : function() {};"));
assertEquals("x", getFunctionLValue("var x = y ? function() {} : z;"));
assertEquals("x", getFunctionLValue("var x = y && function() {};"));
assertEquals("x", getFunctionLValue("var x = y || function() {};"));
assertEquals("x", getFunctionLValue("var x = (y, function() {});"));
}
public void testIsNaN() {
assertEquals(true, NodeUtil.isNaN(getNode("NaN")));
assertEquals(false, NodeUtil.isNaN(getNode("Infinity")));
assertEquals(false, NodeUtil.isNaN(getNode("x")));
assertEquals(true, NodeUtil.isNaN(getNode("0/0")));
assertEquals(false, NodeUtil.isNaN(getNode("1/0")));
assertEquals(false, NodeUtil.isNaN(getNode("0/1")));
assertEquals(false, NodeUtil.isNaN(IR.number(0.0)));
}
public void testIsExecutedExactlyOnce() {
assertEquals(true, executedOnceTestCase("x;"));
assertEquals(true, executedOnceTestCase("x && 1;"));
assertEquals(false, executedOnceTestCase("1 && x;"));
assertEquals(false, executedOnceTestCase("1 && (x && 1);"));
assertEquals(true, executedOnceTestCase("x || 1;"));
assertEquals(false, executedOnceTestCase("1 || x;"));
assertEquals(false, executedOnceTestCase("1 && (x || 1);"));
assertEquals(true, executedOnceTestCase("x ? 1 : 2;"));
assertEquals(false, executedOnceTestCase("1 ? 1 : x;"));
assertEquals(false, executedOnceTestCase("1 ? x : 2;"));
assertEquals(false, executedOnceTestCase("1 && (x ? 1 : 2);"));
assertEquals(true, executedOnceTestCase("if (x) {}"));
assertEquals(false, executedOnceTestCase("if (true) {x;}"));
assertEquals(false, executedOnceTestCase("if (true) {} else {x;}"));
assertEquals(false, executedOnceTestCase("if (1) { if (x) {} }"));
assertEquals(true, executedOnceTestCase("for(x;;){}"));
assertEquals(false, executedOnceTestCase("for(;x;){}"));
assertEquals(false, executedOnceTestCase("for(;;x){}"));
assertEquals(false, executedOnceTestCase("for(;;){x;}"));
assertEquals(false, executedOnceTestCase("if (1) { for(x;;){} }"));
assertEquals(false, executedOnceTestCase("for(x in {}){}"));
assertEquals(true, executedOnceTestCase("for({}.a in x){}"));
assertEquals(false, executedOnceTestCase("for({}.a in {}){x}"));
assertEquals(false, executedOnceTestCase("if (1) { for(x in {}){} }"));
assertEquals(true, executedOnceTestCase("switch (x) {}"));
assertEquals(false, executedOnceTestCase("switch (1) {case x:}"));
assertEquals(false, executedOnceTestCase("switch (1) {case 1: x}"));
assertEquals(false, executedOnceTestCase("switch (1) {default: x}"));
assertEquals(false, executedOnceTestCase("if (1) { switch (x) {} }"));
assertEquals(false, executedOnceTestCase("while (x) {}"));
assertEquals(false, executedOnceTestCase("while (1) {x}"));
assertEquals(false, executedOnceTestCase("do {} while (x)"));
assertEquals(false, executedOnceTestCase("do {x} while (1)"));
assertEquals(false, executedOnceTestCase("try {x} catch (e) {}"));
assertEquals(false, executedOnceTestCase("try {} catch (e) {x}"));
assertEquals(true, executedOnceTestCase("try {} finally {x}"));
assertEquals(false, executedOnceTestCase("if (1) { try {} finally {x} }"));
}
private boolean executedOnceTestCase(String code) {
Node ast = parse(code);
Node nameNode = getNameNode(ast, "x");
return NodeUtil.isExecutedExactlyOnce(nameNode);
}
private String getFunctionLValue(String js) {
Node lVal = NodeUtil.getBestLValue(getFunctionNode(js));
return lVal == null ? null : lVal.getString();
}
static void testFunctionName(String js, String expected) {
assertEquals(
expected,
NodeUtil.getNearestFunctionName(getFunctionNode(js)));
}
static Node getFunctionNode(String js) {
Node root = parse(js);
return getFunctionNode(root);
}
static Node getFunctionNode(Node n) {
if (n.isFunction()) {
return n;
}
for (Node c : n.children()) {
Node result = getFunctionNode(c);
if (result != null) {
return result;
}
}
return null;
}
static Node getNameNode(Node n, String name) {
if (n.isName() && n.getString().equals(name)) {
return n;
}
for (Node c : n.children()) {
Node result = getNameNode(c, name);
if (result != null) {
return result;
}
}
return null;
}
}