Class.getDeclaredMethods() is returning inherited methods

Michael Rasmussen Michael.Rasmussen at roguewave.com
Mon Jan 7 10:46:16 UTC 2019


Hi,

We recently discovered something similar, although with the native counterparts of getDeclaredMethods: the JVM-TI function GetClassMethods and the JDI method ReferenceType.methods().
The documentation for which states "Only directly declared methods are returned (not inherited methods)" or "(...) declared directly in this type. Inherited methods are not included."

When running the code pasted below with HotSpot (tried OpenJDK8 and 11), I get the following output:
[public abstract java.lang.String app1.Test$Child.method2()]
[public default java.lang.String app1.Test$Child.method(), public abstract java.lang.String app1.Test$Child.method2()]

As visible, the native call also includes the inherited method() from Parent in the list, which is not present in Child's classfile. Verified with javap -v, an no traces of "method" were found anywhere. For abbreviation, here is grepped output:
javap -v app1.Test$Child | grep method
   #4 = Utf8               method2
  public abstract java.lang.String method2();

For comparison, tried running on IBM SDK 8, and OpenJDK 11 with OpenJ9. Neither of those listed method() from the native methods.

Kind regards
Michael Rasmussen

Code snippets:
// --- snip ---
package app1;

import java.lang.reflect.Method;
import java.util.Arrays;

class Test {
  public static native Method[] getJVMTIDeclaredMethods(Class<?> klass);

  public interface Parent {
    default String def() { return "Parent"; }
    String method();
  }

  public interface Child extends Parent {
    String method2();
  }

  public static class Impl implements Child {
    public String method() { return ""; }
    public String method2() { return ""; }
  }

  public static void main(String[] args) {
    new Impl();
    System.out.println(Arrays.toString(Child.class.getDeclaredMethods()));
    System.out.println(Arrays.toString(getJVMTIDeclaredMethods(Child.class)));
  }
}
// --- snap ---

and the corresponding native component:
// -- snip ---
#include <stdio.h>

#include "jni.h"
#include "jvmti.h"

static jvmtiEnv *jvmti;

JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {
  (*vm)->GetEnv(vm, (void **)&jvmti, JVMTI_VERSION_1_2);

  return 0;
}

JNIEXPORT jobjectArray JNICALL Java_app1_Test_getJVMTIDeclaredMethods(JNIEnv *env, jclass static_klass, jclass klass) {
  jint method_count;
  jmethodID* methods;
  (*jvmti)->GetClassMethods(jvmti, klass, &method_count, &methods);

  jclass method_cls = (*env)->FindClass(env, "java/lang/reflect/Method");
  jobjectArray array = (*env)->NewObjectArray(env, method_count, method_cls, NULL);

  for (int i = 0; i < method_count; i++) {
    jint modifiers = (*jvmti)->GetMethodModifiers(jvmti, methods[i], &modifiers);

    jobject m = (*env)->ToReflectedMethod(env, klass, methods[i], modifiers & 8 == 8);
    (*env)->SetObjectArrayElement(env, array, i, m);

    (*env)->DeleteLocalRef(env, m);
  }

  (*jvmti)->Deallocate(jvmti, (unsigned char *)methods);

  return array;
}
// --- snap ---

Example run on Windows with OpenJDK 8 (HotSpot):
C:\XWork\native-test>\Work\jvm\jdk8\bin\java -agentpath:build\libs\hello\shared\hello.dll -cp build\classes\java\main app1.Test
[public abstract java.lang.String app1.Test$Child.method2()]
[public default java.lang.String app1.Test$Child.method(), public abstract java.lang.String app1.Test$Child.method2()]


More information about the core-libs-dev mailing list