Updates the reflective visitor pattern uses in GenerateJavaAST
to cache Method instance lookups.  This cuts the runtime
of GenerateJavaAST.exec() in half.

Review at http://gwt-code-reviews.appspot.com/817802


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@8683 8db76d5a-ed1c-0410-87a9-c151d255dfc7
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 7849d45..e324a30 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
@@ -237,6 +237,56 @@
   private static class JavaASTGenerationVisitor {
 
     /**
+     * Used to cache {@link Method} lookups.
+     */
+    private static class MethodKey {
+      private Class<? extends Object> childClass;
+      private String name;
+
+      public MethodKey(String name, Class<? extends Object>childClass) {
+        this.name = name;
+        this.childClass = childClass;
+      }
+
+      public boolean equals(Object obj) {
+        if (obj instanceof MethodKey) {
+          MethodKey otherKey = (MethodKey) obj;
+          return name.equals(otherKey.name) && childClass.equals(otherKey.childClass);
+        }
+       return super.equals(obj);
+      }
+
+      @Override
+      public int hashCode() {
+        return name.hashCode() + (101 * childClass.hashCode());
+      }
+    }
+
+    /**
+     * Used to cache {@link Method} lookups.
+     */
+    private static class MethodValue {
+      private final NoSuchMethodException ex;
+      private final Method method;
+      public MethodValue(Method method) {
+        this.method = method;
+        this.ex = null;
+      }
+
+      public MethodValue(NoSuchMethodException ex) {
+        this.ex = ex;
+        this.method = null;
+      }
+
+      public Method getMethod() throws NoSuchMethodException {
+        if (this.ex != null) {
+          throw (ex);
+        }
+        return method;
+      }
+    }
+
+    /**
      * The literal for the JLS identifier that represents the length
      * field on an array.
      */
@@ -285,6 +335,8 @@
 
     private final Map<JMethod, Map<String, JLabel>> labelMap = new IdentityHashMap<JMethod, Map<String, JLabel>>();
 
+    private final Map<MethodKey, MethodValue> methodCache = new HashMap<MethodKey, MethodValue>();
+
     private final JProgram program;
 
     private final TypeMap typeMap;
@@ -536,8 +588,7 @@
       }
 
       try {
-        // TODO: This is really slow! Cache or otherwise fix.
-        Method method = getClass().getDeclaredMethod(name, child.getClass());
+        Method method = getCachedMethod(name, child.getClass());
         return (JNode) method.invoke(this, child);
       } catch (Throwable e) {
         if (e instanceof InvocationTargetException) {
@@ -2275,6 +2326,22 @@
       return typeBinding;
     }
 
+    private Method getCachedMethod(String name, Class<? extends Object> childClass) throws NoSuchMethodException {
+      MethodKey key = new MethodKey(name, childClass);
+      MethodValue value = methodCache.get(key);
+      if (value == null) {
+        try {
+          Method method = getClass().getDeclaredMethod(name, childClass);
+          value = new MethodValue(method);
+        } catch (NoSuchMethodException ex) {
+          value = new MethodValue(ex);
+        }
+        methodCache.put(key, value);
+      }
+      // Might throw an exception here.
+      return value.getMethod();
+    }
+
     private JInterfaceType getOrCreateExternalType(SourceInfo info,
         char[][] compoundName) {
       String name = BuildTypeMap.dotify(compoundName);