| /* |
| * Copyright 2011 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.jjs.impl; |
| |
| import java.util.Map; |
| import java.util.TreeMap; |
| |
| import com.google.gwt.core.ext.PropertyOracle; |
| import com.google.gwt.core.ext.UnableToCompleteException; |
| import com.google.gwt.core.ext.linker.SymbolData; |
| import com.google.gwt.core.ext.linker.impl.StandardSymbolData; |
| import com.google.gwt.dev.cfg.BindingProperty; |
| import com.google.gwt.dev.cfg.ConditionNone; |
| import com.google.gwt.dev.cfg.ConfigurationProperty; |
| import com.google.gwt.dev.cfg.StaticPropertyOracle; |
| import com.google.gwt.dev.javac.CompilationState; |
| import com.google.gwt.dev.javac.CompilationStateBuilder; |
| import com.google.gwt.dev.javac.testing.impl.MockJavaResource; |
| import com.google.gwt.dev.jjs.JavaAstConstructor; |
| import com.google.gwt.dev.jjs.JsOutputOption; |
| import com.google.gwt.dev.jjs.ast.JProgram; |
| import com.google.gwt.dev.js.ast.JsBlock; |
| import com.google.gwt.dev.js.ast.JsContext; |
| import com.google.gwt.dev.js.ast.JsFunction; |
| import com.google.gwt.dev.js.ast.JsName; |
| import com.google.gwt.dev.js.ast.JsProgram; |
| import com.google.gwt.dev.js.ast.JsVisitor; |
| |
| /** |
| * Unit test for {@link CodeSplitter2}. |
| */ |
| public class CodeSplitter2Test extends JJSTestBase { |
| // These will be the functions that are shared between fragments. This unit test will |
| // be based for finding these function in the proper fragments. |
| private final String functionA = "public static void functionA() {}"; |
| private final String functionB = "public static void functionB() {}"; |
| private final String functionC = "public static void functionC() {}"; |
| private final String functionD = "public static void functionD() {}"; |
| |
| |
| // Compilation Configuration Properties. |
| private BindingProperty stackMode = new BindingProperty("compiler.stackMode"); |
| private BindingProperty[] orderedProps = {stackMode}; |
| private String[] orderedPropValues = {"STRIP" }; |
| private ConfigurationProperty[] configProps = {}; |
| |
| private JProgram jProgram = null; |
| private JsProgram jsProgram = null; |
| |
| public void setUp() throws Exception{ |
| super.setUp(); |
| stackMode.addDefinedValue(new ConditionNone(), "STRIP"); |
| jsProgram = new JsProgram(); |
| } |
| |
| public void testSimple() throws UnableToCompleteException { |
| StringBuffer code = new StringBuffer(); |
| code.append("package test;\n"); |
| code.append("import com.google.gwt.core.client.GWT;\n"); |
| code.append("import com.google.gwt.core.client.RunAsyncCallback;\n"); |
| code.append("public class EntryPoint {\n"); |
| code.append("static {"); |
| //code.append(" functionC();"); |
| code.append("}"); |
| code.append(functionA); |
| code.append(functionB); |
| code.append(functionC); |
| code.append(" public static void onModuleLoad() {\n"); |
| code.append("functionC();"); |
| // Fragment #1 |
| code.append(createRunAsync("functionA();")); |
| // Fragment #1 (merged) |
| code.append(createRunAsync("functionA(); functionB();")); |
| // Fragment #2 |
| code.append(createRunAsync("functionC();")); |
| code.append(" }\n"); |
| code.append("}\n"); |
| compileSnippet(code.toString()); |
| |
| // init + 2 fragments + leftover. |
| assertFragmentCount(4); |
| assertInFragment("functionA", 1); |
| |
| // Verify that functionA isn't duplicated else where. |
| assertNotInFragment("functionA", 0); |
| assertNotInFragment("functionA", 2); |
| assertNotInFragment("functionA", 3); |
| |
| assertInFragment("functionB", 1); |
| |
| // functionC must be in the initial fragment. |
| assertInFragment("functionC", 0); |
| } |
| |
| public void testNoMergeMoreThanTwo() throws UnableToCompleteException { |
| StringBuffer code = new StringBuffer(); |
| code.append("package test;\n"); |
| code.append("import com.google.gwt.core.client.GWT;\n"); |
| code.append("import com.google.gwt.core.client.RunAsyncCallback;\n"); |
| code.append("public class EntryPoint {\n"); |
| code.append(functionA); |
| code.append(functionB); |
| code.append(functionC); |
| code.append(" public static void onModuleLoad() {\n"); |
| // Fragment #1 |
| code.append(createRunAsync("functionA();")); |
| // Fragment #2 |
| code.append(createRunAsync("functionA();")); |
| // Fragment #3 |
| code.append(createRunAsync("functionA();")); |
| code.append(" }\n"); |
| code.append("}\n"); |
| compileSnippet(code.toString()); |
| |
| // init + 3 fragments + leftover. |
| assertFragmentCount(5); |
| } |
| |
| public void testDoubleMerge() throws UnableToCompleteException { |
| StringBuffer code = new StringBuffer(); |
| code.append("package test;\n"); |
| code.append("import com.google.gwt.core.client.GWT;\n"); |
| code.append("import com.google.gwt.core.client.RunAsyncCallback;\n"); |
| code.append("public class EntryPoint {\n"); |
| code.append(functionA); |
| code.append(functionB); |
| code.append(functionC); |
| code.append(" public static void onModuleLoad() {\n"); |
| // Fragment #1 |
| code.append(createRunAsync("functionA();")); |
| // Fragment #1 |
| code.append(createRunAsync("functionA(); functionC();")); |
| // Fragment #2 |
| code.append(createRunAsync("functionB(); functionC();")); |
| // Fragment #2 |
| code.append(createRunAsync("functionB(); functionC();")); |
| code.append(" }\n"); |
| code.append("}\n"); |
| compileSnippet(code.toString()); |
| |
| // init + 2 fragments + leftover. |
| assertFragmentCount(4); |
| assertInFragment("functionA", 1); |
| assertInFragment("functionB", 2); |
| assertInFragment("functionC", 3); |
| } |
| |
| private void assertFragmentCount(int num) { |
| assertEquals(num, jsProgram.getFragmentCount()); |
| } |
| |
| private void assertInFragment(String functionName, int fragmentNum) { |
| JsBlock fragment = jsProgram.getFragmentBlock(fragmentNum); |
| assertTrue(findFunctionIn(functionName, fragment)); |
| } |
| |
| private void assertNotInFragment(String functionName, int fragmentNum) { |
| JsBlock fragment = jsProgram.getFragmentBlock(fragmentNum); |
| assertFalse(findFunctionIn(functionName, fragment)); |
| } |
| |
| /** |
| * @return true if the function exists in that fragment. |
| */ |
| private static boolean findFunctionIn(final String functionName, JsBlock fragment) { |
| final boolean[] found = {false}; |
| JsVisitor visitor = new JsVisitor() { |
| @Override |
| public boolean visit(JsFunction x, JsContext ctx) { |
| JsName jsName = x.getName(); |
| if (jsName != null && jsName.getShortIdent().equals(functionName)) { |
| found[0] = true; |
| } |
| return false; |
| } |
| }; |
| visitor.accept(fragment); |
| return found[0]; |
| } |
| |
| /** |
| * Compiles a Java class <code>test.EntryPoint</code> and use the code splitter on it. |
| */ |
| protected void compileSnippet(final String code) throws UnableToCompleteException { |
| addMockIntrinsic(); |
| sourceOracle.addOrReplace(new MockJavaResource("test.EntryPoint") { |
| @Override |
| public CharSequence getContent() { |
| return code; |
| } |
| }); |
| addBuiltinClasses(sourceOracle); |
| CompilationState state = |
| CompilationStateBuilder.buildFrom(logger, sourceOracle.getResources(), |
| getAdditionalTypeProviderDelegate()); |
| jProgram = |
| JavaAstConstructor.construct(logger, state, "test.EntryPoint", |
| "com.google.gwt.lang.Exceptions"); |
| jProgram.addEntryMethod(findMethod(jProgram, "onModuleLoad")); |
| CastNormalizer.exec(jProgram, false); |
| ArrayNormalizer.exec(jProgram); |
| Map<StandardSymbolData, JsName> symbolTable = |
| new TreeMap<StandardSymbolData, JsName>(new SymbolData.ClassIdentComparator()); |
| JavaToJavaScriptMap map = GenerateJavaScriptAST.exec( |
| jProgram, jsProgram, JsOutputOption.PRETTY, symbolTable, new PropertyOracle[]{ |
| new StaticPropertyOracle(orderedProps, orderedPropValues, configProps)}); |
| CodeSplitter2.exec(logger, jProgram, jsProgram, map, 4, null); |
| } |
| |
| private static String createRunAsync(String body) { |
| return "GWT.runAsync(new RunAsyncCallback() {" + |
| "public void onFailure(Throwable reason) {}" + |
| "public void onSuccess() {" + body + "}});"; |
| } |
| |
| /** |
| * Add some of the compiler intrinsic |
| */ |
| private void addMockIntrinsic() { |
| sourceOracle.addOrReplace(new MockJavaResource("java.lang.Comparable") { |
| @Override |
| public CharSequence getContent() { |
| return "package java.lang; public interface Comparable {}"; |
| } |
| }); |
| |
| sourceOracle.addOrReplace(new MockJavaResource("java.lang.Object") { |
| @Override |
| public CharSequence getContent() { |
| return "package java.lang; public class Object {" + |
| "public Object castableTypeMap = null;" + |
| "public Object typeMarker = null;" + |
| "public Object ___clazz = null;" + |
| "public Object getClass() {return null;}" + |
| "public String toString() {return null;} }"; |
| } |
| }); |
| |
| sourceOracle.addOrReplace(new MockJavaResource("com.google.gwt.lang.Array") { |
| @Override |
| public CharSequence getContent() { |
| return "package com.google.gwt.lang; public class Array {" + |
| " public static int length = 0;" + |
| " public static void setCheck(Array array, int index, Object value) { }" + |
| " static void initDim() { }" + |
| " static void initDims() { }" + |
| " static void initValues() { }" + |
| "}"; |
| } |
| }); |
| |
| sourceOracle.addOrReplace(new MockJavaResource("java.lang.CharSequence") { |
| @Override |
| public CharSequence getContent() { |
| return "package java.lang; public interface CharSequence {}"; |
| } |
| }); |
| |
| sourceOracle.addOrReplace(new MockJavaResource("com.google.gwt.lang.SeedUtil") { |
| @Override |
| public CharSequence getContent() { |
| return "package com.google.gwt.lang; public class SeedUtil {" + |
| "public static Object defineSeed(int id, int seed, Object map){return null;}}"; |
| } |
| }); |
| |
| sourceOracle.addOrReplace(new MockJavaResource("com.google.gwt.core.client.impl.Impl") { |
| @Override |
| public CharSequence getContent() { |
| return "package com.google.gwt.core.client.impl; public class Impl {"+ |
| "public static Object registerEntry(){return null;}}"; |
| } |
| }); |
| |
| sourceOracle.addOrReplace(new MockJavaResource("com.google.gwt.lang.CollapsedPropertyHolder") { |
| @Override |
| public CharSequence getContent() { |
| return "package com.google.gwt.lang; public class CollapsedPropertyHolder {" + |
| "public static int permutationId = -1;}"; |
| } |
| }); |
| |
| sourceOracle.addOrReplace(new MockJavaResource("com.google.gwt.core.client.JavaScriptObject") { |
| @Override |
| public CharSequence getContent() { |
| return "package com.google.gwt.core.client; public class JavaScriptObject {" + |
| "public static Object createArray() {return null;}" + |
| "public static Object createObject() {return null;}}"; |
| } |
| }); |
| |
| sourceOracle.addOrReplace(new MockJavaResource("com.google.gwt.core.client.GWT") { |
| @Override |
| public CharSequence getContent() { |
| return "package com.google.gwt.core.client; public class GWT {"+ |
| "public static void runAsync(RunAsyncCallback cb){}}"; |
| } |
| }); |
| |
| sourceOracle.addOrReplace(new MockJavaResource("com.google.gwt.core.client.RunAsyncCallback") { |
| @Override |
| public CharSequence getContent() { |
| return "package com.google.gwt.core.client; public class RunAsyncCallback {" + |
| "public void onFailure(Throwable reason) {}" + |
| "public void onSuccess() {}}"; |
| } |
| }); |
| } |
| } |