Place class literal in a correct fragment for -XenableClosureFormat.
When -XenbleClosureFormat is specified the code that links class
literals to the class variable is emitted with the class definition.
This situation generates a new load order dependecy.
This patch makes sure that class literal creation happens before
class definition, and also makes sure that if a enum class literal
is moved then also are the corresponding values() and valueOf()
methods.
Change-Id: I1cb32ce70dd37e4c3a7b0e6a933e529dd0347302
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/codesplitter/CodeSplitter.java b/dev/core/src/com/google/gwt/dev/jjs/impl/codesplitter/CodeSplitter.java
index 418b186..baa6e4d 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/codesplitter/CodeSplitter.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/codesplitter/CodeSplitter.java
@@ -216,7 +216,7 @@
private final TreeLogger logger;
private final boolean logFragmentMap;
private final JavaToJavaScriptMap map;
- private final Set<JMethod> methodsInJavaScript;
+ private final Set<JMethod> methodsStillInJavaScript;
private final List<Fragment> fragments = Lists.newArrayList();
@@ -238,7 +238,7 @@
initiallyLiveCfa = computeInitiallyLive(jprogram, dependencyRecorder);
- methodsInJavaScript = fragmentExtractor.findAllMethodsInJavaScript();
+ methodsStillInJavaScript = fragmentExtractor.findAllMethodsStillInJavaScript();
// TODO(rluble): expected fragment count is not enforced. the actual number
// of fragments may be more or less....
@@ -341,7 +341,7 @@
computeNotExclusiveCfaForFragments(exclusiveFragments);
ExclusivityMap exclusivityMap = ExclusivityMap.computeExclusivityMap(exclusiveFragments,
completeCfa, notExclusiveCfaByFragment);
- exclusivityMap.fixUpLoadOrderDependencies(logger, jprogram, methodsInJavaScript);
+ exclusivityMap.fixUpLoadOrderDependencies(logger, jprogram, methodsStillInJavaScript);
return exclusivityMap;
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/codesplitter/ExclusivityMap.java b/dev/core/src/com/google/gwt/dev/jjs/impl/codesplitter/ExclusivityMap.java
index 02e72bd..7e13e74 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/codesplitter/ExclusivityMap.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/codesplitter/ExclusivityMap.java
@@ -25,7 +25,9 @@
import com.google.gwt.dev.jjs.ast.JNode;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JRunAsync;
+import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.jjs.ast.JVisitor;
+import com.google.gwt.dev.jjs.ast.js.JsniMethodRef;
import com.google.gwt.dev.jjs.impl.ControlFlowAnalyzer;
import com.google.gwt.dev.js.ast.JsStatement;
import com.google.gwt.thirdparty.guava.common.base.Predicates;
@@ -156,21 +158,34 @@
private Map<JDeclaredType, Fragment> fragmentForType = Maps.newHashMap();
/**
- * Traverse {@code exp} and find all referenced JFields.
+ * Traverse {@code exp} and find all referenced class literals.
*/
- private static Set<JClassLiteral> classLiteralsIn(JExpression exp) {
+ private static Set<JClassLiteral> classLiteralsIn(JExpression expression) {
final Set<JClassLiteral> literals = Sets.newHashSet();
- class ClassLiteralFinder extends JVisitor {
+ new JVisitor() {
@Override
public void endVisit(JClassLiteral classLiteral, Context ctx) {
literals.add(classLiteral);
}
- }
- (new ClassLiteralFinder()).accept(exp);
+ }.accept(expression);
return literals;
}
/**
+ * Traverse {@code exp} and find all referenced JMethods.
+ */
+ private static Set<JMethod> methodsReferencesIn(JExpression expression) {
+ final Set<JMethod> methods = Sets.newHashSet();
+ new JVisitor() {
+ @Override
+ public void endVisit(JsniMethodRef jsniMethodRef, Context ctx) {
+ methods.add(jsniMethodRef.getTarget());
+ }
+ }.accept(expression);
+ return methods;
+ }
+
+ /**
* Map atoms to exclusive fragments. Do this by trying to find code atoms that
* are only needed by a single split point. Such code can be moved to the
* exclusively live fragment associated with that split point.
@@ -217,10 +232,10 @@
* </p>
*/
public void fixUpLoadOrderDependencies(TreeLogger logger, JProgram jprogram,
- Set<JMethod> methodsInJavaScript) {
- fixUpLoadOrderDependenciesForMethods(logger, jprogram, methodsInJavaScript);
+ Set<JMethod> methodsStillInJavaScript) {
+ fixUpLoadOrderDependenciesForMethods(logger, jprogram, methodsStillInJavaScript);
fixUpLoadOrderDependenciesForTypes(logger, jprogram);
- fixUpLoadOrderDependenciesForClassLiterals(logger, jprogram);
+ fixUpLoadOrderDependenciesForClassLiterals(logger, jprogram, methodsStillInJavaScript);
}
/**
@@ -263,7 +278,8 @@
* Make sure that the strings are available for all class literals at the time they are
* loaded and make sure that superclass class literals are loaded before.
*/
- private void fixUpLoadOrderDependenciesForClassLiterals(TreeLogger logger, JProgram jprogram) {
+ private void fixUpLoadOrderDependenciesForClassLiterals(
+ TreeLogger logger, JProgram jprogram, Set<JMethod> methodsStillInJavaScript) {
int numFixups = 0;
/**
* Consider all static fields of ClassLiteralHolder; the majority if not all its static
@@ -280,31 +296,61 @@
}
Fragment classLiteralFragment = fragmentForField.get(field);
+
+ // In -XenableClosureFormat creation of class literals needs to happen before or with class
+ // definition. This fixup takes care when it is not the case.
+ JType type = jprogram.getTypeByClassLiteralField(field);
+ Fragment classLiteralTypeFragment = fragmentForType.get(type);
+ if (!canReferenceAtomsFrom(classLiteralTypeFragment, classLiteralFragment)) {
+ numFixups++;
+ fragmentForField.put(field, NOT_EXCLUSIVE);
+ classLiteralFragment = NOT_EXCLUSIVE;
+ }
+
JExpression initializer = field.getInitializer();
- // Fixup the class literals.
+ // Fixup the superclass class literals.
for (JClassLiteral superclassClassLiteral : classLiteralsIn(initializer)) {
JField superclassClassLiteralField = superclassClassLiteral.getField();
// Fix the super class literal and add it to the reexamined.
Fragment superclassClassLiteralFragment = fragmentForField.get(superclassClassLiteralField);
- if (!fragmentsAreConsistent(classLiteralFragment, superclassClassLiteralFragment)) {
+ if (!canReferenceAtomsFrom(classLiteralFragment, superclassClassLiteralFragment)) {
numFixups++;
fragmentForField.put(superclassClassLiteralField, NOT_EXCLUSIVE);
- // Add the field back so that its superclass classliteral gets fixed if necessary.
+ // Add the field back so that its superclass class literal gets fixed if necessary.
potentialClassLiteralFields.add(superclassClassLiteralField);
}
}
+
+ // If there are references to methods move those as well. In particular the enum class
+ // literals reference the static methods values() and valueOf() for the particular enum type
+ // those methods need to be defined before the class literal.
+ for (JMethod referencedMethod : methodsReferencesIn(initializer)) {
+ // Move the referenced methods if necessary.
+ Fragment referencedMethodFragment = fragmentForMethod.get(referencedMethod);
+ if (methodsStillInJavaScript.contains(referencedMethod)
+ && !canReferenceAtomsFrom(classLiteralFragment, referencedMethodFragment)) {
+ assert referencedMethod.isStatic();
+ numFixups++;
+ fragmentForMethod.put(referencedMethod, NOT_EXCLUSIVE);
+ }
+ }
}
+
logger.log(TreeLogger.DEBUG, "Fixed up load-order dependencies by moving " +
numFixups + " fields in class literal constructors to fragment 0, out of " +
numClassLiterals);
}
/**
- * Fixes up the load-order dependencies from instance methods to their enclosing types.
+ * Fixes up the load-order dependencies from instance methods to their enclosing types, in some
+ * cases there is some freedom to place instance methods in one of two or more exclusive
+ * fragment. That scenario arises when an instance method is only accessible after two or
+ * more exclusive fragments have been loaded. In such scenario this fixup will move the method
+ * to the fragment where the type is instantiated.
*/
private void fixUpLoadOrderDependenciesForMethods(TreeLogger logger, JProgram jprogram,
- Set<JMethod> methodsInJavaScript) {
+ Set<JMethod> methodsStillInJavaScript) {
int numFixups = 0;
for (JDeclaredType type : jprogram.getDeclaredTypes()) {
@@ -313,11 +359,11 @@
continue;
}
/*
- * If the type is in an exclusive fragment, all its instance methods
- * must be in the same one.
+ * If the type is in an exclusive fragment, all its instance methods must be in the same one;
+ * if this is not the case move the type to the NOT_EXCLUSIVE fragment.
*/
for (JMethod method : type.getMethods()) {
- if (method.needsDynamicDispatch() && methodsInJavaScript.contains(method)
+ if (method.needsDynamicDispatch() && methodsStillInJavaScript.contains(method)
&& typeFrag != fragmentForMethod.get(method)) {
fragmentForType.put(type, NOT_EXCLUSIVE);
numFixups++;
@@ -345,7 +391,7 @@
if (type.getSuperClass() != null) {
Fragment typeFrag = fragmentForType.get(type);
Fragment supertypeFrag = fragmentForType.get(type.getSuperClass());
- if (!fragmentsAreConsistent(typeFrag, supertypeFrag)) {
+ if (!canReferenceAtomsFrom(typeFrag, supertypeFrag)) {
numFixups++;
fragmentForType.put(type.getSuperClass(), NOT_EXCLUSIVE);
typesToCheck.add(type.getSuperClass());
@@ -364,7 +410,7 @@
/**
* Returns true if atoms in thatFragment are visible from thisFragment.
*/
- private static boolean fragmentsAreConsistent(Fragment thisFragment, Fragment thatFragment) {
+ private static boolean canReferenceAtomsFrom(Fragment thisFragment, Fragment thatFragment) {
return thisFragment == null || thisFragment == thatFragment || !thatFragment.isExclusive();
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/codesplitter/FragmentExtractor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/codesplitter/FragmentExtractor.java
index 507518e..19ad75f 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/codesplitter/FragmentExtractor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/codesplitter/FragmentExtractor.java
@@ -261,7 +261,7 @@
* Find all Java methods that still exist in the resulting JavaScript, even
* after JavaScript inlining and pruning.
*/
- public Set<JMethod> findAllMethodsInJavaScript() {
+ public Set<JMethod> findAllMethodsStillInJavaScript() {
Set<JMethod> methodsInJs = new HashSet<JMethod>();
for (int fragment = 0; fragment < jsprogram.getFragmentCount(); fragment++) {
for (JsStatement statement : jsprogram.getFragmentBlock(fragment).getStatements()) {