/* * Copyright 2013 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.codesplitter; import com.google.gwt.dev.jjs.Correlation; import com.google.gwt.dev.jjs.Correlation.Axis; import com.google.gwt.dev.jjs.CorrelationFactory; import com.google.gwt.dev.jjs.SourceInfo; import com.google.gwt.dev.jjs.SourceOrigin; import com.google.gwt.dev.jjs.ast.AccessModifier; import com.google.gwt.dev.jjs.ast.JClassType; import com.google.gwt.dev.jjs.ast.JConstructor; import com.google.gwt.dev.jjs.ast.JDeclaredType; import com.google.gwt.dev.jjs.ast.JField; import com.google.gwt.dev.jjs.ast.JMethod; import com.google.gwt.dev.jjs.impl.JJSTestBase; import com.google.gwt.dev.jjs.impl.JavaToJavaScriptMap; import com.google.gwt.dev.js.ast.JsExprStmt; import com.google.gwt.dev.js.ast.JsInvocation; import com.google.gwt.dev.js.ast.JsName; import com.google.gwt.dev.js.ast.JsNameRef; import com.google.gwt.dev.js.ast.JsProgram; import com.google.gwt.dev.js.ast.JsStatement; import java.util.List; /** * Unit tests for {@link com.google.gwt.dev.jjs.impl.codesplitter.FragmentExtractor}. */ public class FragmentExtractorTest extends JJSTestBase { private static final JsName DEFINE_CLASS_FUNCTION_NAME = new JsName(null, "defineClass", "defineClass"); private static class MockSourceInfo implements SourceInfo { @Override public void addCorrelation(Correlation c) { } @Override public Correlation getCorrelation(Axis axis) { return null; } @Override public Correlation[] getCorrelations() { return null; } @Override public CorrelationFactory getCorrelator() { return null; } @Override public int getEndPos() { return 0; } @Override public String getFileName() { return null; } @Override public SourceOrigin getOrigin() { return null; } @Override public int getStartLine() { return 0; } @Override public int getStartPos() { return 0; } @Override public SourceInfo makeChild() { return null; } @Override public SourceInfo makeChild(SourceOrigin origin) { return null; } } private static class MockJavaToJavaScriptMap implements JavaToJavaScriptMap { @Override public JsName nameForField(JField field) { return null; } @Override public JsName nameForMethod(JMethod method) { return null; } @Override public JsName nameForType(JDeclaredType type) { return null; } @Override public JField nameToField(JsName name) { return null; } @Override public JMethod nameToMethod(JsName name) { return null; } @Override public JClassType nameToType(JsName name) { return null; } @Override public JClassType typeForStatement(JsStatement stat) { return null; } @Override public JMethod methodForStatement(JsStatement stat) { return null; } } private static class MockLivenessPredicate implements LivenessPredicate { @Override public boolean isLive(JDeclaredType type) { return false; } @Override public boolean isLive(JField field) { return false; } @Override public boolean isLive(JMethod method) { return false; } @Override public boolean miscellaneousStatementsAreLive() { return false; } } /** * Invokes FragmentExtractor with a fragment description claiming that Bar was not made live by * the current fragment, but that it has a constructor which *was* made live. Verifies that the * defineClass invocation from the global JS block *is* included in the extracted statement output. */ public void testDefineClass_DeadTypeLiveConstructor() { FragmentExtractor fragmentExtractor; LivenessPredicate constructorLivePredicate; // Environment setup. { SourceInfo nullSourceInfo = new MockSourceInfo(); final JClassType barType = new JClassType(nullSourceInfo, "Bar", false, false); final JsName barConstructorName = new JsName(null, "Bar", "Bar"); final JConstructor barConstructor = new JConstructor(nullSourceInfo, barType, AccessModifier.PUBLIC); final JsExprStmt defineClassStatement = createDefineClassStatement(barConstructorName); JsProgram jsProgram = new JsProgram(); // Defines the entirety of the JS program being split, to be the one defineClass statement. jsProgram.getGlobalBlock().getStatements().add(defineClassStatement); JavaToJavaScriptMap map = new MockJavaToJavaScriptMap() { @Override public JMethod nameToMethod(JsName name) { if (name == barConstructorName) { // Finds the Bar constructor by name. return barConstructor; } return null; } @Override public JClassType typeForStatement(JsStatement statement) { if (statement == defineClassStatement) { // Indicates that Bar is the type associated with the defineClass statement. return barType; } return null; } }; fragmentExtractor = new FragmentExtractor(jsProgram, map, null, DEFINE_CLASS_FUNCTION_NAME); constructorLivePredicate = new MockLivenessPredicate() { @Override public boolean isLive(JDeclaredType type) { // Claims that Bar is not made live by the current fragment. return false; } @Override public boolean isLive(JMethod method) { // Claims that the bar Constructor *is* made live by the current fragment. return method == barConstructor; } }; } List<JsStatement> extractedStatements = fragmentExtractor.extractStatements(constructorLivePredicate, new NothingAlivePredicate()); // Asserts that the single defineClass statement was included in the extraction output. assertEquals(1, extractedStatements.size()); JsStatement defineClassStatement = extractedStatements.get(0); assertTrue(defineClassStatement.toString().contains("defineClass")); } private JsExprStmt createDefineClassStatement(final JsName barConstructorName) { SourceInfo nullSourceInfo = new MockSourceInfo(); JsInvocation defineClassInvocation = new JsInvocation(nullSourceInfo, new JsNameRef(nullSourceInfo, DEFINE_CLASS_FUNCTION_NAME), new JsNameRef(nullSourceInfo, barConstructorName)); return defineClassInvocation.makeStmt(); } }