Skip to content

Commit

Permalink
Enforce that PureFunctionIdentifier runs over only normalized ASTs.
Browse files Browse the repository at this point in the history
This is a step towards running `PureFunctionIdentifer` using `OptimizeCalls` which also requires normalization to have run.

Enforcing normalization requires updating tests for passes that depend on pure function identification to also perform normalization. An upshot of this is that some test cases become redundant (and are deleted) when this is enforced.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=241393703
  • Loading branch information
nreid260 authored and blickly committed Apr 3, 2019
1 parent df9a5d6 commit 0225e6d
Show file tree
Hide file tree
Showing 11 changed files with 174 additions and 133 deletions.
2 changes: 2 additions & 0 deletions src/com/google/javascript/jscomp/NodeUtil.java
Expand Up @@ -1177,6 +1177,7 @@ private static boolean checkForStateChangeHelper(
break; break;


case NAME: case NAME:
// TODO(b/129564961): Consider EXPORT declarations.
if (n.hasChildren()) { if (n.hasChildren()) {
// This is the left side of a var/let/const // This is the left side of a var/let/const
return true; return true;
Expand Down Expand Up @@ -1616,6 +1617,7 @@ static boolean nodeTypeMayHaveSideEffects(Node n, AbstractCompiler compiler) {
return NodeUtil.constructorCallHasSideEffects(n); return NodeUtil.constructorCallHasSideEffects(n);
case NAME: case NAME:
// A variable definition. // A variable definition.
// TODO(b/129564961): Consider EXPORT declarations.
return n.hasChildren(); return n.hasChildren();
case REST: case REST:
case SPREAD: case SPREAD:
Expand Down
8 changes: 7 additions & 1 deletion src/com/google/javascript/jscomp/PeepholeRemoveDeadCode.java
Expand Up @@ -90,7 +90,7 @@ Node optimizeSubtree(Node subtree) {
case IF: case IF:
return tryFoldIf(subtree); return tryFoldIf(subtree);
case WHILE: case WHILE:
return tryFoldWhile(subtree); throw checkNormalization(false, "WHILE");
case FOR: case FOR:
{ {
Node condition = NodeUtil.getConditionExpression(subtree); Node condition = NodeUtil.getConditionExpression(subtree);
Expand Down Expand Up @@ -755,6 +755,7 @@ Node tryOptimizeBlock(Node n) {
for (Node c = n.getFirstChild(); c != null; ) { for (Node c = n.getFirstChild(); c != null; ) {
Node next = c.getNext(); // save c.next, since 'c' may be removed Node next = c.getNext(); // save c.next, since 'c' may be removed
if (!isUnremovableNode(c) && !mayHaveSideEffects(c)) { if (!isUnremovableNode(c) && !mayHaveSideEffects(c)) {
checkNormalization(!NodeUtil.isFunctionDeclaration(n), "function declaration");
// TODO(johnlenz): determine what this is actually removing. Candidates // TODO(johnlenz): determine what this is actually removing. Candidates
// include: EMPTY nodes, control structures without children // include: EMPTY nodes, control structures without children
// (removing infinite loops), empty try blocks. What else? // (removing infinite loops), empty try blocks. What else?
Expand Down Expand Up @@ -1269,4 +1270,9 @@ private void tryFoldForCondition(Node forCondition) {
forCondition.replaceWith(IR.empty()); forCondition.replaceWith(IR.empty());
} }
} }

private static IllegalStateException checkNormalization(boolean condition, String feature) {
checkState(condition, "Unexpected %s. AST should be normalized.", feature);
return null;
}
} }
Expand Up @@ -134,6 +134,7 @@ public PureFunctionIdentifier(


@Override @Override
public void process(Node externsAst, Node srcAst) { public void process(Node externsAst, Node srcAst) {
checkState(compiler.getLifeCycleStage().isNormalized());
checkState( checkState(
externs == null && root == null, externs == null && root == null,
"PureFunctionIdentifier::process may only be called once per instance."); "PureFunctionIdentifier::process may only be called once per instance.");
Expand Down
24 changes: 22 additions & 2 deletions src/com/google/javascript/jscomp/UnreachableCodeElimination.java
Expand Up @@ -19,7 +19,7 @@
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;


import com.google.javascript.jscomp.ControlFlowGraph.Branch; import com.google.javascript.jscomp.ControlFlowGraph.Branch;
import com.google.javascript.jscomp.NodeTraversal.AbstractShallowCallback; import com.google.javascript.jscomp.NodeTraversal.Callback;
import com.google.javascript.jscomp.NodeTraversal.ChangeScopeRootCallback; import com.google.javascript.jscomp.NodeTraversal.ChangeScopeRootCallback;
import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge; import com.google.javascript.jscomp.graph.DiGraph.DiGraphEdge;
import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode; import com.google.javascript.jscomp.graph.DiGraph.DiGraphNode;
Expand Down Expand Up @@ -59,6 +59,8 @@ class UnreachableCodeElimination implements CompilerPass {


@Override @Override
public void process(Node externs, Node toplevel) { public void process(Node externs, Node toplevel) {
checkState(compiler.getLifeCycleStage().isNormalized());

NodeTraversal.traverseChangedFunctions(compiler, new ChangeScopeRootCallback() { NodeTraversal.traverseChangedFunctions(compiler, new ChangeScopeRootCallback() {
@Override @Override
public void enterChangeScopeRoot(AbstractCompiler compiler, Node root) { public void enterChangeScopeRoot(AbstractCompiler compiler, Node root) {
Expand All @@ -80,12 +82,30 @@ public void enterChangeScopeRoot(AbstractCompiler compiler, Node root) {
}); });
} }


private class EliminationPass extends AbstractShallowCallback { private class EliminationPass implements Callback {
private final ControlFlowGraph<Node> cfg; private final ControlFlowGraph<Node> cfg;

private EliminationPass(ControlFlowGraph<Node> cfg) { private EliminationPass(ControlFlowGraph<Node> cfg) {
this.cfg = cfg; this.cfg = cfg;
} }


@Override
public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n, Node parent) {
if (parent == null) {
return true;
} else if (n.isExport()) {
// TODO(b/129564961): We should be exploring EXPORTs. We don't because their descendants
// have side-effects that `NodeUtil::mayHaveSideEffects` doesn't recognize. Since this pass
// currently runs after exports are removed anyway, this isn't yet an issue.
return false;
} else if (parent.isFunction()) {
// We only want to traverse the name of a function.
return n.isFirstChildOf(parent);
}

return true;
}

@Override @Override
public void visit(NodeTraversal t, Node n, Node parent) { public void visit(NodeTraversal t, Node n, Node parent) {
if (parent == null || n.isFunction() || n.isScript()) { if (parent == null || n.isFunction() || n.isScript()) {
Expand Down
4 changes: 2 additions & 2 deletions test/com/google/javascript/jscomp/FunctionInjectorTest.java
Expand Up @@ -1863,8 +1863,8 @@ public void helperInlineReferenceToFunction(
final Node expectedRoot = parseExpected(new Compiler(), expectedResult); final Node expectedRoot = parseExpected(new Compiler(), expectedResult);


Node mainRoot = tree; Node mainRoot = tree;
PureFunctionIdentifier.Driver mark = new PureFunctionIdentifier.Driver(compiler); new Normalize(compiler, false).process(externsRoot, mainRoot);
mark.process(externsRoot, mainRoot); new PureFunctionIdentifier.Driver(compiler).process(externsRoot, mainRoot);


Normalize normalize = new Normalize(compiler, false); Normalize normalize = new Normalize(compiler, false);
normalize.process(externsRoot, mainRoot); normalize.process(externsRoot, mainRoot);
Expand Down
24 changes: 11 additions & 13 deletions test/com/google/javascript/jscomp/FunctionToBlockMutatorTest.java
Expand Up @@ -20,7 +20,6 @@
import static com.google.javascript.jscomp.CompilerTestCase.lines; import static com.google.javascript.jscomp.CompilerTestCase.lines;
import static com.google.javascript.rhino.testing.NodeSubject.assertNode; import static com.google.javascript.rhino.testing.NodeSubject.assertNode;


import com.google.javascript.jscomp.AbstractCompiler.LifeCycleStage;
import com.google.javascript.jscomp.NodeTraversal.Callback; import com.google.javascript.jscomp.NodeTraversal.Callback;
import com.google.javascript.rhino.IR; import com.google.javascript.rhino.IR;
import com.google.javascript.rhino.Node; import com.google.javascript.rhino.Node;
Expand Down Expand Up @@ -163,12 +162,13 @@ public void testMutateInitializeUninitializedVars1() {
@Test @Test
public void testMutateInitializeUninitializedVars2() { public void testMutateInitializeUninitializedVars2() {
helperMutate( helperMutate(
"function foo(a) {for(var b in c)return a;}; foo(1);", "function foo(a) {var b; for(b in c)return a;}; foo(1);",
lines( lines(
"{", "{",
" JSCompiler_inline_label_foo_2:", " JSCompiler_inline_label_foo_2:",
" {", " {",
" for (var b$jscomp$inline_1 in c) {", " var b$jscomp$inline_1;",
" for (b$jscomp$inline_1 in c) {",
" 1;", " 1;",
" break JSCompiler_inline_label_foo_2;", " break JSCompiler_inline_label_foo_2;",
" }", " }",
Expand Down Expand Up @@ -256,11 +256,11 @@ public void testMutateFunctionDefinitionHoisting() {
"foo(1);"), "foo(1);"),
lines( lines(
"{", "{",
" var g$jscomp$inline_2 = function(c$jscomp$inline_6) {return c$jscomp$inline_6};", " var g$jscomp$inline_1 = function(c$jscomp$inline_6) {return c$jscomp$inline_6};",
" var h$jscomp$inline_4 = function(){};", " var h$jscomp$inline_2 = function(){};",
" var i$jscomp$inline_5 = function(){};", " var i$jscomp$inline_3 = function(){};",
" var b$jscomp$inline_1 = g$jscomp$inline_2(1);", " var b$jscomp$inline_4 = g$jscomp$inline_1(1);",
" var c$jscomp$inline_3 = i$jscomp$inline_5();", " var c$jscomp$inline_5 = i$jscomp$inline_3();",
"}"), "}"),
"foo", "foo",
null); null);
Expand Down Expand Up @@ -288,13 +288,11 @@ public void helperMutate(String code, String expectedResult, String fnName, Stri
compiler.externsRoot = new Node(Token.ROOT); compiler.externsRoot = new Node(Token.ROOT);
compiler.jsRoot = IR.root(script); compiler.jsRoot = IR.root(script);
compiler.externAndJsRoot = IR.root(compiler.externsRoot, compiler.jsRoot); compiler.externAndJsRoot = IR.root(compiler.externsRoot, compiler.jsRoot);
PureFunctionIdentifier.Driver mark = new PureFunctionIdentifier.Driver(compiler);
mark.process(compiler.externsRoot, compiler.jsRoot);


final Node fnNode = findFunction(script, fnName); new Normalize(compiler, false).process(compiler.externsRoot, compiler.jsRoot);
new PureFunctionIdentifier.Driver(compiler).process(compiler.externsRoot, compiler.jsRoot);


// Fake precondition. final Node fnNode = findFunction(script, fnName);
compiler.setLifeCycleStage(LifeCycleStage.NORMALIZED);


// inline tester // inline tester
Method tester = Method tester =
Expand Down
10 changes: 10 additions & 0 deletions test/com/google/javascript/jscomp/J2clClinitPrunerPassTest.java
Expand Up @@ -16,6 +16,7 @@
package com.google.javascript.jscomp; package com.google.javascript.jscomp;


import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import org.junit.Before;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
Expand Down Expand Up @@ -43,6 +44,15 @@ protected int getNumRepetitions() {
return 1; return 1;
} }


@Override
@Before
public void setUp() throws Exception {
super.setUp();

enableNormalize();
enableComputeSideEffects();
}

@Test @Test
public void testRemoveDuplicates() { public void testRemoveDuplicates() {
test( test(
Expand Down
30 changes: 4 additions & 26 deletions test/com/google/javascript/jscomp/PeepholeIntegrationTest.java
Expand Up @@ -32,6 +32,8 @@ public class PeepholeIntegrationTest extends CompilerTestCase {
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
super.setUp(); super.setUp();

enableNormalize();
this.late = false; this.late = false;
this.numRepetitions = 2; this.numRepetitions = 2;
} }
Expand Down Expand Up @@ -136,9 +138,6 @@ public void testBug1059649() {
// ensure that folding blocks with a single var node doesn't explode // ensure that folding blocks with a single var node doesn't explode
test("if(x){var y=3;}var z=5", "if(x)var y=3;var z=5"); test("if(x){var y=3;}var z=5", "if(x)var y=3;var z=5");


// With normalization, we no longer have this case.
testSame("if(x){var y=3;}else{var y=4;}var z=5");
test("while(x){var y=3;}var z=5", "while(x)var y=3;var z=5");
test("for(var i=0;i<10;i++){var y=3;}var z=5", test("for(var i=0;i<10;i++){var y=3;}var z=5",
"for(var i=0;i<10;i++)var y=3;var z=5"); "for(var i=0;i<10;i++)var y=3;var z=5");
test("for(var i in x){var y=3;}var z=5", test("for(var i in x){var y=3;}var z=5",
Expand Down Expand Up @@ -186,7 +185,7 @@ public void testFoldLogicalOpIntegration() {


@Test @Test
public void testFoldBitwiseOpStringCompareIntegration() { public void testFoldBitwiseOpStringCompareIntegration() {
test("while (-1 | 0) {}", "while (1);"); test("for (;-1 | 0;) {}", "for (;;);");
} }


@Test @Test
Expand All @@ -206,16 +205,6 @@ public void testBug1438784() {
test("for(var i=0;i<10;i++)if(x)x.y;", "for(var i=0;i<10;i++);"); test("for(var i=0;i<10;i++)if(x)x.y;", "for(var i=0;i<10;i++);");
} }


@Test
public void testFoldUselessWhileIntegration() {
test("while(!true) { foo() }", "");
test("while(!false) foo() ", "while(1) foo()");
test("while(!void 0) foo()", "while(1) foo()");

// Make sure proper empty nodes are inserted.
test("if(foo())while(false){foo()}else bar()", "foo()||bar()");
}

@Test @Test
public void testFoldUselessForIntegration() { public void testFoldUselessForIntegration() {
test("for(;!true;) { foo() }", ""); test("for(;!true;) { foo() }", "");
Expand All @@ -239,17 +228,6 @@ public void testFoldUselessDoIntegration() {
test("if(foo())do {foo()} while(false) else bar()", "foo()?foo():bar()"); test("if(foo())do {foo()} while(false) else bar()", "foo()?foo():bar()");
} }


@Test
public void testMinimizeWhileConstantConditionIntegration() {
test("while(!false) foo()", "while(1) foo()");
test("while(202) foo()", "while(1) foo()");
test("while(Infinity) foo()", "while(1) foo()");
test("while('text') foo()", "while(1) foo()");
test("while([]) foo()", "while(1) foo()");
test("while({}) foo()", "while(1) foo()");
test("while(/./) foo()", "while(1) foo()");
}

@Test @Test
public void testMinimizeExpr() { public void testMinimizeExpr() {
test("!!true", ""); test("!!true", "");
Expand Down Expand Up @@ -286,7 +264,7 @@ public void testBugIssue43() {


@Test @Test
public void testFoldNegativeBug() { public void testFoldNegativeBug() {
test("while(-3){};", "while(1);"); test("for (;-3;){};", "for (;;);");
} }


@Test @Test
Expand Down

0 comments on commit 0225e6d

Please sign in to comment.