Fix problems with getting parameter names in generated code.  This manifests
itself when, for example, generating i18n output files from messages in a
UiBinder generated source file.  Also, used the actual argument names in the
generated output files for convenience and clarity.

Patch by: jat
Review by: scottb, rjrjr


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7378 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java b/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
index 4d523fe..eb4f340 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationStateBuilder.java
@@ -74,7 +74,7 @@
         ArtificialRescueChecker.check(cud, builder.isGenerated());
         BinaryTypeReferenceRestrictionsChecker.check(cud);
 
-        // TODO: Collect parameter names?
+        MethodArgNamesLookup methodArgs = MethodParamCollector.collect(cud);
 
         CompilationUnitInvalidator.reportErrors(logger, cud,
             builder.getSource());
@@ -82,7 +82,8 @@
         Set<ContentId> dependencies = compiler.computeDependencies(cud,
             jsniDeps);
         CompilationUnit unit = builder.build(compiledClasses, dependencies,
-            jsniMethods.values(), cud.compilationResult().getProblems());
+            jsniMethods.values(), methodArgs,
+            cud.compilationResult().getProblems());
         if (cud.compilationResult().hasErrors()) {
           unit = new ErrorCompilationUnit(unit);
         } else {
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java b/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java
index b54161c..11bb848 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java
@@ -266,6 +266,11 @@
   public abstract long getLastModified();
 
   /**
+   * @return a way to lookup method argument names for this compilation unit.
+   */
+  public abstract MethodArgNamesLookup getMethodArgs();
+
+  /**
    * Returns the source code for this unit.
    */
   @Deprecated
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationUnitBuilder.java b/dev/core/src/com/google/gwt/dev/javac/CompilationUnitBuilder.java
index 6941e9b..d5f3413 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationUnitBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationUnitBuilder.java
@@ -63,9 +63,9 @@
     protected CompilationUnit makeUnit(List<CompiledClass> compiledClasses,
         Set<ContentId> dependencies,
         Collection<? extends JsniMethod> jsniMethods,
-        CategorizedProblem[] problems) {
+        MethodArgNamesLookup methodArgs, CategorizedProblem[] problems) {
       return new GeneratedCompilationUnit(generatedUnit, compiledClasses,
-          dependencies, jsniMethods, problems);
+          dependencies, jsniMethods, methodArgs, problems);
     }
 
     @Override
@@ -148,9 +148,9 @@
     protected CompilationUnit makeUnit(List<CompiledClass> compiledClasses,
         Set<ContentId> dependencies,
         Collection<? extends JsniMethod> jsniMethods,
-        CategorizedProblem[] problems) {
+        MethodArgNamesLookup methodArgs, CategorizedProblem[] problems) {
       return new SourceFileCompilationUnit(getResource(), contentId,
-          compiledClasses, dependencies, jsniMethods, problems);
+          compiledClasses, dependencies, jsniMethods, methodArgs, problems);
     }
   }
 
@@ -161,8 +161,8 @@
     public GeneratedCompilationUnit(GeneratedUnit generatedUnit,
         List<CompiledClass> compiledClasses, Set<ContentId> dependencies,
         Collection<? extends JsniMethod> jsniMethods,
-        CategorizedProblem[] problems) {
-      super(compiledClasses, dependencies, jsniMethods, problems);
+        MethodArgNamesLookup methodArgs, CategorizedProblem[] problems) {
+      super(compiledClasses, dependencies, jsniMethods, methodArgs, problems);
       this.generatedUnit = generatedUnit;
     }
 
@@ -176,6 +176,7 @@
       return generatedUnit.creationTime();
     }
 
+    @Deprecated
     @Override
     public String getSource() {
       return generatedUnit.getSource();
@@ -186,11 +187,13 @@
       return generatedUnit.getTypeName();
     }
 
+    @Deprecated
     @Override
     public boolean isGenerated() {
       return true;
     }
 
