Fix JNI GetMethodID on interfaces

The GetMethodID call was only searching through methods declared by
classes and superclasses.  If you passed it an interface class and
asked for a method declared in a superinterface, the call would fail.
We now have separate code for handling lookups on interfaces.

This also refactors some similar code in the interface method resolver.

Bug 3329492

(Cherry-pick from dalvik-dev)

Change-Id: Icaf744b9e75a1fd6d99f47281002cc6b3c36e368
diff --git a/vm/Jni.c b/vm/Jni.c
index 0d525a0..a416b13 100644
--- a/vm/Jni.c
+++ b/vm/Jni.c
@@ -2331,6 +2331,11 @@
 /*
  * Get a method ID for an instance method.
  *
+ * While Dalvik bytecode has distinct instructions for virtual, super,
+ * static, direct, and interface method invocation, JNI only provides
+ * two functions for acquiring a method ID.  This call handles everything
+ * but static methods.
+ *
  * JNI defines <init> as an instance method, but Dalvik considers it a
  * "direct" method, so we have to special-case it here.
  *
@@ -2347,10 +2352,16 @@
 
     if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
         assert(dvmCheckException(_self));
+    } else if (dvmIsInterfaceClass(clazz)) {
+        Method* meth = dvmFindInterfaceMethodHierByDescriptor(clazz, name, sig);
+        if (meth == NULL) {
+            dvmThrowExceptionFmt("Ljava/lang/NoSuchMethodError;",
+                "no method with name='%s' signature='%s' in interface %s",
+                name, sig, clazz->descriptor);
+        }
+        id = (jmethodID) meth;
     } else {
-        Method* meth;
-
-        meth = dvmFindVirtualMethodHierByDescriptor(clazz, name, sig);
+        Method* meth = dvmFindVirtualMethodHierByDescriptor(clazz, name, sig);
         if (meth == NULL) {
             /* search private methods and constructors; non-hierarchical */
             meth = dvmFindDirectMethodByDescriptor(clazz, name, sig);
