Fixes CodeSplitter to address a third load-order
dependency: instance methods cannot be loaded
before their enclosing type is loaded.

Review by: bobv (TBR)


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@3983 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
index 63cd8ea..d0e5e1f 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
@@ -91,9 +91,8 @@
             JsFunction func = (JsFunction) expr;
             if (func.getName() != null) {
               JMethod method = map.nameToMethod(func.getName());
-              if (method != null && method.getEnclosingType() != null) {
-                System.out.println(method.getEnclosingType().getName() + "."
-                    + JProgram.getJsniSig(method));
+              if (method != null) {
+                System.out.println(fullNameString(method));
               }
             }
           }
@@ -212,6 +211,11 @@
     new CodeSplitter(logger, jprogram, jsprogram, map).execImpl();
   }
 
+  private static String fullNameString(JMethod method) {
+    return method.getEnclosingType().getName() + "."
+        + JProgram.getJsniSig(method);
+  }
+
   private static <T> int getOrZero(Map<T, Integer> map, T key) {
     Integer value = map.get(key);
     return (value == null) ? 0 : value;
@@ -245,6 +249,7 @@
   private final TreeLogger logger;
   private final boolean logging;
   private JavaToJavaScriptMap map;
+  private final Set<JMethod> methodsInJavaScript;
   private final int numEntries;
 
   private CodeSplitter(TreeLogger logger, JProgram jprogram,
@@ -263,6 +268,8 @@
     initiallyLive = new ControlFlowAnalyzer(jprogram);
     traverseEntry(initiallyLive, 0);
     initiallyLive.finishTraversal();
+
+    methodsInJavaScript = fragmentExtractor.findAllMethodsInJavaScript();
   }
 
   /**
@@ -318,6 +325,7 @@
     for (int entry = 0; entry < numEntries; entry++) {
       traverseEntry(everything, entry);
     }
+    everything.traverseFromLeftoversFragmentHasLoaded();
     everything.finishTraversal();
     return everything;
   }
@@ -399,6 +407,7 @@
    * dependencies to fragment 0, so they are sure to be available.
    */
   private void fixUpLoadOrderDependencies(FragmentMap fragmentMap) {
+    fixUpLoadOrderDependenciesForMethods(fragmentMap);
     fixUpLoadOrderDependenciesForTypes(fragmentMap);
     fixUpLoadOrderDependenciesForClassLiterals(fragmentMap);
   }
@@ -433,14 +442,48 @@
   }
 
   /**
-   * The setup code for a class cannot be loaded before the setup code for its
-   * superclass.
+   * Fixes up the load-order dependencies from instance methods to their
+   * enclosing types.
+   */
+  private void fixUpLoadOrderDependenciesForMethods(FragmentMap fragmentMap) {
+    int numFixups = 0;
+
+    for (JReferenceType type : jprogram.getDeclaredTypes()) {
+      int typeFrag = getOrZero(fragmentMap.types, type);
+
+      if (typeFrag != 0) {
+        /*
+         * If the type is in an exclusive fragment, all its instance methods
+         * must be in the same one.
+         */
+        for (JMethod method : type.methods) {
+          if (!method.isStatic() && methodsInJavaScript.contains(method)) {
+            int methodFrag = getOrZero(fragmentMap.methods, method);
+            if (methodFrag != typeFrag) {
+              fragmentMap.types.put(type, 0);
+              numFixups++;
+              break;
+            }
+          }
+        }
+      }
+    }
+
+    logger.log(TreeLogger.DEBUG,
+        "Fixed up load-order dependencies for instance methods by moving "
+            + numFixups + " types to fragment 0, out of "
+            + jprogram.getDeclaredTypes().size());
+  }
+
+  /**
+   * Fixes up load order dependencies from types to their supertypes.
    */
   private void fixUpLoadOrderDependenciesForTypes(FragmentMap fragmentMap) {
     int numFixups = 0;
     Queue<JReferenceType> typesToCheck = new ArrayBlockingQueue<JReferenceType>(
         jprogram.getDeclaredTypes().size());
     typesToCheck.addAll(jprogram.getDeclaredTypes());
+
     while (!typesToCheck.isEmpty()) {
       JReferenceType type = typesToCheck.remove();
       if (type.extnds != null) {
@@ -453,9 +496,11 @@
         }
       }
     }
-    logger.log(TreeLogger.DEBUG, "Fixed up load-order dependencies by moving "
-        + numFixups + " types to fragment 0, out of "
-        + jprogram.getDeclaredTypes().size());
+
+    logger.log(TreeLogger.DEBUG,
+        "Fixed up load-order dependencies on supertypes by moving " + numFixups
+            + " types to fragment 0, out of "
+            + jprogram.getDeclaredTypes().size());
   }
 
   /**
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
index 3f9df05..a3a75eb 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
@@ -270,6 +270,24 @@
     return extractedStats;
   }
 
+  /**
+   * Find all Java methods that still exist in the resulting JavaScript, even
+   * after JavaScript inlining and pruning.
+   */
+  public Set<JMethod> findAllMethodsInJavaScript() {
+    Set<JMethod> methodsInJs = new HashSet<JMethod>();
+    for (int frag = 0; frag < jsprogram.getFragmentCount(); frag++) {
+      List<JsStatement> stats = jsprogram.getFragmentBlock(frag).getStatements();
+      for (JsStatement stat : stats) {
+        JMethod method = methodFor(stat);
+        if (method != null) {
+          methodsInJs.add(method);
+        }
+      }
+    }
+    return methodsInJs;
+  }
+
   public void setStatementLogger(StatementLogger logger) {
     statementLogger = logger;
   }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
index c06573a..a05cb76 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
@@ -301,12 +301,9 @@
 
       // my global name
       JsName globalName;
-      if (x.getEnclosingType() == null) {
-        globalName = topScope.declareName(name);
-      } else {
-        String mangleName = mangleNameForGlobal(x);
-        globalName = topScope.declareName(mangleName, name);
-      }
+      assert x.getEnclosingType() != null;
+      String mangleName = mangleNameForGlobal(x);
+      globalName = topScope.declareName(mangleName, name);
       names.put(x, globalName);
 
       JsFunction jsFunction;
@@ -1964,9 +1961,6 @@
     GenerateJavaScriptVisitor generator = new GenerateJavaScriptVisitor();
     generator.accept(program);
     final Map<JsName, JMethod> nameToMethodMap = new HashMap<JsName, JMethod>();
-    for (JAbstractMethodBody body : methodBodyMap.keySet()) {
-      nameToMethodMap.put(methodBodyMap.get(body).getName(), body.getMethod());
-    }
     final HashMap<JsName, JField> nameToFieldMap = new HashMap<JsName, JField>();
     final HashMap<JsName, JReferenceType> constructorNameToTypeMap = new HashMap<JsName, JReferenceType>();
     for (JReferenceType type : program.getDeclaredTypes()) {
@@ -1982,6 +1976,12 @@
           }
         }
       }
+      for (JMethod method : type.methods) {
+        JsName methodName = names.get(method);
+        if (methodName != null) {
+          nameToMethodMap.put(methodName, method);
+        }
+      }
     }
 
     // TODO(spoon): Instead of gathering the information here, get it via