- Fixes a bug in GenerateJavaAST where the Iterator variable of a foreach was assigned the wrong type, causing all kinds of madness.
- Fixes a latent problem where referencing a generic field fails to generate an appropriate cast operation.
- Adds gwt.jjs.traceMethods to the compiler for easy method change tracking

Found by: jaimeyap
Review by: spoon (pair prog)


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2171 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
index e2976b3..e008c91 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
@@ -20,13 +20,20 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 
 /**
  * A Java method implementation.
  */
 public final class JMethod extends JNode implements HasEnclosingType, HasName,
-    HasSettableType, CanBeAbstract, CanBeSetFinal, CanBeNative,
-    CanBeStatic {
+    HasSettableType, CanBeAbstract, CanBeSetFinal, CanBeNative, CanBeStatic {
+
+  private static void trace(String title, String code) {
+    System.out.println("---------------------------");
+    System.out.println(title + ":");
+    System.out.println("---------------------------");
+    System.out.println(code);
+  }
 
   /**
    * References to any methods which this method overrides. This should be an
@@ -46,6 +53,8 @@
   private final String name;
   private ArrayList<JType> originalParamTypes;
   private JType returnType;
+  private boolean trace = false;
+  private boolean traceFirst = true;
 
   /**
    * These are only supposed to be constructed by JProgram.
@@ -72,6 +81,22 @@
       JParameter param = params.get(i);
       originalParamTypes.add(param.getType());
     }
+
+    // Determine if we should trace this method.
+    if (enclosingType != null) {
+      String jsniSig = JProgram.getJsniSig(this);
+      Set<String> set = JProgram.traceMethods.get(enclosingType.getName());
+      if (set != null && (set.contains(name) || set.contains(jsniSig))) {
+        trace = true;
+      }
+      // Try the short name.
+      if (!trace && enclosingType != null) {
+        set = JProgram.traceMethods.get(enclosingType.getShortName());
+        if (set != null && (set.contains(name) || set.contains(jsniSig))) {
+          trace = true;
+        }
+      }
+    }
   }
 
   public JAbstractMethodBody getBody() {
@@ -138,6 +163,14 @@
   }
 
   public void traverse(JVisitor visitor, Context ctx) {
+    String before = null;
+    if (trace && visitor instanceof JModVisitor) {
+      before = this.toSource();
+      if (traceFirst) {
+        traceFirst = false;
+        trace("Initial", before);
+      }
+    }
     if (visitor.visit(this, ctx)) {
       visitor.accept(params);
       if (body != null) {
@@ -145,6 +178,16 @@
       }
     }
     visitor.endVisit(this, ctx);
+    if (trace && visitor instanceof JModVisitor) {
+      String after = this.toSource();
+      if (!after.equals(before)) {
+        String title = visitor.getClass().getSimpleName();
+        trace(title, after);
+      }
+    }
   }
 
+  void copyTraceStatusFrom(JMethod x) {
+    this.trace = x.trace;
+  }
 }
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 9bc354a..9cbbc04 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
@@ -50,6 +50,8 @@
           "com.google.gwt.lang.Exceptions", "com.google.gwt.lang.LongLib",
           "com.google.gwt.lang.Stats",}));
 
+  static final Map<String, Set<String>> traceMethods = new HashMap<String, Set<String>>();
+
   private static final Set<String> INDEX_TYPES_SET = new HashSet<String>(
       Arrays.asList(new String[] {
           "java.lang.Object", "java.lang.String", "java.lang.Class",
@@ -67,6 +69,43 @@
 
   static {
     INDEX_TYPES_SET.addAll(CODEGEN_TYPES_SET);
+
+    /*
+     * The format to trace methods is a colon-separated list of
+     * "className.methodName", such as "Hello.onModuleLoad:Foo.bar". You can
+     * fully-qualify a class to disambiguate classes, and you can also append
+     * the JSNI signature of the method to disambiguate overloads, ala
+     * "Foo.bar(IZ)".
+     */
+    String toTrace = System.getProperty("gwt.jjs.traceMethods");
+    if (toTrace != null) {
+      String[] split = toTrace.split(":");
+      for (String str : split) {
+        int pos = str.lastIndexOf('.');
+        if (pos > 0) {
+          String className = str.substring(0, pos);
+          String methodName = str.substring(pos + 1);
+          Set<String> set = traceMethods.get(className);
+          if (set == null) {
+            set = new HashSet<String>();
+            traceMethods.put(className, set);
+          }
+          set.add(methodName);
+        }
+      }
+    }
+  }
+
+  public static String getJsniSig(JMethod method) {
+    StringBuffer sb = new StringBuffer();
+    sb.append(method.getName());
+    sb.append("(");
+    for (int i = 0; i < method.getOriginalParamTypes().size(); ++i) {
+      JType type = method.getOriginalParamTypes().get(i);
+      sb.append(type.getJsniSignatureName());
+    }
+    sb.append(")");
+    return sb.toString();
   }
 
   public static boolean methodsDoMatch(JMethod method1, JMethod method2) {
@@ -701,6 +740,7 @@
   public void putStaticImpl(JMethod method, JMethod staticImpl) {
     instanceToStaticMap.put(method, staticImpl);
     staticToInstanceMap.put(staticImpl, method);
+    staticImpl.copyTraceStatusFrom(method);
   }
 
   public JClassType rebind(JType type) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
index fd392c4..b51c265 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
@@ -278,7 +278,7 @@
             for (int i = 0; i < type.methods.size(); ++i) {
               JMethod method = type.methods.get(i);
               if (method.getName().equals(methodName)) {
-                String jsniSig = getJsniSig(method);
+                String jsniSig = JProgram.getJsniSig(method);
                 if (jsniSig.equals(rhs)) {
                   return method;
                 } else if (almostMatches == null) {
@@ -387,18 +387,6 @@
       }
     }
 
-    private static String getJsniSig(JMethod method) {
-      StringBuffer sb = new StringBuffer();
-      sb.append(method.getName());
-      sb.append("(");
-      for (int i = 0; i < method.getOriginalParamTypes().size(); ++i) {
-        JType type = method.getOriginalParamTypes().get(i);
-        sb.append(type.getJsniSignatureName());
-      }
-      sb.append(")");
-      return sb.toString();
-    }
-
     private static InternalCompilerException translateException(JNode node,
         Throwable e) {
       InternalCompilerException ice;
@@ -1190,6 +1178,7 @@
     JExpression processExpression(FieldReference x) {
       SourceInfo info = makeSourceInfo(x);
       FieldBinding fieldBinding = x.binding;
+      JType type = (JType) typeMap.get(x.resolvedType);
       JField field;
       if (fieldBinding.declaringClass == null) {
         // probably array.length
@@ -1203,7 +1192,14 @@
       JExpression instance = dispProcessExpression(x.receiver);
       JExpression fieldRef = new JFieldRef(program, info, instance, field,
           currentClass);
-      return fieldRef;
+
+      if (type != field.getType()) {
+        // Must be a generic; insert a cast operation.
+        JReferenceType toType = (JReferenceType) type;
+        return new JCastOperation(program, info, toType, fieldRef);
+      } else {
+        return fieldRef;
+      }
     }
 
     JExpression processExpression(InstanceOfExpression x) {
@@ -1753,7 +1749,7 @@
          * </pre>
          */
         JLocal iteratorVar = createSyntheticLocal(info, elementVarName
-            + "$iterator", (JType) typeMap.get(x.collection.resolvedType));
+            + "$iterator", program.getIndexedType("Iterator"));
 
         List<JStatement> initializers = new ArrayList<JStatement>(1);
         // Iterator<T> i$iterator = collection.iterator()
@@ -1766,10 +1762,20 @@
             createVariableRef(info, iteratorVar),
             program.getIndexedMethod("Iterator.hasNext"));
 
-        // T elementVar = i$array[i$index];
-        elementDecl.initializer = new JMethodCall(program, info,
+        // T elementVar = (T) i$iterator.next();
+        JMethodCall nextCall = new JMethodCall(program, info,
             createVariableRef(info, iteratorVar),
             program.getIndexedMethod("Iterator.next"));
+
+        JType elementType = elementDecl.getVariableRef().getType();
+        if (elementType != nextCall.getType()) {
+          // Must be a generic; insert a cast operation.
+          elementDecl.initializer = new JCastOperation(program, info,
+              elementType, nextCall);
+        } else {
+          elementDecl.initializer = nextCall;
+        }
+
         body.statements.add(0, elementDecl);
 
         result = new JForStatement(program, info, initializers, condition,