blob: 778f0642b79a1bf9207b4d5269883ec4e27636c0 [file] [log] [blame]
/*
* 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.JsFunction;
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.JsRootScope;
import com.google.gwt.dev.js.ast.JsStatement;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 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(JClassType 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 vtableInitToMethod(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);
Map<String, JsFunction> functionsByName = new HashMap<String, JsFunction>();
functionsByName.put("JavaClassHierarchySetupUtil.defineClass",
new JsFunction(nullSourceInfo, new JsRootScope(), DEFINE_CLASS_FUNCTION_NAME));
final JsExprStmt defineClassStatement = createDefineClassStatement(barConstructorName);
JsProgram jsProgram = new JsProgram();
jsProgram.setIndexedFunctions(functionsByName);
// 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(null, jsProgram, map);
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();
}
}