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",