+    @Deprecated
     @Override
     public boolean isSuperSource() {
       return false;
@@ -234,10 +237,11 @@
   public CompilationUnit build(List<CompiledClass> compiledClasses,
       Set<ContentId> dependencies,
       Collection<? extends JsniMethod> jsniMethods,
-      CategorizedProblem[] problems) {
+      MethodArgNamesLookup methodArgs, CategorizedProblem[] problems) {
     // Free the source now.
     source = null;
-    return makeUnit(compiledClasses, dependencies, jsniMethods, problems);
+    return makeUnit(compiledClasses, dependencies, jsniMethods, methodArgs,
+        problems);
   }
 
   public abstract ContentId getContentId();
@@ -262,7 +266,8 @@
 
   protected abstract CompilationUnit makeUnit(
       List<CompiledClass> compiledClasses, Set<ContentId> dependencies,
-      Collection<? extends JsniMethod> jsniMethods, CategorizedProblem[] errors);
+      Collection<? extends JsniMethod> jsniMethods,
+      MethodArgNamesLookup methodArgs, CategorizedProblem[] errors);
 
   /**
    * This only matters for {@link ArtificialRescueChecker}.
diff --git a/dev/core/src/com/google/gwt/dev/javac/CompilationUnitImpl.java b/dev/core/src/com/google/gwt/dev/javac/CompilationUnitImpl.java
index f97c545..fcb3c0f 100644
--- a/dev/core/src/com/google/gwt/dev/javac/CompilationUnitImpl.java
+++ b/dev/core/src/com/google/gwt/dev/javac/CompilationUnitImpl.java
@@ -29,35 +29,46 @@
   private final List<CompiledClass> exposedCompiledClasses;
   private final List<JsniMethod> jsniMethods;
   private final CategorizedProblem[] problems;
+  private final MethodArgNamesLookup methodArgs;
 
   public CompilationUnitImpl(List<CompiledClass> compiledClasses,
       Set<ContentId> dependencies,
       Collection<? extends JsniMethod> jsniMethods,
-      CategorizedProblem[] problems) {
+      MethodArgNamesLookup methodArgs, CategorizedProblem[] problems) {
     this.exposedCompiledClasses = Lists.normalizeUnmodifiable(compiledClasses);
     this.dependencies = dependencies;
     this.jsniMethods = Lists.create(jsniMethods.toArray(new JsniMethod[jsniMethods.size()]));
+    this.methodArgs = methodArgs;
     this.problems = problems;
     for (CompiledClass cc : compiledClasses) {
       cc.initUnit(this);
     }
   }
 
+  @Override
   public List<JsniMethod> getJsniMethods() {
     return jsniMethods;
   }
 
+  @Override
+  public MethodArgNamesLookup getMethodArgs() {
+    return methodArgs;
+  }
+
   /**
    * Returns all contained classes.
    */
+  @Override
   Collection<CompiledClass> getCompiledClasses() {
     return exposedCompiledClasses;
   }
 
+  @Override
   Set<ContentId> getDependencies() {
     return dependencies;
   }
 
+  @Override
   CategorizedProblem[] getProblems() {
     return problems;
   }
diff --git a/dev/core/src/com/google/gwt/dev/javac/ErrorCompilationUnit.java b/dev/core/src/com/google/gwt/dev/javac/ErrorCompilationUnit.java
index 9564cdd..0dbc168 100644
--- a/dev/core/src/com/google/gwt/dev/javac/ErrorCompilationUnit.java
+++ b/dev/core/src/com/google/gwt/dev/javac/ErrorCompilationUnit.java
@@ -32,38 +32,55 @@
     this.unit = unit;
   }
 
+  @Override
   public String getDisplayLocation() {
     return unit.getDisplayLocation();
   }
 
+  @Override
   public List<JsniMethod> getJsniMethods() {
     return unit.getJsniMethods();
   }
 
+  @Override
   public long getLastModified() {
     return unit.getLastModified();
   }
 
+  @Override
+  public MethodArgNamesLookup getMethodArgs() {
+    return unit.getMethodArgs();
+  }
+
+  @Override
+  @Deprecated
   public String getSource() {
     return unit.getSource();
   }
 
+  @Override
   public String getTypeName() {
     return unit.getTypeName();
   }
 
+  @Override
   public boolean isCompiled() {
     return false;
   }
 
+  @Override
   public boolean isError() {
     return true;
   }
 
+  @Override
+  @Deprecated
   public boolean isGenerated() {
     return unit.isGenerated();
   }
 
+  @Override
+  @Deprecated
   public boolean isSuperSource() {
     return unit.isSuperSource();
   }
diff --git a/dev/core/src/com/google/gwt/dev/javac/InvalidCompilationUnit.java b/dev/core/src/com/google/gwt/dev/javac/InvalidCompilationUnit.java
index a87be20..c0fbeef 100644
--- a/dev/core/src/com/google/gwt/dev/javac/InvalidCompilationUnit.java
+++ b/dev/core/src/com/google/gwt/dev/javac/InvalidCompilationUnit.java
@@ -32,38 +32,55 @@
     this.unit = unit;
   }
 
+  @Override
   public String getDisplayLocation() {
     return unit.getDisplayLocation();
   }
 
+  @Override
   public List<JsniMethod> getJsniMethods() {
     return unit.getJsniMethods();
   }
 
+  @Override
   public long getLastModified() {
     return unit.getLastModified();
   }
 
+  @Override
+  public MethodArgNamesLookup getMethodArgs() {
+    return unit.getMethodArgs();
+  }
+
+  @Override
+  @Deprecated
   public String getSource() {
     return unit.getSource();
   }
 
+  @Override
   public String getTypeName() {
     return unit.getTypeName();
   }
 
+  @Override
   public boolean isCompiled() {
     return false;
   }
 
