Refactoring of $clinit and $init method accessors.

Change-Id: I5caab1249df99e44d0113481a596536897833a43
Review-Link: https://gwt-review.googlesource.com/#/c/1171/


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@11375 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
index 7b127d2..2e3270a 100755
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JDeclaredType.java
@@ -27,7 +27,22 @@
 import java.util.List;
 
 /**
- * Base class for any reference type.
+ * Base class for any declared type.
+ *
+ * Declared types have fields and methods. Two of the methods are treated specially: the class
+ * initializer method (named <code>$clinit</code>) and the instance initializer method
+ * (named <code>$init</code>).
+ *
+ * The class initializer method is responsible for initializing all class variables as well as
+ * those of the superclasses (by calling the superclass class initializer method).
+ *
+ * The instance initializer is responsible for initializing all instance variables as well as those
+ * of the superclasses (by calling the superclass instance initializer method).
+ *
+ * Optimizations may eliminate class initializers (<code>$clinit</code>) if no static variables need
+ * initialization, and use the private variable <code>clinitTarget</code>to keep track which
+ * initializer in the superclass chain needs to be called.
+ *
  */
 public abstract class JDeclaredType extends JReferenceType {
 
@@ -48,8 +63,8 @@
   protected transient List<JMethod> methods = Lists.create();
 
   /**
-   * Tracks the target static initialization for this class. Default to self
-   * until removed or set to be a superclass.
+   * Tracks the target static initialization for this class. Default to self (if it has a non
+   * empty initializer) or point to a superclass or be null.
    */
   private JDeclaredType clinitTarget = this;
 
@@ -100,8 +115,12 @@
   /**
    * Adds a method to this type.
    */
-  public void addMethod(JMethod method) {
+  public final void addMethod(JMethod method) {
     assert method.getEnclosingType() == this;
+    assert !method.getName().equals("$clinit") || getMethods().size() == 0 : "Attempted adding "
+        + "$clinit method with index != 0";
+    assert !method.getName().equals("$init") || getMethods().size() == 1 : "Attempted adding $init "
+        + "method with index != 1";
     methods = Lists.add(methods, method);
   }
 
@@ -140,10 +159,40 @@
   }
 
   /**
+   * Returns the instance initializer ($init) method.
+   * Can only be called after making sure the class has an instance initializer method.
+   *
+   * @return The instance initializer method.
+   */
+  public final JMethod getInitMethod() {
+    assert getMethods().size() > 1;
+    JMethod init = this.getMethods().get(1);
+
+    assert init != null;
+    assert init.getName().equals("$init");
+    return init;
+  }
+
+  /**
+   * Returns the class initializer method.
+   * Can only be called after making sure the class has a class initializer method.
+   *
+   * @return The class initializer method.
+   */
+   public final JMethod getClinitMethod() {
+     assert getMethods().size() != 0;
+     JMethod clinit = this.getMethods().get(0);
+
+     assert clinit != null;
+     assert clinit.getName().equals("$clinit");
+     return clinit;
+  }
+
+  /**
    * Returns the class that must be initialized to use this class. May be a
    * superclass, or <code>null</code> if this class has no static initializer.
    */
-  public JDeclaredType getClinitTarget() {
+  public final JDeclaredType getClinitTarget() {
     return clinitTarget;
   }
 
@@ -186,7 +235,7 @@
    * Returns this type's declared methods; does not include methods defined in a
    * super type unless they are overridden by this type.
    */
-  public List<JMethod> getMethods() {
+  public final List<JMethod> getMethods() {
     return methods;
   }
 
@@ -218,7 +267,7 @@
    * Removes the field at the specified index.
    */
   public void removeField(int i) {
-    assert !isExternal() : "External types can not be modiified.";
+    assert !isExternal() : "External types can not be modified.";
     fields = Lists.remove(fields, i);
   }
 
@@ -226,11 +275,23 @@
    * Removes the method at the specified index.
    */
   public void removeMethod(int i) {
-    assert !isExternal() : "External types can not be modiified.";
+    assert !isExternal() : "External types can not be modified.";
     methods = Lists.remove(methods, i);
   }
 
   /**
+   * Resets the clinitTarget to the current class. Used by optimizations that move initializers from
+   * superclasses down.
+   *
+   * Prerequisite: the $clinit method must exist and be non empty.
+   */
+  public void resetClinitTarget() {
+    assert !((JMethodBody) getClinitMethod().getBody()).getStatements().isEmpty() : "Attempted to "
+        + "reset the clinitTarget to an empty $clinit";
+    this.clinitTarget = this;
+  }
+
+  /**
    * Resolves external references during AST stitching.
    */
   public void resolve(List<JInterfaceType> resolvedInterfaces, List<JNode> resolvedRescues) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
index ad4542f..bcc1331 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
@@ -231,7 +231,7 @@
 
   public static boolean isClinit(JMethod method) {
     JDeclaredType enclosingType = method.getEnclosingType();
-    if ((enclosingType != null) && (method == enclosingType.getMethods().get(0))) {
+    if ((enclosingType != null) && (method == enclosingType.getClinitMethod())) {
       assert (method.getName().equals("$clinit"));
       return true;
     } else {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
index 8850cb6..5963dd1 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
@@ -729,7 +729,7 @@
     // Track that we've been seen.
     alreadySeen.add(type);
 
-    JMethod method = type.getMethods().get(0);
+    JMethod method = type.getClinitMethod();
     assert (JProgram.isClinit(method));
     CheckClinitVisitor v = new CheckClinitVisitor();
     v.accept(method);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
index f9b3aaa..93485dd 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
@@ -226,7 +226,7 @@
 
       // Rescue my clinit (it won't ever be explicitly referenced)
       if (type.hasClinit()) {
-        rescue(type.getMethods().get(0));
+        rescue(type.getClinitMethod());
       }
 
       // JLS 12.4.1: don't rescue my super interfaces just because I'm rescued.
@@ -304,7 +304,7 @@
 
       // Rescue my clinit (it won't ever be explicitly referenced)
       if (type.hasClinit()) {
-        rescue(type.getMethods().get(0));
+        rescue(type.getClinitMethod());
       }
 
       // JLS 12.4.1: don't rescue my super interfaces just because I'm rescued.
@@ -671,7 +671,7 @@
             JField field = (JField) var;
             accept(field.getInitializer());
             referencedTypes.add(field.getEnclosingType());
-            liveFieldsAndMethods.add(field.getEnclosingType().getMethods().get(0));
+            liveFieldsAndMethods.add(field.getEnclosingType().getClinitMethod());
           } else if (argsToRescueIfParameterRead != null && var instanceof JParameter) {
             List<JExpression> list = argsToRescueIfParameterRead.remove(var);
             if (list != null) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java b/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
index 7013f53..c44be1a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
@@ -703,7 +703,7 @@
     }
 
     private JMethodCall createClinitCall(SourceInfo sourceInfo, JDeclaredType targetType) {
-      JMethod clinit = targetType.getClinitTarget().getMethods().get(0);
+      JMethod clinit = targetType.getClinitTarget().getClinitMethod();
       assert (JProgram.isClinit(clinit));
       return new JMethodCall(sourceInfo, null, clinit);
     }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/EnumOrdinalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/EnumOrdinalizer.java
index 959abc1..d7ea10c 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/EnumOrdinalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/EnumOrdinalizer.java
@@ -667,10 +667,9 @@
 
       if (canBeOrdinal(x)) {
         /*
-         * Cleanup clinit method for ordinalizable enums. Note, method 0 is
-         * always the clinit.
+         * Cleanup clinit method for ordinalizable enums.
          */
-        updateClinit(x.getMethods().get(0));
+        updateClinit(x.getClinitMethod());
 
         /*
          * Remove any static impl mappings for any methods in an ordinal enum
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 1616123..81ad168 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
@@ -1187,7 +1187,7 @@
           return;
         } else if (type != clinitTarget) {
           // replace the method with its retargeted clinit
-          method = clinitTarget.getMethods().get(0);
+          method = clinitTarget.getClinitMethod();
         }
       }
 
@@ -1774,7 +1774,7 @@
        * refs to super classes are preserved.
        */
       JMethodBody clinitBody =
-          (JMethodBody) program.getTypeClassLiteralHolder().getMethods().get(0).getBody();
+          (JMethodBody) program.getTypeClassLiteralHolder().getClinitMethod().getBody();
       for (JStatement stmt : clinitBody.getStatements()) {
         if (stmt instanceof JDeclarationStatement) {
           generateClassLiteral((JDeclarationStatement) stmt, vars);
@@ -2157,7 +2157,7 @@
         return null;
       }
 
-      JMethod clinitMethod = targetType.getMethods().get(0);
+      JMethod clinitMethod = targetType.getClinitMethod();
       SourceInfo sourceInfo = x.getSourceInfo();
       JsInvocation jsInvocation = new JsInvocation(sourceInfo);
       jsInvocation.setQualifier(names.get(clinitMethod).makeRef(sourceInfo));
@@ -2180,7 +2180,7 @@
         return null;
       }
 
-      JMethod clinitMethod = enclosingType.getClinitTarget().getMethods().get(0);
+      JMethod clinitMethod = enclosingType.getClinitTarget().getClinitMethod();
       SourceInfo sourceInfo = x.getSourceInfo();
       JsInvocation jsInvocation = new JsInvocation(sourceInfo);
       jsInvocation.setQualifier(names.get(clinitMethod).makeRef(sourceInfo));
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
index 30102bc..3ecdefb 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
@@ -686,8 +686,7 @@
          * explicit this constructor call, in which case the callee will.
          */
         if (!hasExplicitThis) {
-          // $init is always in position 1 (clinit is in 0)
-          JMethod initMethod = curClass.type.getMethods().get(1);
+          JMethod initMethod = curClass.type.getInitMethod();
           JMethodCall initCall = new JMethodCall(info, makeThisRef(info), initMethod);
           block.addStmt(initCall.makeStatement());
         }
@@ -1743,11 +1742,11 @@
       JDeclaredType type = curClass.type;
       /*
        * Make clinits chain to super class (JDT doesn't write code to do this).
-       * Call super class $clinit; $clinit is always in position 0.
+       * Call super class $clinit;
        */
       if (type.getSuperClass() != null) {
-        JMethod myClinit = type.getMethods().get(0);
-        JMethod superClinit = type.getSuperClass().getMethods().get(0);
+        JMethod myClinit = type.getClinitMethod();
+        JMethod superClinit = type.getSuperClass().getClinitMethod();
         JMethodCall superClinitCall = new JMethodCall(myClinit.getSourceInfo(), null, superClinit);
         JMethodBody body = (JMethodBody) myClinit.getBody();
         body.getBlock().addStmt(0, superClinitCall.makeStatement());
@@ -2051,7 +2050,7 @@
       JNewArray newExpr = JNewArray.createInitializers(info, enumArrayType, initializers);
       JFieldRef valuesRef = new JFieldRef(info, null, valuesField, type);
       JDeclarationStatement declStmt = new JDeclarationStatement(info, valuesRef, newExpr);
-      JBlock clinitBlock = ((JMethodBody) type.getMethods().get(0).getBody()).getBlock();
+      JBlock clinitBlock = ((JMethodBody) type.getClinitMethod().getBody()).getBlock();
 
       /*
        * HACKY: the $VALUES array must be initialized immediately after all of
@@ -2433,9 +2432,9 @@
     private void pushInitializerMethodInfo(FieldDeclaration x, MethodScope scope) {
       JMethod initMeth;
       if (x.isStatic()) {
-        initMeth = curClass.type.getMethods().get(0);
+        initMeth = curClass.type.getClinitMethod();
       } else {
-        initMeth = curClass.type.getMethods().get(1);
+        initMeth = curClass.type.getInitMethod();
       }
       pushMethodInfo(new MethodInfo(initMeth, (JMethodBody) initMeth.getBody(), scope));
     }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementClassLiteralsAsFields.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementClassLiteralsAsFields.java
index d13c0fc..868cd93 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementClassLiteralsAsFields.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementClassLiteralsAsFields.java
@@ -113,7 +113,7 @@
     this.program = program;
     this.typeClassLiteralHolder = program.getTypeClassLiteralHolder();
     this.classLiteralHolderClinitBody =
-        (JMethodBody) typeClassLiteralHolder.getMethods().get(0).getBody();
+        (JMethodBody) typeClassLiteralHolder.getClinitMethod().getBody();
     assert program.getDeclaredTypes().contains(typeClassLiteralHolder);
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java
index 92eb2bb..beaf790 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java
@@ -110,7 +110,7 @@
         List<JStatement> stmts = body.getStatements();
 
         if (method.getEnclosingType() != null
-            && method.getEnclosingType().getMethods().get(0) == method && !stmts.isEmpty()) {
+            && method.getEnclosingType().getClinitMethod() == method && !stmts.isEmpty()) {
           // clinit() calls cannot be inlined unless they are empty
           possibleToInline = false;
         } else if (!body.getLocals().isEmpty()) {
@@ -177,7 +177,7 @@
         return null;
       }
 
-      JMethod clinit = targetType.getMethods().get(0);
+      JMethod clinit = targetType.getClinitMethod();
 
       // If the clinit is a non-native, empty body we can optimize it out here
       if (!clinit.isNative() && (((JMethodBody) clinit.getBody())).getStatements().size() == 0) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java b/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java
index b6967c4..a7aa3a9 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/UnifyAst.java
@@ -641,7 +641,7 @@
       }
 
       // Special clinit handling.
-      JMethod clinit = type.getMethods().get(0);
+      JMethod clinit = type.getClinitMethod();
       if (!liveFieldsAndMethods.contains(clinit)) {
         clinit.setBody(new JMethodBody(SourceOrigin.UNKNOWN));
       }
@@ -994,7 +994,7 @@
       assert errorsFound;
       return;
     }
-    JMethod clinit = type.getMethods().get(0);
+    JMethod clinit = type.getClinitMethod();
     if (!liveFieldsAndMethods.contains(clinit)) {
       flowInto(clinit);
       if (type.getSuperClass() != null) {