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

312 lines
9 KiB
Java
Raw Normal View History

2023-04-25 11:33:41 +00:00
/*
* Copyright 2009 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.javascript.jscomp.AbstractCompiler.LifeCycleStage;
import com.google.javascript.jscomp.NodeTraversal.Callback;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import junit.framework.TestCase;
import java.util.Set;
/**
* @author johnlenz@google.com (John Lenz)
*/
public class FunctionToBlockMutatorTest extends TestCase {
public void testMutateNoReturnWithoutResultAssignment() {
helperMutate(
"function foo(){}; foo();",
"{}",
"foo");
}
public void testMutateNoReturnWithResultAssignment() {
helperMutate(
"function foo(){}; var result = foo();",
"{result = void 0}",
"foo", true, false);
}
public void testMutateNoValueReturnWithoutResultAssignment() {
helperMutate(
"function foo(){return;}; foo();",
"{}",
"foo", null);
}
public void testMutateNoValueReturnWithResultAssignment() {
helperMutate(
"function foo(){return;}; var result = foo();",
"{result = void 0}",
"foo");
}
public void testMutateValueReturnWithoutResultAssignment() {
helperMutate(
"function foo(){return true;}; foo();",
"{true;}",
"foo", null);
}
public void testMutateValueReturnWithResultAssignment() {
helperMutate(
"function foo(){return true;}; var x=foo();",
"{x=true}",
"foo", "x", true, false);
}
public void testMutateWithMultipleReturns() {
helperMutate(
"function foo(){ if (0) {return 0} else {return 1} };" +
"var result=foo();",
"{" +
"JSCompiler_inline_label_foo_0:{" +
"if(0) {" +
"result=0; break JSCompiler_inline_label_foo_0" +
"} else {" +
"result=1; break JSCompiler_inline_label_foo_0" +
"} result=void 0" +
"}" +
"}",
"foo", true, false);
}
public void testMutateWithParameters1() {
// Simple call with useless parameter
helperMutate(
"function foo(a){return true;}; foo(x);",
"{true}",
"foo", null);
}
public void testMutateWithParameters2() {
// Simple call with parameter
helperMutate(
"function foo(a){return x;}; foo(x);",
"{x}",
"foo", null);
}
public void testMutateWithParameters3() {
// Parameter has side-effects.
helperMutate(
"function foo(a){return a;}; " +
"function x() { foo(x++); }",
"{var a$$inline_0 = x++; a$$inline_0}",
"foo", null);
}
public void testMutate8() {
// Parameter has side-effects.
helperMutate(
"function foo(a){return a+a;}; foo(x++);",
"{var a$$inline_0 = x++;" +
"a$$inline_0 + a$$inline_0;}",
"foo", null);
}
public void testMutateInitializeUninitializedVars1() {
helperMutate(
"function foo(a){var b;return a;}; foo(1);",
"{var b$$inline_1=void 0;1}",
"foo", null, false, true);
}
public void testMutateInitializeUninitializedVars2() {
helperMutate(
"function foo(a){for(var b in c)return a;}; foo(1);",
"{JSCompiler_inline_label_foo_2:" +
"{" +
"for(var b$$inline_1 in c){" +
"1;break JSCompiler_inline_label_foo_2" +
"}" +
"}" +
"}",
"foo", null);
}
public void testMutateCallInLoopVars1() {
// baseline: outside a loop, the constant remains constant.
boolean callInLoop = false;
helperMutate(
"function foo(a){var B = bar(); a;}; foo(1);",
"{var B$$inline_1=bar(); 1;}",
"foo", null, false, callInLoop);
// ... in a loop, the constant-ness is removed.
// TODO(johnlenz): update this test to look for the const annotation.
callInLoop = true;
helperMutate(
"function foo(a){var B = bar(); a;}; foo(1);",
"{var B$$inline_1 = bar(); 1;}",
"foo", null, false, callInLoop);
}
public void testMutateFunctionDefinition() {
// function declarations are rewritten as function
// expressions
helperMutate(
"function foo(a){function g(){}}; foo(1);",
"{var g$$inline_1=function(){};}",
"foo", null);
}
public void helperMutate(
String code, final String expectedResult, final String fnName) {
helperMutate(code, expectedResult, fnName, false, false);
}
public void helperMutate(
String code, final String expectedResult, final String fnName,
final boolean needsDefaultResult,
final boolean isCallInLoop) {
helperMutate(code, expectedResult, fnName,
"result", needsDefaultResult, isCallInLoop);
}
public void helperMutate(
String code, final String expectedResult, final String fnName,
final String resultName) {
helperMutate(code, expectedResult, fnName, resultName, false, false);
}
private void validateSourceInfo(Compiler compiler, Node subtree) {
(new LineNumberCheck(compiler)).setCheckSubTree(subtree);
// Source information problems are reported as compiler errors.
if (compiler.getErrorCount() != 0) {
String msg = "Error encountered: ";
for (JSError err : compiler.getErrors()) {
msg += err.toString() + "\n";
}
assertTrue(msg, compiler.getErrorCount() == 0);
}
}
public void helperMutate(
String code, final String expectedResult, final String fnName,
final String resultName,
final boolean needsDefaultResult,
final boolean isCallInLoop) {
final Compiler compiler = new Compiler();
final FunctionToBlockMutator mutator = new FunctionToBlockMutator(
compiler, compiler.getUniqueNameIdSupplier());
Node expectedRoot = parse(compiler, expectedResult);
Preconditions.checkState(compiler.getErrorCount() == 0);
final Node expected = expectedRoot.getFirstChild();
final Node tree = parse(compiler, code);
Preconditions.checkState(compiler.getErrorCount() == 0);
Node externsRoot = new Node(Token.EMPTY);
Node mainRoot = tree;
MarkNoSideEffectCalls mark = new MarkNoSideEffectCalls(compiler);
mark.process(externsRoot, mainRoot);
final Node fnNode = findFunction(tree, fnName);
final Set<String> unsafe =
FunctionArgumentInjector.findModifiedParameters(fnNode);
// Fake precondition.
compiler.setLifeCycleStage(LifeCycleStage.NORMALIZED);
// inline tester
Method tester = new Method() {
@Override
public boolean call(NodeTraversal t, Node n, Node parent) {
Node result = mutator.mutate(
fnName, fnNode, n, resultName,
needsDefaultResult, isCallInLoop);
validateSourceInfo(compiler, result);
String explanation = expected.checkTreeEquals(result);
assertNull("\nExpected: " + compiler.toSource(expected) +
"\nResult: " + compiler.toSource(result) +
"\n" + explanation, explanation);
return true;
}
};
compiler.resetUniqueNameId();
TestCallback test = new TestCallback(fnName, tester);
NodeTraversal.traverse(compiler, tree, test);
}
interface Method {
boolean call(NodeTraversal t, Node n, Node parent);
}
class TestCallback implements Callback {
private final String callname;
private final Method method;
private boolean complete = false;
TestCallback(String callname, Method method) {
this.callname = callname;
this.method = method;
}
@Override
public boolean shouldTraverse(
NodeTraversal nodeTraversal, Node n, Node parent) {
return !complete;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isCall()) {
Node first = n.getFirstChild();
if (first.isName() &&
first.getString().equals(callname)) {
complete = method.call(t, n, parent);
}
}
if (parent == null) {
assertTrue(complete);
}
}
}
private static Node findFunction(Node n, String name) {
if (n.isFunction()) {
if (n.getFirstChild().getString().equals(name)) {
return n;
}
}
for (Node c : n.children()) {
Node result = findFunction(c, name);
if (result != null) {
return result;
}
}
return null;
}
private static Node parse(Compiler compiler, String js) {
Node n = compiler.parseTestCode(js);
assertEquals(0, compiler.getErrorCount());
return n;
}
}