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

1598 lines
55 KiB
Java

/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.javascript.jscomp.AbstractCompiler.LifeCycleStage;
import com.google.javascript.jscomp.FunctionInjector.CanInlineResult;
import com.google.javascript.jscomp.FunctionInjector.InliningMode;
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.List;
import java.util.Set;
/**
* Inline function tests.
* @author johnlenz@google.com (John Lenz)
*/
public class FunctionInjectorTest extends TestCase {
static final InliningMode INLINE_DIRECT = InliningMode.DIRECT;
static final InliningMode INLINE_BLOCK = InliningMode.BLOCK;
private boolean assumeStrictThis = false;
private boolean assumeMinimumCapture = false;
@Override
protected void setUp() throws Exception {
super.setUp();
assumeStrictThis = false;
}
private FunctionInjector getInjector() {
Compiler compiler = new Compiler();
return new FunctionInjector(
compiler, compiler.getUniqueNameIdSupplier(), true,
assumeStrictThis, assumeMinimumCapture);
}
public void testIsSimpleFunction1() {
assertTrue(getInjector().isDirectCallNodeReplacementPossible(
prep("function f(){}")));
}
public void testIsSimpleFunction2() {
assertTrue(getInjector().isDirectCallNodeReplacementPossible(
prep("function f(){return 0;}")));
}
public void testIsSimpleFunction3() {
assertTrue(getInjector().isDirectCallNodeReplacementPossible(
prep("function f(){return x ? 0 : 1}")));
}
public void testIsSimpleFunction4() {
assertFalse(getInjector().isDirectCallNodeReplacementPossible(
prep("function f(){return;}")));
}
public void testIsSimpleFunction5() {
assertFalse(getInjector().isDirectCallNodeReplacementPossible(
prep("function f(){return 0; return 0;}")));
}
public void testIsSimpleFunction6() {
assertFalse(getInjector().isDirectCallNodeReplacementPossible(
prep("function f(){var x=true;return x ? 0 : 1}")));
}
public void testIsSimpleFunction7() {
assertFalse(getInjector().isDirectCallNodeReplacementPossible(
prep("function f(){if (x) return 0; else return 1}")));
}
public void testCanInlineReferenceToFunction1() {
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(){}; foo();", "foo", INLINE_DIRECT);
}
public void testCanInlineReferenceToFunction2() {
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(){}; foo();", "foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunction3() {
// NOTE: FoldConstants will convert this to a empty function,
// so there is no need to explicitly support it.
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(){return;}; foo();", "foo", INLINE_DIRECT);
}
public void testCanInlineReferenceToFunction4() {
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(){return;}; foo();", "foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunction5() {
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(){return true;}; foo();", "foo", INLINE_DIRECT);
}
public void testCanInlineReferenceToFunction6() {
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(){return true;}; foo();", "foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunction7() {
// In var initialization.
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(){return true;}; var x=foo();", "foo", INLINE_DIRECT);
}
public void testCanInlineReferenceToFunction8() {
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(){return true;}; var x=foo();", "foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunction9() {
// In assignment.
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(){return true;}; var x; x=foo();", "foo", INLINE_DIRECT);
}
public void testCanInlineReferenceToFunction10() {
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(){return true;}; var x; x=foo();", "foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunction11() {
// In expression.
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(){return true;}; var x; x=x+foo();", "foo",
INLINE_DIRECT);
}
public void testCanInlineReferenceToFunction12() {
// "foo" is not known to be side-effect free, it might change the value
// of "x", so it can't be inlined.
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(){return true;}; var x; x=x+foo();", "foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunction12b() {
// "foo" is not known to be side-effect free, it might change the value
// of "x", so it can't be inlined.
helperCanInlineReferenceToFunction(
CanInlineResult.AFTER_PREPARATION,
"function foo(){return true;}; var x; x=x+foo();",
"foo", INLINE_BLOCK, true);
}
// TODO(nicksantos): Re-enable with side-effect detection.
// public void testCanInlineReferenceToFunction13() {
// // ... if foo is side-effect free we can inline here.
// helperCanInlineReferenceToFunction(true,
// "/** @nosideeffects */ function foo(){return true;};" +
// "var x; x=x+foo();", "foo", INLINE_BLOCK);
// }
public void testCanInlineReferenceToFunction14() {
// Simple call with parameters
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(a){return true;}; foo(x);", "foo", INLINE_DIRECT);
}
public void testCanInlineReferenceToFunction15() {
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(a){return true;}; foo(x);", "foo", INLINE_BLOCK);
}
// TODO(johnlenz): remove this constant once this has been proven in
// production code.
static final CanInlineResult NEW_VARS_IN_GLOBAL_SCOPE =
CanInlineResult.YES;
public void testCanInlineReferenceToFunction16() {
// Function "foo" as it contains "var b" which
// must be brought into the global scope.
helperCanInlineReferenceToFunction(NEW_VARS_IN_GLOBAL_SCOPE,
"function foo(a){var b;return a;}; foo(goo());", "foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunction17() {
// This doesn't bring names into the global name space.
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(a){return a;}; " +
"function x() { foo(goo()); }",
"foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunction18() {
// Parameter has side-effects.
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(a){return a;} foo(x++);", "foo", INLINE_DIRECT);
}
public void testCanInlineReferenceToFunction19() {
// Parameter has mutable parameter referenced more than once.
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(a){return a+a} foo([]);", "foo", INLINE_DIRECT);
}
public void testCanInlineReferenceToFunction20() {
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(a){return a+a} foo({});", "foo", INLINE_DIRECT);
}
public void testCanInlineReferenceToFunction21() {
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(a){return a+a} foo(new Date);", "foo", INLINE_DIRECT);
}
public void testCanInlineReferenceToFunction22() {
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(a){return a+a} foo(true && new Date);", "foo",
INLINE_DIRECT);
}
public void testCanInlineReferenceToFunction23() {
// variables to global scope.
helperCanInlineReferenceToFunction(NEW_VARS_IN_GLOBAL_SCOPE,
"function foo(a){return a;}; foo(x++);", "foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunction24() {
// ... this is OK, because it doesn't introduce a new global name.
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(a){return a;}; " +
"function x() { foo(x++); }",
"foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunction25() {
// Parameter has side-effects.
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(a){return a+a;}; foo(x++);", "foo", INLINE_DIRECT);
}
public void testCanInlineReferenceToFunction26() {
helperCanInlineReferenceToFunction(NEW_VARS_IN_GLOBAL_SCOPE,
"function foo(a){return a+a;}; foo(x++);", "foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunction27() {
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(a){return a+a;}; " +
"function x() { foo(x++); }",
"foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunction28() {
// Parameter has side-effects.
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(a){return true;}; foo(goo());", "foo", INLINE_DIRECT);
}
public void testCanInlineReferenceToFunction29() {
helperCanInlineReferenceToFunction(NEW_VARS_IN_GLOBAL_SCOPE,
"function foo(a){return true;}; foo(goo());", "foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunction30() {
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(a){return true;}; " +
"function x() { foo(goo()); }",
"foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunction31() {
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(a) {return true;}; " +
"function x() {foo.call(this, 1);}",
"foo", INLINE_DIRECT);
}
public void testCanInlineReferenceToFunction32() {
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(a){return true;}; " +
"function x() { foo.apply(this, [1]); }",
"foo", INLINE_DIRECT);
}
public void testCanInlineReferenceToFunction33() {
// No special handling is required for method calls passing this.
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(a){return true;}; " +
"function x() { foo.bar(this, 1); }",
"foo", INLINE_DIRECT);
}
public void testCanInlineReferenceToFunction34() {
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(a){return true;}; " +
"function x() { foo.call(this, goo()); }",
"foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunction35() {
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(a){return true;}; " +
"function x() { foo.apply(this, goo()); }",
"foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunction36() {
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(a){return true;}; " +
"function x() { foo.bar(this, goo()); }",
"foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunction37() {
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(a){return true;}; " +
"function x() { foo.call(null, 1); }",
"foo", INLINE_DIRECT);
}
public void testCanInlineReferenceToFunction38() {
assumeStrictThis = false;
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(a){return true;}; " +
"function x() { foo.call(null, goo()); }",
"foo", INLINE_BLOCK);
assumeStrictThis = true;
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(a){return true;}; " +
"function x() { foo.call(null, goo()); }",
"foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunction39() {
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(a){return true;}; " +
"function x() { foo.call(bar, 1); }",
"foo", INLINE_DIRECT);
}
public void testCanInlineReferenceToFunction40() {
assumeStrictThis = false;
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(a){return true;}; " +
"function x() { foo.call(bar, goo()); }",
"foo", INLINE_BLOCK);
assumeStrictThis = true;
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(a){return true;}; " +
"function x() { foo.call(bar, goo()); }",
"foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunction41() {
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(a){return true;}; " +
"function x() { foo.call(new bar(), 1); }",
"foo", INLINE_DIRECT);
}
public void testCanInlineReferenceToFunction42() {
assumeStrictThis = false;
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(a){return true;}; " +
"function x() { foo.call(new bar(), goo()); }",
"foo", INLINE_BLOCK);
assumeStrictThis = true;
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(a){return true;}; " +
"function x() { foo.call(new bar(), goo()); }",
"foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunction43() {
// Handle the case of a missing 'this' value in a call.
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(){return true;}; " +
"function x() { foo.call(); }",
"foo", INLINE_DIRECT);
}
public void testCanInlineReferenceToFunction44() {
assumeStrictThis = false;
// Handle the case of a missing 'this' value in a call.
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(){return true;}; " +
"function x() { foo.call(); }",
"foo", INLINE_BLOCK);
assumeStrictThis = true;
// Handle the case of a missing 'this' value in a call.
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(){return true;}; " +
"function x() { foo.call(); }",
"foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunction45() {
// Call with inner function expression.
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(){return function() {return true;}}; foo();",
"foo", INLINE_DIRECT);
}
public void testCanInlineReferenceToFunction46() {
// Call with inner function expression.
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(){return function() {return true;}}; foo();",
"foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunction47() {
// Call with inner function expression and variable decl.
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(){var a; return function() {return true;}}; foo();",
"foo", INLINE_DIRECT);
}
public void testCanInlineReferenceToFunction48() {
// Call with inner function expression and variable decl.
// TODO(johnlenz): should we validate no values in scope?
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(){var a; return function() {return true;}}; foo();",
"foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunction49() {
// Call with inner function expression.
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(){return function() {var a; return true;}}; foo();",
"foo", INLINE_DIRECT);
}
public void testCanInlineReferenceToFunction50() {
// Call with inner function expression.
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(){return function() {var a; return true;}}; foo();",
"foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunction51() {
// Call with inner function statement.
helperCanInlineReferenceToFunction(CanInlineResult.YES,
"function foo(){function x() {var a; return true;} return x}; foo();",
"foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunctionInExpression1() {
// Call in if condition
helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION,
"function foo(a){return true;}; " +
"function x() { if (foo(1)) throw 'test'; }",
"foo", INLINE_BLOCK, true);
}
public void testCanInlineReferenceToFunctionInExpression2() {
// Call in return expression
helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION,
"function foo(a){return true;}; " +
"function x() { return foo(1); }",
"foo", INLINE_BLOCK, true);
}
public void testCanInlineReferenceToFunctionInExpression3() {
// Call in switch expression
helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION,
"function foo(a){return true;}; " +
"function x() { switch(foo(1)) { default:break; } }",
"foo", INLINE_BLOCK, true);
}
public void testCanInlineReferenceToFunctionInExpression4() {
// Call in hook condition
helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION,
"function foo(a){return true;}; " +
"function x() {foo(1)?0:1 }",
"foo", INLINE_BLOCK, true);
}
public void testCanInlineReferenceToFunctionInExpression5() {
// Call in hook side-effect free condition
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(a){return true;}; " +
"function x() {true?foo(1):1 }",
"foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunctionInExpression5a() {
// Call in hook side-effect free condition
helperCanInlineReferenceToFunction(
CanInlineResult.AFTER_PREPARATION,
"function foo(a){return true;}; " +
"function x() {true?foo(1):1 }",
"foo", INLINE_BLOCK, true);
}
public void testCanInlineReferenceToFunctionInExpression6() {
// Call in expression statement "condition"
helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION,
"function foo(a){return true;}; " +
"function x() {foo(1) && 1 }",
"foo", INLINE_BLOCK, true);
}
public void testCanInlineReferenceToFunctionInExpression7() {
// Call in expression statement after side-effect free "condition"
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(a){return true;}; " +
"function x() {1 && foo(1) }",
"foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunctionInExpression7a() {
// Call in expression statement after side-effect free "condition"
helperCanInlineReferenceToFunction(
CanInlineResult.AFTER_PREPARATION,
"function foo(a){return true;}; " +
"function x() {1 && foo(1) }",
"foo", INLINE_BLOCK, true);
}
public void testCanInlineReferenceToFunctionInExpression8() {
// Call in expression statement after side-effect free operator
helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION,
"function foo(a){return true;}; " +
"function x() {1 + foo(1) }",
"foo", INLINE_BLOCK, true);
}
public void testCanInlineReferenceToFunctionInExpression9() {
// Call in VAR expression.
helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION,
"function foo(a){return true;}; " +
"function x() {var b = 1 + foo(1)}",
"foo", INLINE_BLOCK, true);
}
public void testCanInlineReferenceToFunctionInExpression10() {
// Call in assignment expression.
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(a){return true;}; " +
"function x() {var b; b += 1 + foo(1) }",
"foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunctionInExpression10a() {
// Call in assignment expression.
helperCanInlineReferenceToFunction(
CanInlineResult.AFTER_PREPARATION,
"function foo(a){return true;}; " +
"function x() {var b; b += 1 + foo(1) }",
"foo", INLINE_BLOCK, true);
}
// TODO(nicksantos): Re-enable with side-effect detection.
// public void testCanInlineReferenceToFunctionInExpression11() {
// helperCanInlineReferenceToFunction(true,
// "/** @nosideeffects */ function foo(a){return true;}; " +
// "function x() {var b; b += 1 + foo(1) }",
// "foo", INLINE_BLOCK);
// }
public void testCanInlineReferenceToFunctionInExpression12() {
helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION,
"function foo(a){return true;}; " +
"function x() {var a,b,c; a = b = c = foo(1) }",
"foo", INLINE_BLOCK, true);
}
public void testCanInlineReferenceToFunctionInExpression13() {
helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION,
"function foo(a){return true;}; " +
"function x() {var a,b,c; a = b = c = 1 + foo(1) }",
"foo", INLINE_BLOCK, true);
}
public void testCanInlineReferenceToFunctionInExpression14() {
// ... foo can not be inlined because of possible changes to "c".
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"var a = {}, b = {}, c;" +
"a.test = 'a';" +
"b.test = 'b';" +
"c = a;" +
"function foo(){c = b; return 'foo'};" +
"c.test=foo();",
"foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunctionInExpression14a() {
// ... foo can be inlined despite possible changes to "c".
helperCanInlineReferenceToFunction(
CanInlineResult.AFTER_PREPARATION,
"var a = {}, b = {}, c;" +
"a.test = 'a';" +
"b.test = 'b';" +
"c = a;" +
"function foo(){c = b; return 'foo'};" +
"c.test=foo();",
"foo", INLINE_BLOCK, true);
}
// TODO(nicksantos): Re-enable with side-effect detection.
// public void testCanInlineReferenceToFunctionInExpression15() {
// // ... foo can be inlined as it is side-effect free.
// helperCanInlineReferenceToFunction(true,
// "var a = {}, b = {}, c;" +
// "a.test = 'a';" +
// "b.test = 'b';" +
// "c = a;" +
// "/** @nosideeffects */ function foo(){return 'foo'};" +
// "c.test=foo();",
// "foo", INLINE_BLOCK);
// }
// public void testCanInlineReferenceToFunctionInExpression16() {
// // ... foo can not be inlined because of possible side-effects of x()
// helperCanInlineReferenceToFunction(false,
// "var a = {}, b = {}, c;" +
// "a.test = 'a';" +
// "b.test = 'b';" +
// "c = a;" +
// "function x(){return c};" +
// "/** @nosideeffects */ function foo(){return 'foo'};" +
// "x().test=foo();",
// "foo", INLINE_BLOCK);
// }
// public void testCanInlineReferenceToFunctionInExpression17() {
// // ... foo can be inlined because of x() is side-effect free.
// helperCanInlineReferenceToFunction(true,
// "var a = {}, b = {}, c;" +
// "a.test = 'a';" +
// "b.test = 'b';" +
// "c = a;" +
// "/** @nosideeffects */ function x(){return c};" +
// "/** @nosideeffects */ function foo(){return 'foo'};" +
// "x().test=foo();",
// "foo", INLINE_BLOCK);
// }
public void testCanInlineReferenceToFunctionInExpression18() {
// Call in within a call
helperCanInlineReferenceToFunction(CanInlineResult.AFTER_PREPARATION,
"function foo(){return _g();}; " +
"function x() {1 + foo()() }",
"foo", INLINE_BLOCK, true);
}
public void testCanInlineReferenceToFunctionInExpression19() {
// ... unless foo is known to be side-effect free, it might actually
// change the value of "_g" which would unfortunately change the behavior,
// so we can't inline here.
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(){return a;}; " +
"function x() {1 + _g(foo()) }",
"foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunctionInExpression19a() {
// ... unless foo is known to be side-effect free, it might actually
// change the value of "_g" which would unfortunately change the behavior,
// so we can't inline here.
helperCanInlineReferenceToFunction(
CanInlineResult.AFTER_PREPARATION,
"function foo(){return a;}; " +
"function x() {1 + _g(foo()) }",
"foo", INLINE_BLOCK, true);
}
// TODO(nicksantos): Re-enable with side-effect detection.
// public void testCanInlineReferenceToFunctionInExpression20() {
// helperCanInlineReferenceToFunction(true,
// "/** @nosideeffects */ function foo(){return a;}; " +
// "function x() {1 + _g(foo()) }",
// "foo", INLINE_BLOCK);
// }
public void testCanInlineReferenceToFunctionInExpression21() {
// Assignments to object are problematic if the call has side-effects,
// as the object that is being referred to can change.
// Note: This could be changed be inlined if we in some way make "z"
// as not escaping from the local scope.
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"var z = {};" +
"function foo(a){z = {};return true;}; " +
"function x() { z.gack = foo(1) }",
"foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunctionInExpression21a() {
// Assignments to object are problematic if the call has side-effects,
// as the object that is being referred to can change.
// Note: This could be changed be inlined if we in some way make "z"
// as not escaping from the local scope.
helperCanInlineReferenceToFunction(
CanInlineResult.AFTER_PREPARATION,
"var z = {};" +
"function foo(a){z = {};return true;}; " +
"function x() { z.gack = foo(1) }",
"foo", INLINE_BLOCK, true);
}
public void testCanInlineReferenceToFunctionInExpression22() {
// ... foo() is after a side-effect
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(){return a;}; " +
"function x() {1 + _g(_a(), foo()) }",
"foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunctionInExpression22a() {
// ... foo() is after a side-effect
helperCanInlineReferenceToFunction(
CanInlineResult.AFTER_PREPARATION,
"function foo(){return a;}; " +
"function x() {1 + _g(_a(), foo()) }",
"foo", INLINE_BLOCK, true);
}
public void testCanInlineReferenceToFunctionInExpression23() {
// ... foo() is after a side-effect
helperCanInlineReferenceToFunction(CanInlineResult.NO,
"function foo(){return a;}; " +
"function x() {1 + _g(_a(), foo.call(this)) }",
"foo", INLINE_BLOCK);
}
public void testCanInlineReferenceToFunctionInExpression23a() {
// ... foo() is after a side-effect
helperCanInlineReferenceToFunction(
CanInlineResult.AFTER_PREPARATION,
"function foo(){return a;}; " +
"function x() {1 + _g(_a(), foo.call(this)) }",
"foo", INLINE_BLOCK, true);
}
public void testCanInlineReferenceToFunctionInLoop1() {
helperCanInlineReferenceToFunction(
CanInlineResult.YES,
"function foo(){return a;}; " +
"while(1) { foo(); }",
"foo", INLINE_BLOCK, true);
}
public void testCanInlineReferenceToFunctionInLoop2() {
// If function contains function, don't inline it into a loop.
// TODO(johnlenz): this can be improved by looking to see
// if the inner function contains any references to values defined
// in the outer function.
helperCanInlineReferenceToFunction(
CanInlineResult.NO,
"function foo(){return function() {};}; " +
"while(1) { foo(); }",
"foo", INLINE_BLOCK, true);
}
public void testInline1() {
helperInlineReferenceToFunction(
"function foo(){}; foo();",
"function foo(){}; void 0",
"foo", INLINE_DIRECT);
}
public void testInline2() {
helperInlineReferenceToFunction(
"function foo(){}; foo();",
"function foo(){}; {}",
"foo", INLINE_BLOCK);
}
public void testInline3() {
helperInlineReferenceToFunction(
"function foo(){return;}; foo();",
"function foo(){return;}; {}",
"foo", INLINE_BLOCK);
}
public void testInline4() {
helperInlineReferenceToFunction(
"function foo(){return true;}; foo();",
"function foo(){return true;}; true;",
"foo", INLINE_DIRECT);
}
public void testInline5() {
helperInlineReferenceToFunction(
"function foo(){return true;}; foo();",
"function foo(){return true;}; {true;}",
"foo", INLINE_BLOCK);
}
public void testInline6() {
// In var initialization.
helperInlineReferenceToFunction(
"function foo(){return true;}; var x=foo();",
"function foo(){return true;}; var x=true;",
"foo", INLINE_DIRECT);
}
public void testInline7() {
helperInlineReferenceToFunction(
"function foo(){return true;}; var x=foo();",
"function foo(){return true;}; var x;" +
"{x=true}",
"foo", INLINE_BLOCK);
}
public void testInline8() {
// In assignment.
helperInlineReferenceToFunction(
"function foo(){return true;}; var x; x=foo();",
"function foo(){return true;}; var x; x=true;",
"foo", INLINE_DIRECT);
}
public void testInline9() {
helperInlineReferenceToFunction(
"function foo(){return true;}; var x; x=foo();",
"function foo(){return true;}; var x;{x=true}",
"foo", INLINE_BLOCK);
}
public void testInline10() {
// In expression.
helperInlineReferenceToFunction(
"function foo(){return true;}; var x; x=x+foo();",
"function foo(){return true;}; var x; x=x+true;",
"foo", INLINE_DIRECT);
}
public void testInline11() {
// Simple call with parameters
helperInlineReferenceToFunction(
"function foo(a){return true;}; foo(x);",
"function foo(a){return true;}; true;",
"foo", INLINE_DIRECT);
}
public void testInline12() {
helperInlineReferenceToFunction(
"function foo(a){return true;}; foo(x);",
"function foo(a){return true;}; {true}",
"foo", INLINE_BLOCK);
}
public void testInline13() {
// Parameter has side-effects.
helperInlineReferenceToFunction(
"function foo(a){return a;}; " +
"function x() { foo(x++); }",
"function foo(a){return a;}; " +
"function x() {{var a$$inline_0=x++;" +
"a$$inline_0}}",
"foo", INLINE_BLOCK);
}
public void testInline14() {
// Parameter has side-effects.
helperInlineReferenceToFunction(
"function foo(a){return a+a;}; foo(x++);",
"function foo(a){return a+a;}; " +
"{var a$$inline_0=x++;" +
" a$$inline_0+" +
"a$$inline_0;}",
"foo", INLINE_BLOCK);
}
public void testInline15() {
// Parameter has mutable, references more than once.
helperInlineReferenceToFunction(
"function foo(a){return a+a;}; foo(new Date());",
"function foo(a){return a+a;}; " +
"{var a$$inline_0=new Date();" +
" a$$inline_0+" +
"a$$inline_0;}",
"foo", INLINE_BLOCK);
}
public void testInline16() {
// Parameter is large, references more than once.
helperInlineReferenceToFunction(
"function foo(a){return a+a;}; foo(function(){});",
"function foo(a){return a+a;}; " +
"{var a$$inline_0=function(){};" +
" a$$inline_0+" +
"a$$inline_0;}",
"foo", INLINE_BLOCK);
}
public void testInline17() {
// Parameter has side-effects.
helperInlineReferenceToFunction(
"function foo(a){return true;}; foo(goo());",
"function foo(a){return true;};" +
"{var a$$inline_0=goo();true}",
"foo", INLINE_BLOCK);
}
public void testInline18() {
// This doesn't bring names into the global name space.
helperInlineReferenceToFunction(
"function foo(a){var b;return a;}; " +
"function x() { foo(goo()); }",
"function foo(a){var b;return a;}; " +
"function x() {{var a$$inline_0=goo();" +
"var b$$inline_1;a$$inline_0}}",
"foo", INLINE_BLOCK);
}
public void testInline19() {
// Properly alias.
helperInlineReferenceToFunction(
"var x = 1; var y = 2;" +
"function foo(a,b){x = b; y = a;}; " +
"function bar() { foo(x,y); }",
"var x = 1; var y = 2;" +
"function foo(a,b){x = b; y = a;}; " +
"function bar() {" +
"{var a$$inline_0=x;" +
"x = y;" +
"y = a$$inline_0;}" +
"}",
"foo", INLINE_BLOCK);
}
public void testInline19b() {
helperInlineReferenceToFunction(
"var x = 1; var y = 2;" +
"function foo(a,b){y = a; x = b;}; " +
"function bar() { foo(x,y); }",
"var x = 1; var y = 2;" +
"function foo(a,b){y = a; x = b;}; " +
"function bar() {" +
"{var b$$inline_1=y;" +
"y = x;" +
"x = b$$inline_1;}" +
"}",
"foo", INLINE_BLOCK);
}
public void testInlineIntoLoop() {
helperInlineReferenceToFunction(
"function foo(a){var b;return a;}; " +
"for(;1;){ foo(1); }",
"function foo(a){var b;return a;}; " +
"for(;1;){ {" +
"var b$$inline_1=void 0;1}}",
"foo", INLINE_BLOCK);
helperInlineReferenceToFunction(
"function foo(a){var b;return a;}; " +
"do{ foo(1); } while(1)",
"function foo(a){var b;return a;}; " +
"do{ {" +
"var b$$inline_1=void 0;1}}while(1)",
"foo", INLINE_BLOCK);
helperInlineReferenceToFunction(
"function foo(a){for(var b in c)return a;}; " +
"for(;1;){ foo(1); }",
"function foo(a){var b;for(b in c)return a;}; " +
"for(;1;){ {JSCompiler_inline_label_foo_2:{" +
"var b$$inline_1=void 0;for(b$$inline_1 in c){" +
"1;break JSCompiler_inline_label_foo_2" +
"}}}}",
"foo", INLINE_BLOCK);
}
public void testInlineFunctionWithInnerFunction1() {
// Call with inner function expression.
helperInlineReferenceToFunction(
"function foo(){return function() {return true;}}; foo();",
"function foo(){return function() {return true;}};" +
"(function() {return true;})",
"foo", INLINE_DIRECT);
}
public void testInlineFunctionWithInnerFunction2() {
// Call with inner function expression.
helperInlineReferenceToFunction(
"function foo(){return function() {return true;}}; foo();",
"function foo(){return function() {return true;}};" +
"{(function() {return true;})}",
"foo", INLINE_BLOCK);
}
public void testInlineFunctionWithInnerFunction3() {
// Call with inner function expression.
helperInlineReferenceToFunction(
"function foo(){return function() {var a; return true;}}; foo();",
"function foo(){return function() {var a; return true;}};" +
"(function() {var a; return true;});",
"foo", INLINE_DIRECT);
}
public void testInlineFunctionWithInnerFunction4() {
// Call with inner function expression.
helperInlineReferenceToFunction(
"function foo(){return function() {var a; return true;}}; foo();",
"function foo(){return function() {var a; return true;}};" +
"{(function() {var a$$inline_0; return true;});}",
"foo", INLINE_BLOCK);
}
public void testInlineFunctionWithInnerFunction5() {
// Call with inner function statement.
helperInlineReferenceToFunction(
"function foo(){function x() {var a; return true;} return x}; foo();",
"function foo(){function x(){var a;return true}return x};" +
"{var x$$inline_0 = function(){" +
"var a$$inline_1;return true};x$$inline_0}",
"foo", INLINE_BLOCK);
}
public void testInlineReferenceInExpression1() {
// Call in if condition
helperInlineReferenceToFunction(
"function foo(a){return true;}; " +
"function x() { if (foo(1)) throw 'test'; }",
"function foo(a){return true;}; " +
"function x() { var JSCompiler_inline_result$$0; " +
"{JSCompiler_inline_result$$0=true;}" +
"if (JSCompiler_inline_result$$0) throw 'test'; }",
"foo", INLINE_BLOCK, true);
}
public void testInlineReferenceInExpression2() {
// Call in return expression
helperInlineReferenceToFunction(
"function foo(a){return true;}; " +
"function x() { return foo(1); }",
"function foo(a){return true;}; " +
"function x() { var JSCompiler_inline_result$$0; " +
"{JSCompiler_inline_result$$0=true;}" +
"return JSCompiler_inline_result$$0; }",
"foo", INLINE_BLOCK, true);
}
public void testInlineReferenceInExpression3() {
// Call in switch expression
helperInlineReferenceToFunction(
"function foo(a){return true;}; " +
"function x() { switch(foo(1)) { default:break; } }",
"function foo(a){return true;}; " +
"function x() { var JSCompiler_inline_result$$0; " +
"{JSCompiler_inline_result$$0=true;}" +
"switch(JSCompiler_inline_result$$0) { default:break; } }",
"foo", INLINE_BLOCK, true);
}
public void testInlineReferenceInExpression4() {
// Call in hook condition
helperInlineReferenceToFunction(
"function foo(a){return true;}; " +
"function x() {foo(1)?0:1 }",
"function foo(a){return true;}; " +
"function x() { var JSCompiler_inline_result$$0; " +
"{JSCompiler_inline_result$$0=true;}" +
"JSCompiler_inline_result$$0?0:1 }",
"foo", INLINE_BLOCK, true);
}
public void testInlineReferenceInExpression5() {
// Call in expression statement "condition"
helperInlineReferenceToFunction(
"function foo(a){return true;}; " +
"function x() {foo(1)&&1 }",
"function foo(a){return true;}; " +
"function x() { var JSCompiler_inline_result$$0; " +
"{JSCompiler_inline_result$$0=true;}" +
"JSCompiler_inline_result$$0&&1 }",
"foo", INLINE_BLOCK, true);
}
public void testInlineReferenceInExpression6() {
// Call in expression statement after side-effect free "condition"
helperInlineReferenceToFunction(
"function foo(a){return true;}; " +
"function x() {1 + foo(1) }",
"function foo(a){return true;}; " +
"function x() { var JSCompiler_inline_result$$0; " +
"{JSCompiler_inline_result$$0=true;}" +
"1 + JSCompiler_inline_result$$0 }",
"foo", INLINE_BLOCK, true);
}
public void testInlineReferenceInExpression7() {
// Call in expression statement "condition"
helperInlineReferenceToFunction(
"function foo(a){return true;}; " +
"function x() {foo(1) && 1 }",
"function foo(a){return true;}; " +
"function x() { var JSCompiler_inline_result$$0; " +
"{JSCompiler_inline_result$$0=true;}" +
"JSCompiler_inline_result$$0&&1 }",
"foo", INLINE_BLOCK, true);
}
public void testInlineReferenceInExpression8() {
// Call in expression statement after side-effect free operator
helperInlineReferenceToFunction(
"function foo(a){return true;}; " +
"function x() {1 + foo(1) }",
"function foo(a){return true;}; " +
"function x() { var JSCompiler_inline_result$$0;" +
"{JSCompiler_inline_result$$0=true;}" +
"1 + JSCompiler_inline_result$$0 }",
"foo", INLINE_BLOCK, true);
}
public void testInlineReferenceInExpression9() {
// Call in VAR expression.
helperInlineReferenceToFunction(
"function foo(a){return true;}; " +
"function x() {var b = 1 + foo(1)}",
"function foo(a){return true;}; " +
"function x() { " +
"var JSCompiler_inline_result$$0;" +
"{JSCompiler_inline_result$$0=true;}" +
"var b = 1 + JSCompiler_inline_result$$0 " +
"}",
"foo", INLINE_BLOCK, true);
}
// TODO(nicksantos): Re-enable with side-effect detection.
// public void testInlineReferenceInExpression10() {
// // Call in assignment expression.
// helperInlineReferenceToFunction(
// "/** @nosideeffects */ function foo(a){return true;}; " +
// "function x() {var b; b += 1 + foo(1) }",
// "function foo(a){return true;}; " +
// "function x() {var b;" +
// "{var JSCompiler_inline_result$$0; " +
// "JSCompiler_inline_result$$0=true;}" +
// "b += 1 + JSCompiler_inline_result$$0 }",
// "foo", INLINE_BLOCK);
// }
public void testInlineReferenceInExpression11() {
// Call under label
helperInlineReferenceToFunction(
"function foo(a){return true;}; " +
"function x() {a:foo(1)?0:1 }",
"function foo(a){return true;}; " +
"function x() {" +
" a:{" +
" var JSCompiler_inline_result$$0; " +
" {JSCompiler_inline_result$$0=true;}" +
" JSCompiler_inline_result$$0?0:1 " +
" }" +
"}",
"foo", INLINE_BLOCK, true);
}
public void testInlineReferenceInExpression12() {
helperInlineReferenceToFunction(
"function foo(a){return true;}" +
"function x() { 1?foo(1):1; }",
"function foo(a){return true}" +
"function x() {" +
" if(1) {" +
" {true;}" +
" } else {" +
" 1;" +
" }" +
"}",
"foo", INLINE_BLOCK, true);
}
public void testInlineReferenceInExpression13() {
helperInlineReferenceToFunction(
"function foo(a){return true;}; " +
"function x() { goo() + (1?foo(1):1) }",
"function foo(a){return true;}; " +
"function x() { var JSCompiler_temp_const$$0=goo();" +
"var JSCompiler_temp$$1;" +
"if(1) {" +
" {JSCompiler_temp$$1=true;} " +
"} else {" +
" JSCompiler_temp$$1=1;" +
"}" +
"JSCompiler_temp_const$$0 + JSCompiler_temp$$1" +
"}",
"foo", INLINE_BLOCK, true);
}
public void testInlineReferenceInExpression14() {
helperInlineReferenceToFunction(
"var z = {};" +
"function foo(a){z = {};return true;}; " +
"function x() { z.gack = foo(1) }",
"var z = {};" +
"function foo(a){z = {};return true;}; " +
"function x() {" +
"var JSCompiler_temp_const$$0=z;" +
"var JSCompiler_inline_result$$1;" +
"{" +
"z= {};" +
"JSCompiler_inline_result$$1 = true;" +
"}" +
"JSCompiler_temp_const$$0.gack = JSCompiler_inline_result$$1;" +
"}",
"foo", INLINE_BLOCK, true);
}
public void testInlineReferenceInExpression15() {
helperInlineReferenceToFunction(
"var z = {};" +
"function foo(a){z = {};return true;}; " +
"function x() { z.gack = foo.call(this,1) }",
"var z = {};" +
"function foo(a){z = {};return true;}; " +
"function x() {" +
"var JSCompiler_temp_const$$0=z;" +
"var JSCompiler_inline_result$$1;" +
"{" +
"z= {};" +
"JSCompiler_inline_result$$1 = true;" +
"}" +
"JSCompiler_temp_const$$0.gack = JSCompiler_inline_result$$1;" +
"}",
"foo", INLINE_BLOCK, true);
}
public void testInlineReferenceInExpression16() {
helperInlineReferenceToFunction(
"var z = {};" +
"function foo(a){z = {};return true;}; " +
"function x() { z[bar()] = foo(1) }",
"var z = {};" +
"function foo(a){z = {};return true;}; " +
"function x() {" +
"var JSCompiler_temp_const$$1=z;" +
"var JSCompiler_temp_const$$0=bar();" +
"var JSCompiler_inline_result$$2;" +
"{" +
"z= {};" +
"JSCompiler_inline_result$$2 = true;" +
"}" +
"JSCompiler_temp_const$$1[JSCompiler_temp_const$$0] = " +
"JSCompiler_inline_result$$2;" +
"}",
"foo", INLINE_BLOCK, true);
}
public void testInlineReferenceInExpression17() {
helperInlineReferenceToFunction(
"var z = {};" +
"function foo(a){z = {};return true;}; " +
"function x() { z.y.x.gack = foo(1) }",
"var z = {};" +
"function foo(a){z = {};return true;}; " +
"function x() {" +
"var JSCompiler_temp_const$$0=z.y.x;" +
"var JSCompiler_inline_result$$1;" +
"{" +
"z= {};" +
"JSCompiler_inline_result$$1 = true;" +
"}" +
"JSCompiler_temp_const$$0.gack = JSCompiler_inline_result$$1;" +
"}",
"foo", INLINE_BLOCK, true);
}
public void testInlineWithinCalls1() {
// Call in within a call
helperInlineReferenceToFunction(
"function foo(){return _g;}; " +
"function x() {1 + foo()() }",
"function foo(){return _g;}; " +
"function x() { var JSCompiler_inline_result$$0;" +
"{JSCompiler_inline_result$$0=_g;}" +
"1 + JSCompiler_inline_result$$0() }",
"foo", INLINE_BLOCK, true);
}
// TODO(nicksantos): Re-enable with side-effect detection.
// public void testInlineWithinCalls2() {
// helperInlineReferenceToFunction(
// "/** @nosideeffects */ function foo(){return true;}; " +
// "function x() {1 + _g(foo()) }",
// "function foo(){return true;}; " +
// "function x() { {var JSCompiler_inline_result$$0; " +
// "JSCompiler_inline_result$$0=true;}" +
// "1 + _g(JSCompiler_inline_result$$0) }",
// "foo", INLINE_BLOCK, true);
// }
public void testInlineAssignmentToConstant() {
// Call in within a call
helperInlineReferenceToFunction(
"function foo(){return _g;}; " +
"function x(){var CONSTANT_RESULT = foo(); }",
"function foo(){return _g;}; " +
"function x() {" +
" var JSCompiler_inline_result$$0;" +
" {JSCompiler_inline_result$$0=_g;}" +
" var CONSTANT_RESULT = JSCompiler_inline_result$$0;" +
"}",
"foo", INLINE_BLOCK, true);
}
public void testBug1897706() {
helperInlineReferenceToFunction(
"function foo(a){}; foo(x())",
"function foo(a){}; {var a$$inline_0=x()}",
"foo", INLINE_BLOCK);
helperInlineReferenceToFunction(
"function foo(a){bar()}; foo(x())",
"function foo(a){bar()}; {var a$$inline_0=x();bar()}",
"foo", INLINE_BLOCK);
helperInlineReferenceToFunction(
"function foo(a,b){bar()}; foo(x(),y())",
"function foo(a,b){bar()};" +
"{var a$$inline_0=x();var b$$inline_1=y();bar()}",
"foo", INLINE_BLOCK);
}
/**
* Test case
*
* var a = {}, b = {}
* a.test = "a", b.test = "b"
* c = a;
* foo() { c=b; return "a" }
* c.teste
*
*/
public void helperCanInlineReferenceToFunction(
final CanInlineResult expectedResult,
final String code,
final String fnName,
final InliningMode mode) {
helperCanInlineReferenceToFunction(
expectedResult, code, fnName, mode, false);
}
public void helperCanInlineReferenceToFunction(
final CanInlineResult expectedResult,
final String code,
final String fnName,
final InliningMode mode,
boolean allowDecomposition) {
final Compiler compiler = new Compiler();
final FunctionInjector injector = new FunctionInjector(
compiler, compiler.getUniqueNameIdSupplier(), allowDecomposition,
assumeStrictThis,
assumeMinimumCapture);
final Node tree = parse(compiler, code);
Node externsRoot = new Node(Token.EMPTY);
Node mainRoot = tree;
final Node fnNode = findFunction(tree, fnName);
final Set<String> unsafe =
FunctionArgumentInjector.findModifiedParameters(fnNode);
// can-inline tester
Method tester = new Method() {
@Override
public boolean call(NodeTraversal t, Node n, Node parent) {
CanInlineResult result = injector.canInlineReferenceToFunction(
t, n, fnNode, unsafe, mode,
NodeUtil.referencesThis(fnNode),
NodeUtil.containsFunction(NodeUtil.getFunctionBody(fnNode)));
assertEquals(expectedResult, result);
return true;
}
};
compiler.resetUniqueNameId();
TestCallback test = new TestCallback(fnName, tester);
NodeTraversal.traverse(compiler, tree, test);
}
public void helperInlineReferenceToFunction(
String code, final String expectedResult,
final String fnName, final InliningMode mode) {
helperInlineReferenceToFunction(
code, expectedResult, fnName, mode, 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 helperInlineReferenceToFunction(
String code, final String expectedResult,
final String fnName, final InliningMode mode,
final boolean decompose) {
final Compiler compiler = new Compiler();
final FunctionInjector injector = new FunctionInjector(
compiler, compiler.getUniqueNameIdSupplier(), decompose,
assumeStrictThis,
assumeMinimumCapture);
List<SourceFile> externsInputs = Lists.newArrayList(
SourceFile.fromCode("externs", ""));
CompilerOptions options = new CompilerOptions();
options.setCodingConvention(new GoogleCodingConvention());
compiler.init(externsInputs, Lists.newArrayList(
SourceFile.fromCode("code", code)), options);
Node parseRoot = compiler.parseInputs();
Node externsRoot = parseRoot.getFirstChild();
final Node tree = parseRoot.getLastChild();
assertNotNull(tree);
assertTrue(tree != externsRoot);
final Node expectedRoot = parseExpected(new Compiler(), expectedResult);
Node mainRoot = tree;
MarkNoSideEffectCalls mark = new MarkNoSideEffectCalls(compiler);
mark.process(externsRoot, mainRoot);
Normalize normalize = new Normalize(compiler, false);
normalize.process(externsRoot, mainRoot);
compiler.setLifeCycleStage(LifeCycleStage.NORMALIZED);
final Node fnNode = findFunction(tree, fnName);
assertNotNull(fnNode);
final Set<String> unsafe =
FunctionArgumentInjector.findModifiedParameters(fnNode);
assertNotNull(fnNode);
// inline tester
Method tester = new Method() {
@Override
public boolean call(NodeTraversal t, Node n, Node parent) {
CanInlineResult canInline = injector.canInlineReferenceToFunction(
t, n, fnNode, unsafe, mode,
NodeUtil.referencesThis(fnNode),
NodeUtil.containsFunction(NodeUtil.getFunctionBody(fnNode)));
assertTrue("canInlineReferenceToFunction should not be CAN_NOT_INLINE",
CanInlineResult.NO != canInline);
if (decompose) {
assertTrue("canInlineReferenceToFunction " +
"should be CAN_INLINE_AFTER_DECOMPOSITION",
CanInlineResult.AFTER_PREPARATION == canInline);
Set<String> knownConstants = Sets.newHashSet();
ExpressionDecomposer decomposer = new ExpressionDecomposer(
compiler, compiler.getUniqueNameIdSupplier(), knownConstants);
injector.setKnownConstants(knownConstants);
injector.maybePrepareCall(n);
assertTrue("canInlineReferenceToFunction " +
"should be CAN_INLINE",
CanInlineResult.YES != canInline);
}
Node result = injector.inline(
t, n, fnName, fnNode, mode);
validateSourceInfo(compiler, result);
String explanation = expectedRoot.checkTreeEquals(tree.getFirstChild());
assertNull("\nExpected: " + toSource(expectedRoot) +
"\nResult: " + toSource(tree.getFirstChild()) +
"\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 callee;
if (NodeUtil.isGet(n.getFirstChild())) {
callee = n.getFirstChild().getFirstChild();
} else {
callee = n.getFirstChild();
}
if (callee.isName() &&
callee.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 prep(String js) {
Compiler compiler = new Compiler();
Node n = compiler.parseTestCode(js);
assertEquals(0, compiler.getErrorCount());
return n.getFirstChild();
}
private static Node parse(Compiler compiler, String js) {
Node n = compiler.parseTestCode(js);
assertEquals(0, compiler.getErrorCount());
return n;
}
private static Node parseExpected(Compiler compiler, String js) {
Node n = compiler.parseTestCode(js);
String message = "Unexpected errors: ";
JSError[] errs = compiler.getErrors();
for (int i = 0; i < errs.length; i++){
message += "\n" + errs[i].toString();
}
assertEquals(message, 0, compiler.getErrorCount());
return n;
}
private static String toSource(Node n) {
return new CodePrinter.Builder(n)
.setPrettyPrint(false)
.setLineBreak(false)
.setSourceMap(null)
.build();
}
}