diff --git a/vm/analysis/Optimize.c b/vm/analysis/Optimize.c
index 1ed47af..810fd6e 100644
--- a/vm/analysis/Optimize.c
+++ b/vm/analysis/Optimize.c
@@ -922,7 +922,6 @@
 {
     DvmDex* pDvmDex = referrer->pDvmDex;
     Method* resMethod;
-    int i;
 
     LOGVV("--- resolving interface method %d (referrer=%s)\n",
         methodIdx, referrer->descriptor);
@@ -953,24 +952,10 @@
 
         LOGVV("+++ looking for '%s' '%s' in resClass='%s'\n",
             methodName, methodSig, resClass->descriptor);
-        resMethod = dvmFindVirtualMethod(resClass, methodName, &proto);
+        resMethod = dvmFindInterfaceMethodHier(resClass, methodName, &proto);
         if (resMethod == NULL) {
-            /* scan superinterfaces and superclass interfaces */
-            LOGVV("+++ did not resolve immediately\n");
-            for (i = 0; i < resClass->iftableCount; i++) {
-                resMethod = dvmFindVirtualMethod(resClass->iftable[i].clazz,
-                                methodName, &proto);
-                if (resMethod != NULL)
-                    break;
-            }
-
-            if (resMethod == NULL) {
-                LOGVV("+++ unable to resolve method %s\n", methodName);
-                return NULL;
-            }
-        } else {
-            LOGVV("+++ resolved immediately: %s (%s %d)\n", resMethod->name,
-                resMethod->clazz->descriptor, (u4) resMethod->methodIndex);
+            dvmThrowException("Ljava/lang/NoSuchMethodError;", methodName);
+            return NULL;
         }
 
         /* we're expecting this to be abstract */
diff --git a/vm/oo/Object.c b/vm/oo/Object.c
index 32ed679..e99f690 100644
--- a/vm/oo/Object.c
+++ b/vm/oo/Object.c
@@ -517,7 +517,7 @@
 
 /*
  * Find a "virtual" method in a class.  If we don't find it, try the
- * superclass.
+ * superclass.  Does not examine interfaces.
  *
  * Returns NULL if the method can't be found.  (Does not throw an exception.)
  */
@@ -530,7 +530,7 @@
 
 /*
  * Find a "virtual" method in a class.  If we don't find it, try the
- * superclass.
+ * superclass.  Does not examine interfaces.
  *
  * Returns NULL if the method can't be found.  (Does not throw an exception.)
  */
@@ -542,6 +542,51 @@
 }
 
 /*
+ * Find a method in an interface.  Searches superinterfaces.
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindInterfaceMethodHierByDescriptor(const ClassObject* iface,
+    const char* methodName, const char* descriptor)
+{
+    Method* resMethod = dvmFindVirtualMethodByDescriptor(iface,
+        methodName, descriptor);
+    if (resMethod == NULL) {
+        /* scan superinterfaces and superclass interfaces */
+        int i;
+        for (i = 0; i < iface->iftableCount; i++) {
+            resMethod = dvmFindVirtualMethodByDescriptor(iface->iftable[i].clazz,
+                methodName, descriptor);
+            if (resMethod != NULL)
+                break;
+        }
+    }
+    return resMethod;
+}
+
+/*
+ * Find a method in an interface.  Searches superinterfaces.
+ *
+ * Returns NULL if the method can't be found.  (Does not throw an exception.)
+ */
+Method* dvmFindInterfaceMethodHier(const ClassObject* iface,
+    const char* methodName, const DexProto* proto)
+{
+    Method* resMethod = dvmFindVirtualMethod(iface, methodName, proto);
+    if (resMethod == NULL) {
+        /* scan superinterfaces and superclass interfaces */
+        int i;
+        for (i = 0; i < iface->iftableCount; i++) {
+            resMethod = dvmFindVirtualMethod(iface->iftable[i].clazz,
+                methodName, proto);
+            if (resMethod != NULL)
+                break;
+        }
+    }
+    return resMethod;
+}
+
+/*
  * Find a "direct" method (static, private, or "<*init>").
  *
  * Returns NULL if the method can't be found.  (Does not throw an exception.)
diff --git a/vm/oo/Object.h b/vm/oo/Object.h
index 903450f..4fc0e5c 100644
--- a/vm/oo/Object.h
+++ b/vm/oo/Object.h
@@ -629,6 +629,14 @@
     const DexProto* proto);
 
 /*
+ * Find a method in an interface hierarchy.
+ */
+Method* dvmFindInterfaceMethodHierByDescriptor(const ClassObject* iface,
+    const char* methodName, const char* descriptor);
+Method* dvmFindInterfaceMethodHier(const ClassObject* iface,
+    const char* methodName, const DexProto* proto);
+
+/*
  * Find the implementation of "meth" in "clazz".
  *
  * Returns NULL and throws an exception if not found.
diff --git a/vm/oo/Resolve.c b/vm/oo/Resolve.c
index c4bda8b..b277576 100644
--- a/vm/oo/Resolve.c
+++ b/vm/oo/Resolve.c
@@ -287,7 +287,6 @@
     ClassObject* resClass;
     const DexMethodId* pMethodId;
     Method* resMethod;
-    int i;
 
     LOGVV("--- resolving interface method %d (referrer=%s)\n",
         methodIdx, referrer->descriptor);
@@ -338,23 +337,10 @@
 
     LOGVV("+++ looking for '%s' '%s' in resClass='%s'\n",
         methodName, methodSig, resClass->descriptor);
-    resMethod = dvmFindVirtualMethod(resClass, methodName, &proto);
+    resMethod = dvmFindInterfaceMethodHier(resClass, methodName, &proto);
     if (resMethod == NULL) {
-        LOGVV("+++ did not resolve immediately\n");
-        for (i = 0; i < resClass->iftableCount; i++) {
-            resMethod = dvmFindVirtualMethod(resClass->iftable[i].clazz,
-                            methodName, &proto);
-            if (resMethod != NULL)
-                break;
-        }
-
-        if (resMethod == NULL) {
-            dvmThrowException("Ljava/lang/NoSuchMethodError;", methodName);
-            return NULL;
-        }
-    } else {
-        LOGVV("+++ resolved immediately: %s (%s %d)\n", resMethod->name,
-            resMethod->clazz->descriptor, (u4) resMethod->methodIndex);
+        dvmThrowException("Ljava/lang/NoSuchMethodError;", methodName);
+        return NULL;
     }
 
     LOGVV("--- found interface method %d (%s.%s)\n",