| /* |
| * Copyright 2009 Google Inc. |
| * |
| * 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.gwt.dev.js; |
| |
| import com.google.gwt.dev.js.ast.JsContext; |
| import com.google.gwt.dev.js.ast.JsFunction; |
| import com.google.gwt.dev.js.ast.JsModVisitor; |
| import com.google.gwt.dev.js.ast.JsName; |
| import com.google.gwt.dev.js.ast.JsProgram; |
| |
| /** |
| * Safety checks for JsInliner. |
| */ |
| public class JsInlinerTest extends OptimizerTestBase { |
| |
| private static class FixStaticRefsVisitor extends JsModVisitor { |
| |
| /** |
| * Called reflectively. |
| */ |
| @SuppressWarnings("unused") |
| public static void exec(JsProgram program) { |
| (new FixStaticRefsVisitor()).accept(program); |
| } |
| |
| @Override |
| public void endVisit(JsFunction x, JsContext ctx) { |
| JsName name = x.getName(); |
| if (name != null) { |
| name.setStaticRef(x); |
| } |
| } |
| } |
| |
| public void testInlineArrayLiterals() throws Exception { |
| String input = "function a1(arg, x) { arg.x = x; return arg; }" |
| + "function b1() { var x=a1([], 10); } b1();"; |
| compare(input, input); |
| } |
| |
| public void testInlineFunctionLiterals() throws Exception { |
| String input = "function a1(arg, x) { arg.x = x; return arg; }" |
| + "function b1() { var x=a1(function (){}, 10); } b1();"; |
| compare(input, input); |
| String input2 = "function a1(arg, x) { arg.x = x; return arg; }" |
| + "function b1() { var x=a1(function blah(){}, 10); } b1();"; |
| compare(input2, input2); |
| } |
| |
| public void testInlineObjectLiterals() throws Exception { |
| String input = "function a1(arg, x) { arg.x = x; return arg; }" |
| + "function b1() { var x=a1({}, 10); } b1();"; |
| compare(input, input); |
| } |
| |
| /** |
| * A test for mutually-recursive functions. Setup: |
| * |
| * <pre> |
| * a -> b, c |
| * b -> a, c |
| * c -> a, c |
| * </pre> |
| */ |
| public void testMutualRecursion() throws Exception { |
| String input = "function a1() { return ex ? b1() : c1() }" |
| + "function b1() { return ex2 ? a1(): c1(); }" |
| + "function c1() { return ex2? a1():c1(); } c1()"; |
| String expected = "function a1() { return ex ? (ex2 ? a1() : c1()) : c1() }" |
| + "function c1() { return ex2 ? a1() :c1(); } c1()"; |
| compare(expected, input); |
| } |
| |
| /** |
| * Test that a global array reference breaks argument ordering. |
| */ |
| public void testOrderingArrayGlobal() throws Exception { |
| StringBuffer code = new StringBuffer(); |
| |
| code.append("var array; "); |
| code.append("function clinit() { clinit = null; }"); |
| |
| // callee references array[0] before evaluating argument |
| code.append("function callee(arg) { return array[0] + arg; }"); |
| |
| // caller invokes callee with a multi that runs clinit() |
| code.append("function caller() { callee((clinit(),2)); }"); |
| |
| // bootstrap the program |
| code.append("caller();"); |
| |
| compare(code.toString(), code.toString()); |
| } |
| |
| /** |
| * Test that a local reference does not break argument ordering. |
| */ |
| public void testOrderingArrayLocal() throws Exception { |
| StringBuffer code = new StringBuffer(); |
| |
| code.append("function clinit() { clinit = null; }"); |
| |
| // callee references array[0] before evaluating argument |
| code.append("function callee(arg) { var array; return array[0] + arg; }"); |
| |
| // caller invokes callee with a multi that runs clinit() |
| code.append("function caller() { callee((clinit(),2)); }"); |
| |
| // bootstrap the program |
| code.append("caller();"); |
| |
| StringBuffer expected = new StringBuffer(); |
| expected.append("function clinit() { clinit = null; }"); |
| expected.append("function caller() { var array; array[0] + (clinit(), 2); }"); |
| expected.append("caller();"); |
| |
| compare(expected.toString(), code.toString()); |
| } |
| |
| /** |
| * Test that a field reference breaks argument ordering. |
| */ |
| public void testOrderingField() throws Exception { |
| StringBuffer code = new StringBuffer(); |
| |
| code.append("function clinit() { clinit = null; }"); |
| |
| // callee references field.x before evaluating argument |
| code.append("function callee(arg) { var field; return field.x + arg; }"); |
| |
| // caller invokes callee with a multi that runs clinit() |
| code.append("function caller() { callee((clinit(),2)); }"); |
| |
| // bootstrap the program |
| code.append("caller();"); |
| |
| compare(code.toString(), code.toString()); |
| } |
| |
| /** |
| * Test that a global variable breaks argument ordering. |
| */ |
| public void testOrderingGlobal() throws Exception { |
| StringBuffer code = new StringBuffer(); |
| // A global variable x |
| code.append("var x;"); |
| |
| // clinit() sets up x |
| code.append("function clinit() { x = 1; clinit = null; }"); |
| |
| // callee references x before evaluating argument |
| code.append("function callee(arg) { alert(x); return arg; }"); |
| |
| // caller invokes callee with a multi that runs clinit() |
| code.append("function caller() { callee((clinit(),2)); }"); |
| |
| // bootstrap the program |
| code.append("caller();"); |
| |
| compare(code.toString(), code.toString()); |
| } |
| |
| /** |
| * Test that a local variable does not break argument ordering. |
| */ |
| public void testOrderingLocal() throws Exception { |
| StringBuffer code = new StringBuffer(); |
| |
| code.append("function clinit() { clinit = null; }"); |
| |
| // callee references y before evaluating argument |
| code.append("function callee(arg) { var y; y=2; return arg; }"); |
| |
| // caller invokes callee with a multi that runs clinit() |
| code.append("function caller() { return callee((clinit(),3)); }"); |
| |
| // bootstrap the program |
| code.append("caller();"); |
| |
| StringBuffer expected = new StringBuffer(); |
| |
| expected.append("function clinit() { clinit = null; }"); |
| expected.append("function caller() {var y; return y=2,clinit(),3;}"); |
| expected.append("caller();"); |
| compare(expected.toString(), code.toString()); |
| } |
| |
| /** |
| * Test that a new expression breaks argument ordering. |
| */ |
| public void testOrderingNew() throws Exception { |
| StringBuffer code = new StringBuffer(); |
| // A static variable x |
| code.append("var x;"); |
| |
| // foo() uses x |
| code.append("function foo() { alert('x = ' + x); }"); |
| |
| // callee does "new foo" before evaluating its argument |
| code.append("function callee(arg) { new foo(); return arg; }"); |
| |
| // caller invokes callee with a multi that initializes x |
| code.append("function caller() { callee((x=1,2)); }"); |
| |
| // bootstrap the program |
| code.append("caller();"); |
| |
| compare(code.toString(), code.toString()); |
| } |
| |
| public void testSelfRecursion() throws Exception { |
| String input = "function a1() { return blah && b1() }" |
| + "function b1() { return bar && a1()}" + "function c() { a1() } c()"; |
| |
| String expected = "function a1() { return blah && bar && a1() }" |
| + "function c() { a1() } c()"; |
| |
| compare(expected, input); |
| } |
| |
| private void compare(String expected, String input) throws Exception { |
| input = optimize(input, JsSymbolResolver.class, FixStaticRefsVisitor.class, |
| JsInliner.class, JsUnusedFunctionRemover.class); |
| expected = optimize(expected); |
| assertEquals(expected, input); |
| } |
| } |