+  @Override
   public boolean isError() {
     return false;
   }
 
+  @Override
+  @Deprecated
   public boolean isGenerated() {
     return unit.isGenerated();
   }
 
+  @Override
+  @Deprecated
   public boolean isSuperSource() {
     return unit.isSuperSource();
   }
diff --git a/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java b/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
index 99439d7..e685994 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JdtCompiler.java
@@ -85,6 +85,7 @@
       CompilationUnit unit = builder.build(compiledClasses,
           compiler.computeDependencies(cud),
           Collections.<JsniMethod> emptyList(),
+          new MethodArgNamesLookup(),
           cud.compilationResult().getProblems());
       if (cud.compilationResult().hasErrors()) {
         unit = new ErrorCompilationUnit(unit);
diff --git a/dev/core/src/com/google/gwt/dev/javac/JsniCollector.java b/dev/core/src/com/google/gwt/dev/javac/JsniCollector.java
index d91dc84..922faf2 100644
--- a/dev/core/src/com/google/gwt/dev/javac/JsniCollector.java
+++ b/dev/core/src/com/google/gwt/dev/javac/JsniCollector.java
@@ -27,20 +27,15 @@
 import com.google.gwt.dev.js.ast.JsParameter;
 import com.google.gwt.dev.js.ast.JsProgram;
 import com.google.gwt.dev.js.ast.JsStatement;
-import com.google.gwt.dev.util.Name.InternalName;
 import com.google.gwt.dev.util.collect.IdentityHashMap;
 import com.google.gwt.dev.util.collect.IdentityMaps;
 
-import org.eclipse.jdt.internal.compiler.ASTVisitor;
 import org.eclipse.jdt.internal.compiler.CompilationResult;
 import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.Argument;
 import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
 import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
-import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
-import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
-import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
 import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
 import org.eclipse.jdt.internal.compiler.util.Util;
 
@@ -118,6 +113,38 @@
     }
   }
 
+  private static class Visitor extends MethodVisitor {
+
+    private final Map<AbstractMethodDeclaration, JsniMethod> jsniMethods;
+    private final JsProgram jsProgram;
+    private final String source;
+
+    public Visitor(String source, JsProgram program,
+        Map<AbstractMethodDeclaration, JsniMethod> jsniMethods) {
+      this.jsProgram = program;
+      this.jsniMethods = jsniMethods;
+      this.source = source;
+    }
+
+    @Override
+    protected boolean interestingMethod(AbstractMethodDeclaration method) {
+      return method.isNative();
+    }
+
+    @Override
+    protected void processMethod(TypeDeclaration typeDecl,
+        AbstractMethodDeclaration method, String enclosingType,
+        String loc) {
+      JsFunction jsFunction = parseJsniFunction(method, source, enclosingType,
+          loc, jsProgram);
+      if (jsFunction != null) {
+        String jsniSignature = getJsniSignature(enclosingType, method);
+        jsniMethods.put(method, new JsniMethodImpl(jsniSignature,
+            jsFunction));
+      }
+    }
+  }
+
   public static final String JSNI_BLOCK_END = "}-*/";
 
   public static final String JSNI_BLOCK_START = "/*-{";
@@ -125,23 +152,8 @@
   public static Map<AbstractMethodDeclaration, JsniMethod> collectJsniMethods(
       final CompilationUnitDeclaration cud, final String source,
       final JsProgram program) {
-    final Map<AbstractMethodDeclaration, JsniMethod> jsniMethods = new IdentityHashMap<AbstractMethodDeclaration, JsniMethod>();
-    cud.traverse(new ASTVisitor() {
-      @Override
-      public void endVisit(TypeDeclaration type, BlockScope scope) {
-        collectJsniMethods(cud, type, source, program, jsniMethods);
-      }
-
-      @Override
-      public void endVisit(TypeDeclaration type, ClassScope scope) {
-        collectJsniMethods(cud, type, source, program, jsniMethods);
-      }
-
-      @Override
-      public void endVisit(TypeDeclaration type, CompilationUnitScope scope) {
-        collectJsniMethods(cud, type, source, program, jsniMethods);
-      }
-    }, cud.scope);
+    Map<AbstractMethodDeclaration, JsniMethod> jsniMethods = new IdentityHashMap<AbstractMethodDeclaration, JsniMethod>();
+    new Visitor(source, program, jsniMethods).collect(cud);
     return IdentityMaps.normalizeUnmodifiable(jsniMethods);
   }
 
@@ -246,43 +258,6 @@
     reportJsniProblem(info, method, msg, ProblemSeverities.Warning);
   }
 
-  private static void collectJsniMethods(CompilationUnitDeclaration cud,
-      TypeDeclaration typeDecl, String source, JsProgram jsProgram,
-      Map<AbstractMethodDeclaration, JsniMethod> results) {
-    AbstractMethodDeclaration[] methods = typeDecl.methods;
-    if (methods == null) {
-      return;
-    }
-
-    // Lazy initialize these when a native method is actually hit.
-    String enclosingType = null;
-    String loc = null;
-    boolean lazyInitialized = false;
-
-    for (AbstractMethodDeclaration method : methods) {
-      if (!method.isNative()) {
-        continue;
-      }
-
-      if (!lazyInitialized) {
-        char[] constantPoolName = typeDecl.binding.constantPoolName();
-        if (constantPoolName == null) {
-          // Unreachable local type
-          return;
-        }
-        enclosingType = InternalName.toBinaryName(String.valueOf(constantPoolName));
-        loc = String.valueOf(cud.getFileName());
-        lazyInitialized = true;
-      }
-      JsFunction jsFunction = parseJsniFunction(method, source, enclosingType,
-          loc, jsProgram);
-      if (jsFunction != null) {
-        String jsniSignature = getJsniSignature(enclosingType, method);
-        results.put(method, new JsniMethodImpl(jsniSignature, jsFunction));
-      }
-    }
-  }
-
   /**
    * JS reports the error as a line number, to find the absolute position in the
    * real source stream, we have to walk from the absolute JS start position
@@ -336,25 +311,8 @@
    */
   private static String getJsniSignature(String enclosingType,
       AbstractMethodDeclaration method) {
-    return '@' + enclosingType + "::" + getMemberSignature(method);
-  }
-
-  /**
-   * Gets a unique name for this method and its signature (this is used to
-   * determine whether one method overrides another).
-   */
-  private static String getMemberSignature(AbstractMethodDeclaration method) {
-    String name = String.valueOf(method.selector);
-    StringBuilder sb = new StringBuilder();
-    sb.append(name);
-    sb.append("(");
-    if (method.arguments != null) {
-      for (Argument param : method.arguments) {
-        sb.append(param.binding.type.signature());
-      }
-    }
-    sb.append(")");
-    return sb.toString();
+    return '@' + enclosingType + "::"
+        + MethodVisitor.getMemberSignature(method);
   }
 
   private static void reportJsniProblem(SourceInfo info,
diff --git a/dev/core/src/com/google/gwt/dev/javac/MethodArgNamesLookup.java b/dev/core/src/com/google/gwt/dev/javac/MethodArgNamesLookup.java
new file mode 100644
index 0000000..0b9d667
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/MethodArgNamesLookup.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2010 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.javac;
+
+import com.google.gwt.core.ext.typeinfo.JAbstractMethod;
+import com.google.gwt.dev.javac.asm.CollectMethodData;
+import com.google.gwt.dev.util.collect.Maps;
+
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Keeps track of method argument names that cannot be read from just the
+ * bytecode.
+ */
+public class MethodArgNamesLookup {
+
+  private Map<String, String[]> methodArgs;
+
+  public MethodArgNamesLookup() {
+    this.methodArgs = new HashMap<String, String[]>();
+  }
+
+  /**
+   * Prevent further modification to this object.  Calls to
+   * {@link #store(String, AbstractMethodDeclaration)} or 
+   * {@link #mergeFrom(MethodArgNamesLookup)} on this object will fail after
+   * this method is called.
+   */
+  public void freeze() {
+    methodArgs = Maps.normalizeUnmodifiable(methodArgs);
+  }
+  
+  /**
+   * Lookup the argument names for a given method.
+   * 
+   * @param method TypeOracle method
+   * @param methodData method data collected from bytecode
+   * @return an array of the argument names, or null if unavailable
+   */
+  public String[] lookup(JAbstractMethod method, CollectMethodData methodData) {
+    StringBuilder buf = new StringBuilder();
+    buf.append(method.getEnclosingType().getQualifiedBinaryName());
+    buf.append('.').append(method.getName());
+    buf.append(methodData.getDesc());
+    String key = buf.toString();
+    return methodArgs.get(key);
+  }
+
+  /**
+   * Merge argument names from another lookup map into this one.
+   * 
+   * @param other
+   */
+  public void mergeFrom(MethodArgNamesLookup other) {
+    methodArgs.putAll(other.methodArgs);
+  }
+
+  /**
+   * Store the argument names for a method.
+   * <p>
+   * <b>Note: method must have non-zero arguments.<b>
+   * 
+   * @param enclosingType fully qualified binary name of the enclosing type
+   * @param method JDT method
+   */
+  public void store(String enclosingType,
+      AbstractMethodDeclaration method) {
+    int n = method.arguments.length;
+    String[] argNames = new String[n];
+    for (int i = 0; i < n; ++i) {
+      argNames[i] = String.valueOf(method.arguments[i].name);
+    }
+    StringBuilder buf = new StringBuilder();
+    buf.append(enclosingType).append('.').append(method.selector);
+    buf.append(method.binding.signature());
+    String key = buf.toString();
+    methodArgs.put(key, argNames);
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/javac/MethodParamCollector.java b/dev/core/src/com/google/gwt/dev/javac/MethodParamCollector.java
new file mode 100644
index 0000000..8b36de4
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/MethodParamCollector.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2010 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.javac;
+
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+
+/**
+ * Collects method parameter names.
+ */
+public class MethodParamCollector {
+
+  private static class Visitor extends MethodVisitor {
+
+    private final MethodArgNamesLookup methodArgs;
+
+    public Visitor(MethodArgNamesLookup methodArgs) {
+      this.methodArgs = methodArgs;
+    }
+
+    @Override
+    protected boolean interestingMethod(AbstractMethodDeclaration method) {
+      return method.arguments != null && method.arguments.length > 0
+          && method.isAbstract();
+    }
+
+    @Override
+    protected void processMethod(TypeDeclaration typeDecl,
+        AbstractMethodDeclaration method, String enclosingType, String loc) {
+      methodArgs.store(enclosingType, method);
+    }
+  }
+
+  /**
+   * Returns an unmodifiable MethodArgNamesLookup containing the method argument
+   * names for the supplied compilation unit.
+   * 
+   * @param cud
+   * @return MethodArgNamesLookup instance
+   */
+  public static MethodArgNamesLookup collect(CompilationUnitDeclaration cud) {
+    MethodArgNamesLookup methodArgs = new MethodArgNamesLookup();
+    new Visitor(methodArgs).collect(cud);
+    methodArgs.freeze();
+    return methodArgs;
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/javac/MethodVisitor.java b/dev/core/src/com/google/gwt/dev/javac/MethodVisitor.java
new file mode 100644
index 0000000..eab9613
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/javac/MethodVisitor.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2010 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.javac;
+
+import com.google.gwt.dev.util.Name.InternalName;
+
+import org.eclipse.jdt.internal.compiler.ASTVisitor;
+import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.Argument;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
+import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
+import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
+
+/**
+ * Base class of things that walk methods in a CUD and collect things
+ * about interesting methods.
+ */
+public abstract class MethodVisitor {
+
+  /**
+   * Gets a unique name for this method, including its signature.
+   */
+  protected static String getMemberSignature(AbstractMethodDeclaration method) {
+    String name = String.valueOf(method.selector);
+    StringBuilder sb = new StringBuilder();
+    sb.append(name);
+    sb.append("(");
+    if (method.arguments != null) {
+      for (Argument param : method.arguments) {
+        sb.append(param.binding.type.signature());
+      }
+    }
+    sb.append(")");
+    return sb.toString();
+  }
+
+  /**
+   * Collect data about interesting methods in one compilation unit.
+   * 
+   * @param cud
+   */
+  public final void collect(final CompilationUnitDeclaration cud) {
+    cud.traverse(new ASTVisitor() {
+      @Override
+      public void endVisit(TypeDeclaration type, BlockScope scope) {
+        collectMethods(cud, type);
+      }
+
+      @Override
+      public void endVisit(TypeDeclaration type, ClassScope scope) {
+        collectMethods(cud, type);
+      }
+
+      @Override
+      public void endVisit(TypeDeclaration type, CompilationUnitScope scope) {
+        collectMethods(cud, type);
+      }
+    }, cud.scope);
+  }
+
+  /**
+   * Provided by a subclass to return true if this method should be processed.
+   * This is separate since some extra work is performed in order to call
+   * {@link #processMethod}.
+   * 
+   * @param method
+   * @return true if processMethod should be called on this method
+   */
+  protected abstract boolean interestingMethod(
+      AbstractMethodDeclaration method);
+
+  /**
+   * Provided by a subclass to process a method definition.  Methods which have
+   * no name are not passed to this method, even if {@link #interestingMethod}
+   * returns true.
+   * 
+   * @param typeDecl
+   * @param method
+   * @param enclosingType
+   * @param loc
+   */
+  protected abstract void processMethod(TypeDeclaration typeDecl,
+      AbstractMethodDeclaration method, String enclosingType, String loc);
+
+  /**
+   * Collect data about interesting methods on a particular type in a
+   * compilation unit.
+   *  
+   * @param cud
+   * @param typeDecl
+   */
+  private void collectMethods(CompilationUnitDeclaration cud,
+      TypeDeclaration typeDecl) {
+    AbstractMethodDeclaration[] methods = typeDecl.methods;
+    if (methods == null) {
+      return;
+    }
+
+    // Lazy initialize these when an interesting method is actually hit.
+    String enclosingType = null;
+    String loc = null;
+    boolean lazyInitialized = false;
+
+    for (AbstractMethodDeclaration method : methods) {
+      if (!interestingMethod(method)) {
+        continue;
+      }
+
+      if (!lazyInitialized) {
+        char[] constantPoolName = typeDecl.binding.constantPoolName();
+        if (constantPoolName == null) {
+          // Unreachable local type
+          return;
+        }
+        enclosingType = InternalName.toBinaryName(String.valueOf(
+            constantPoolName));
+        loc = String.valueOf(cud.getFileName());
+        lazyInitialized = true;
+      }
+      processMethod(typeDecl, method, enclosingType, loc);
+    }
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java b/dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java
index 4e272ca..e77f614 100644
--- a/dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java
+++ b/dev/core/src/com/google/gwt/dev/javac/SourceFileCompilationUnit.java
@@ -42,8 +42,8 @@
   public SourceFileCompilationUnit(Resource sourceFile, ContentId contentId,
       List<CompiledClass> compiledClasses, Set<ContentId> dependencies,
       Collection<? extends JsniMethod> jsniMethods,
-      CategorizedProblem[] problems) {
-    super(compiledClasses, dependencies, jsniMethods, problems);
+      MethodArgNamesLookup methodArgs, CategorizedProblem[] problems) {
+    super(compiledClasses, dependencies, jsniMethods, methodArgs, problems);
     this.sourceFile = sourceFile;
     this.contentId = contentId;
   }
@@ -58,6 +58,7 @@
     return sourceFile.getLastModified();
   }
 
+  @Deprecated
   @Override
   public String getSource() {
     if (cacheToken < 0) {
@@ -78,11 +79,13 @@
     return Shared.getTypeName(sourceFile);
   }
 
+  @Deprecated
   @Override
   public boolean isGenerated() {
     return false;
   }
 
+  @Deprecated
   @Override
   public boolean isSuperSource() {
     return sourceFile.wasRerooted();
diff --git a/dev/core/src/com/google/gwt/dev/javac/TypeOracleMediator.java b/dev/core/src/com/google/gwt/dev/javac/TypeOracleMediator.java
index 3d828a5..ebd5e8d 100644
--- a/dev/core/src/com/google/gwt/dev/javac/TypeOracleMediator.java
+++ b/dev/core/src/com/google/gwt/dev/javac/TypeOracleMediator.java
@@ -53,7 +53,6 @@
 import com.google.gwt.dev.javac.asm.ResolveTypeSignature;
 import com.google.gwt.dev.javac.asm.CollectAnnotationData.AnnotationData;
 import com.google.gwt.dev.javac.asm.CollectClassData.AnnotationEnum;
-import com.google.gwt.dev.resource.Resource;
 import com.google.gwt.dev.util.Name;
 import com.google.gwt.dev.util.PerfLogger;
 import com.google.gwt.dev.util.Name.InternalName;
@@ -223,6 +222,10 @@
 
   final TypeOracle typeOracle;
 
+  // map of fully qualified method names to their argument names
+  // transient since it is not retained across calls to addNewUnits
+  private transient MethodArgNamesLookup allMethodArgs;
+
   // map of internal names to class visitors
   // transient since it is not retained across calls to addNewUnits
   private transient Map<String, CollectClassData> classMap;
@@ -309,6 +312,7 @@
 
     // Perform a shallow pass to establish identity for new and old types.
     classMapType = new HashMap<JRealClassType, CollectClassData>();
+    allMethodArgs = new MethodArgNamesLookup();
     Set<JRealClassType> unresolvedTypes = new HashSet<JRealClassType>();
     for (CompilationUnit unit : units) {
       if (!unit.isCompiled()) {
@@ -324,11 +328,7 @@
         }
         JRealClassType type = createType(compiledClass, unresolvedTypes);
         if (type != null) {
-          if (unit instanceof SourceFileCompilationUnit) {
-            SourceFileCompilationUnit sourceUnit = (SourceFileCompilationUnit) unit;
-            Resource sourceFile = sourceUnit.getSourceFile();
-            typeOracle.addSourceReference(type, sourceFile);
-          }
+          allMethodArgs.mergeFrom(unit.getMethodArgs());
           binaryMapper.put(internalName, type);
           classMapType.put(type, cv);
         }
@@ -359,6 +359,7 @@
     typeOracle.finish();
 
     // no longer needed
+    allMethodArgs = null;
     classMap = null;
     classMapType = null;
     PerfLogger.end();
@@ -538,7 +539,6 @@
     return output;
   }
 
-
   /**
    * Collects data about a class which only needs the bytecode and no TypeOracle
    * data structures. This is used to make the initial shallow identity pass for
@@ -968,7 +968,7 @@
       ResolveMethodSignature methodResolver = new ResolveMethodSignature(
           resolver, logger, method, typeParamLookup, hasReturnType, methodData,
           methodData.getArgTypes(), methodData.getArgNames(),
-          methodData.hasActualArgNames());
+          methodData.hasActualArgNames(), allMethodArgs);
       // TraceSignatureVisitor trace = new TraceSignatureVisitor(
       // methodData.getAccess());
       // reader.accept(trace);
@@ -1020,8 +1020,15 @@
   private boolean resolveParameters(TreeLogger logger, JAbstractMethod method,
       CollectMethodData methodData) {
     Type[] argTypes = methodData.getArgTypes();
-    String[] argNames = methodData.getArgNames();
     boolean argNamesAreReal = methodData.hasActualArgNames();
+    String[] argNames = methodData.getArgNames();
+    if (!argNamesAreReal) {
+      String[] lookupNames = allMethodArgs.lookup(method, methodData);
+      if (lookupNames != null) {
+        argNames = lookupNames;
+        argNamesAreReal = true;
+      }
+    }
     List<CollectAnnotationData>[] paramAnnot = methodData.getArgAnnotations();
     for (int i = 0; i < argTypes.length; ++i) {
       JType argType = resolveType(argTypes[i]);
diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/CollectMethodData.java b/dev/core/src/com/google/gwt/dev/javac/asm/CollectMethodData.java
index 639e867..82ab78d 100644
--- a/dev/core/src/com/google/gwt/dev/javac/asm/CollectMethodData.java
+++ b/dev/core/src/com/google/gwt/dev/javac/asm/CollectMethodData.java
@@ -87,6 +87,10 @@
       argNames[i] = "arg" + i;
       paramAnnots[i] = new ArrayList<CollectAnnotationData>();
     }
+    if (argNames.length == 0) {
+      // save some work later if there aren't any parameters
+      actualArgNames = true;
+    }
   }
 
   /**
diff --git a/dev/core/src/com/google/gwt/dev/javac/asm/ResolveMethodSignature.java b/dev/core/src/com/google/gwt/dev/javac/asm/ResolveMethodSignature.java
index 2a612e4..8939958 100644
--- a/dev/core/src/com/google/gwt/dev/javac/asm/ResolveMethodSignature.java
+++ b/dev/core/src/com/google/gwt/dev/javac/asm/ResolveMethodSignature.java
@@ -24,6 +24,7 @@
 import com.google.gwt.core.ext.typeinfo.JTypeParameter;
 import com.google.gwt.dev.asm.Type;
 import com.google.gwt.dev.asm.signature.SignatureVisitor;
+import com.google.gwt.dev.javac.MethodArgNamesLookup;
 import com.google.gwt.dev.javac.Resolver;
 import com.google.gwt.dev.javac.TypeParameterLookup;
 
@@ -48,6 +49,7 @@
   private final Type[] argTypes;
   private final String[] argNames;
   private final boolean argNamesAreReal;
+  private final MethodArgNamesLookup allMethodArgs;
 
   private JType[] returnType = new JType[1];
   private List<JType[]> params = new ArrayList<JType[]>();
@@ -67,11 +69,13 @@
    * @param argTypes
    * @param argNames
    * @param argNamesAreReal
+   * @param allMethodArgs 
    */
   public ResolveMethodSignature(Resolver resolver, TreeLogger logger,
       JAbstractMethod method, TypeParameterLookup typeParamLookup,
       boolean hasReturnType, CollectMethodData methodData, Type[] argTypes,
-      String[] argNames, boolean argNamesAreReal) {
+      String[] argNames, boolean argNamesAreReal,
+      MethodArgNamesLookup allMethodArgs) {
     this.resolver = resolver;
     this.logger = logger;
     this.method = method;
@@ -81,6 +85,7 @@
     this.argTypes = argTypes;
     this.argNames = argNames;
     this.argNamesAreReal = argNamesAreReal;
+    this.allMethodArgs = allMethodArgs;
   }
 
   /**
@@ -106,6 +111,16 @@
               + methodData.getDesc() + ") and signature ("
               + methodData.getSignature() + ")");
     }
+    String[] names = argNames;
+    boolean namesAreReal = argNamesAreReal;
+    if (!namesAreReal) {
+      // lookup argument names in allMethodArgs
+      String[] lookupArgNames = allMethodArgs.lookup(method, methodData);
+      if (lookupArgNames != null) {
+        names = lookupArgNames;
+        namesAreReal = true;
+      }
+    }
     for (int i = 0; i < argTypes.length; ++i) {
       JType argType = params.get(i)[0];
       if (argType == null) {
@@ -118,8 +133,8 @@
           declaredAnnotations);
 
       // JParameter adds itself to the method
-      new JParameter(method, argType, argNames[i], declaredAnnotations,
-          argNamesAreReal);
+      new JParameter(method, argType, names[i], declaredAnnotations,
+          namesAreReal);
     }
 
     // Handle thrown exceptions
diff --git a/dev/core/test/com/google/gwt/dev/javac/asm/ResolveGenericsTest.java b/dev/core/test/com/google/gwt/dev/javac/asm/ResolveGenericsTest.java
index eed044b..c47d02d 100644
--- a/dev/core/test/com/google/gwt/dev/javac/asm/ResolveGenericsTest.java
+++ b/dev/core/test/com/google/gwt/dev/javac/asm/ResolveGenericsTest.java
@@ -308,7 +308,7 @@
     }
     ResolveMethodSignature methodResolver = new ResolveMethodSignature(resolver,
         failTreeLogger, method, lookup, true, methodData, argTypes, argNames,
-        false);
+        false, Collections.<String, String[]>emptyMap());
     new SignatureReader(signature).accept(methodResolver);
     methodResolver.finish();
   }
diff --git a/user/src/com/google/gwt/i18n/rebind/LookupMethodCreator.java b/user/src/com/google/gwt/i18n/rebind/LookupMethodCreator.java
index a9a9dd7..c550087 100644
--- a/user/src/com/google/gwt/i18n/rebind/LookupMethodCreator.java
+++ b/user/src/com/google/gwt/i18n/rebind/LookupMethodCreator.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JParameter;
 import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
 import com.google.gwt.core.ext.typeinfo.JType;
 import com.google.gwt.i18n.rebind.AbstractResource.ResourceList;
@@ -66,7 +67,13 @@
   }
 
   void createMethodFor(JMethod targetMethod) {
-    String template = "{0} target = ({0}) cache.get(arg0);";
+    JParameter[] args = targetMethod.getParameters();
+    if (args.length != 1) {
+      throw new IllegalStateException(
+          "ConstantsWithLookup methods must have exactly one argument");
+    }
+    String arg0 = args[0].getName();
+    String template = "{0} target = ({0}) cache.get(" + arg0 + ");";
     String returnTypeName = getReturnTypeName();
     String lookup = MessageFormat.format(template, new Object[] {returnTypeName});
     println(lookup);
@@ -81,7 +88,7 @@
       if (methods[i].getReturnType().getErasedType().equals(erasedType)
           && methods[i] != targetMethod) {
         String methodName = methods[i].getName();
-        String body = "if(arg0.equals(" + wrap(methodName) + ")) {";
+        String body = "if(" + arg0 + ".equals(" + wrap(methodName) + ")) {";
         println(body);
         indent();
         printFound(methodName);
@@ -91,7 +98,7 @@
     }
     String format = "throw new java.util.MissingResourceException(\"Cannot find constant ''\" +"
         + "{0} + \"''; expecting a method name\", \"{1}\", {0});";
-    String result = MessageFormat.format(format, "arg0",
+    String result = MessageFormat.format(format, arg0,
         this.currentCreator.getTarget().getQualifiedSourceName());
     println(result);
   }
diff --git a/user/src/com/google/gwt/i18n/rebind/MessagesMethodCreator.java b/user/src/com/google/gwt/i18n/rebind/MessagesMethodCreator.java
index a360356..a6725e2 100644
--- a/user/src/com/google/gwt/i18n/rebind/MessagesMethodCreator.java
+++ b/user/src/com/google/gwt/i18n/rebind/MessagesMethodCreator.java
@@ -338,7 +338,8 @@
       generated.append(PluralRule.class.getCanonicalName());
       generated.append(" rule = new " + rule.getClass().getCanonicalName()
           + "();\n");
-      generated.append("switch (rule.select(arg" + pluralParamIndex + ")) {\n");
+      generated.append("switch (rule.select("
+          + params[pluralParamIndex].getName() + ")) {\n");
       PluralForm[] pluralForms = rule.pluralForms();
       resourceList.setPluralForms(key, pluralForms);
       // Skip default plural form (index 0); the fall-through case will handle
@@ -370,8 +371,8 @@
       if (!seenFlags[i]) {
         Optional optional = params[i].getAnnotation(Optional.class);
         if (optional == null) {
-          throw error(logger, "Required argument " + i + " not present: "
-              + template);
+          throw error(logger, "Required argument " + params[i].getName()
+              + " not present: " + template);
         }
       }
     }
@@ -459,7 +460,7 @@
                 + " beyond range of arguments: " + template);
           }
           seenFlag[argNumber] = true;
-          String arg = "arg" + argNumber;
+          String arg = params[argNumber].getName();
           String format = argChunk.getFormat();
           if (format != null) {
             String subformat = argChunk.getSubFormat();
diff --git a/user/src/com/google/gwt/user/rebind/AbstractGeneratorClassCreator.java b/user/src/com/google/gwt/user/rebind/AbstractGeneratorClassCreator.java
index 45e23c4..0790903 100644
--- a/user/src/com/google/gwt/user/rebind/AbstractGeneratorClassCreator.java
+++ b/user/src/com/google/gwt/user/rebind/AbstractGeneratorClassCreator.java
@@ -253,8 +253,8 @@
         getWriter().print(",");
       }
       getWriter().print(
-          params[i].getType().getParameterizedQualifiedSourceName() + " arg"
-              + (i));
+          params[i].getType().getParameterizedQualifiedSourceName() + " "
+              + params[i].getName());
     }
     getWriter().println(") {");
     getWriter().indent();