--- old/src/hotspot/share/prims/jvmtiEnv.cpp 2019-06-13 09:48:28.753206400 -0700 +++ new/src/hotspot/share/prims/jvmtiEnv.cpp 2019-06-13 09:48:26.333314700 -0700 @@ -2487,54 +2487,55 @@ jmethodID* result_list = (jmethodID*)jvmtiMalloc(result_length * sizeof(jmethodID)); int index; bool jmethodids_found = true; + int skipped = 0; // skip default methods from superinterface (see JDK-8216324) - if (JvmtiExport::can_maintain_original_method_order()) { - // Use the original method ordering indices stored in the class, so we can emit + for (index = 0; index < result_length; index++) { + Method* m = ik->methods()->at(index); + // Depending on can_maintain_original_method_order capability + // use the original method ordering indices stored in the class, so we can emit // jmethodIDs in the order they appeared in the class file - for (index = 0; index < result_length; index++) { - Method* m = ik->methods()->at(index); - int original_index = ik->method_ordering()->at(index); - assert(original_index >= 0 && original_index < result_length, "invalid original method index"); - jmethodID id; - if (jmethodids_found) { - id = m->find_jmethod_id_or_null(); - if (id == NULL) { - // If we find an uninitialized value, make sure there is - // enough space for all the uninitialized values we might - // find. - ik->ensure_space_for_methodids(index); - jmethodids_found = false; - id = m->jmethod_id(); - } - } else { + // or just copy in any order + int result_index = JvmtiExport::can_maintain_original_method_order() ? ik->method_ordering()->at(index) : index; + assert(result_index >= 0 && result_index < result_length, "invalid original method index"); + if (m->is_overpass()) { + result_list[result_index] = NULL; + skipped++; + continue; + } + jmethodID id; + if (jmethodids_found) { + id = m->find_jmethod_id_or_null(); + if (id == NULL) { + // If we find an uninitialized value, make sure there is + // enough space for all the uninitialized values we might + // find. + ik->ensure_space_for_methodids(index); + jmethodids_found = false; id = m->jmethod_id(); } - result_list[original_index] = id; + } else { + id = m->jmethod_id(); } - } else { - // otherwise just copy in any order - for (index = 0; index < result_length; index++) { - Method* m = ik->methods()->at(index); - jmethodID id; - if (jmethodids_found) { - id = m->find_jmethod_id_or_null(); - if (id == NULL) { - // If we find an uninitialized value, make sure there is - // enough space for all the uninitialized values we might - // find. - ik->ensure_space_for_methodids(index); - jmethodids_found = false; - id = m->jmethod_id(); - } + result_list[result_index] = id; + } + + // Fill in return value. + if (skipped > 0) { + // copy results skipping NULL methodIDs + *methods_ptr = (jmethodID*)jvmtiMalloc((result_length - skipped) * sizeof(jmethodID)); + *method_count_ptr = result_length - skipped; + for (index = 0, skipped = 0; index < result_length; index++) { + if (result_list[index] == NULL) { + skipped++; } else { - id = m->jmethod_id(); + (*methods_ptr)[index - skipped] = result_list[index]; } - result_list[index] = id; } + deallocate((unsigned char *)result_list); + } else { + *method_count_ptr = result_length; + *methods_ptr = result_list; } - // Fill in return value. - *method_count_ptr = result_length; - *methods_ptr = result_list; return JVMTI_ERROR_NONE; } /* end GetClassMethods */ --- /dev/null 2019-06-13 09:48:43.000000000 -0700 +++ new/test/hotspot/jtreg/serviceability/jvmti/GetClassMethods/InterfaceDefMethods.java 2019-06-13 09:48:40.182893000 -0700 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8216324 + * @summary GetClassMethods is confused by the presence of default methods in super interfaces + * @library /test/lib + * @compile InterfaceDefMethods.java + * @run main/othervm/native -agentlib:InterfaceDefMethods InterfaceDefMethods + * @run main/othervm/native -agentlib:InterfaceDefMethods=maintain_original_method_order InterfaceDefMethods + */ + +import java.lang.reflect.Method; +import java.util.Arrays; + +public class InterfaceDefMethods { + + static { + try { + System.loadLibrary("InterfaceDefMethods"); + } catch (UnsatisfiedLinkError ex) { + System.err.println("Could not load InterfaceDefMethods library"); + System.err.println("java.library.path:" + System.getProperty("java.library.path")); + throw ex; + } + } + + static private void log(Object msg) { + System.out.println(String.valueOf(msg)); + } + + static private native Method[] getJVMTIDeclaredMethods(Class klass); + + public interface Parent { + default String def() { return "Parent.def"; } + String method0(); + String method1(); + } + + public interface Child extends Parent { + String method2(); + } + + public static class Impl implements Child { + public String method0() { return "Impl.method0"; } + public String method1() { return "Impl.method1"; } + public String method2() { return "Impl.method2"; } + } + + public static void main(String[] args) { + new Impl(); // To get classes initialized + + Method[] reflectMethods = Child.class.getDeclaredMethods(); + Method[] jvmtiMethods = getJVMTIDeclaredMethods(Child.class); + + if (jvmtiMethods == null) { + throw new RuntimeException("getJVMTIDeclaredMethods failed"); + } + + log("Reflection getDeclaredMethods returned: " + Arrays.toString(reflectMethods)); + log("JVMTI GetClassMethods returned: " + Arrays.toString(jvmtiMethods)); + + if (reflectMethods.length != jvmtiMethods.length) { + throw new RuntimeException("InterfaceDefMethods failed: Unexpected method count from JVMTI GetClassMethods!"); + } + if (!reflectMethods[0].equals(jvmtiMethods[0])) { + throw new RuntimeException("InterfaceDefMethods failed: Unexpected method from JVMTI GetClassMethods!"); + } + log("Test passed: Got expected output from JVMTI GetClassMethods!"); + } +} --- /dev/null 2019-06-13 09:48:54.000000000 -0700 +++ new/test/hotspot/jtreg/serviceability/jvmti/GetClassMethods/libInterfaceDefMethods.cpp 2019-06-13 09:48:50.683115100 -0700 @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include "jvmti.h" + +#ifdef __cplusplus +extern "C" { +#endif + +static jvmtiEnv *jvmti = NULL; + +JNIEXPORT +jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { + return JNI_VERSION_9; +} + +JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) { + vm->GetEnv((void **)&jvmti, JVMTI_VERSION_11); + + if (options != NULL && strcmp(options, "maintain_original_method_order") == 0) { + printf("maintain_original_method_order: true\n"); + jvmtiCapabilities caps = {}; + caps.can_maintain_original_method_order = 1; + + jvmtiError err = jvmti->AddCapabilities(&caps); + if (err != JVMTI_ERROR_NONE) { + printf("Agent_OnLoad: AddCapabilities failed with error: %d\n", err); + return JNI_ERR; + } + } else { + printf("maintain_original_method_order: false\n"); + } + return JNI_OK; +} + +JNIEXPORT jobjectArray JNICALL Java_InterfaceDefMethods_getJVMTIDeclaredMethods(JNIEnv *env, jclass static_klass, jclass klass) { + jint method_count = 0; + jmethodID* methods = NULL; + jvmtiError err = jvmti->GetClassMethods(klass, &method_count, &methods); + if (err != JVMTI_ERROR_NONE) { + printf("GetClassMethods failed with error: %d\n", err); + return NULL; + } + + jclass method_cls = env->FindClass("java/lang/reflect/Method"); + if (method_cls == NULL) { + printf("FindClass (Method) failed\n"); + return NULL; + } + jobjectArray array = env->NewObjectArray(method_count, method_cls, NULL); + if (array == NULL) { + printf("NewObjectArray failed\n"); + return NULL; + } + + for (int i = 0; i < method_count; i++) { + jint modifiers = 0; + err = jvmti->GetMethodModifiers(methods[i], &modifiers); + if (err != JVMTI_ERROR_NONE) { + printf("GetMethodModifiers failed with error: %d\n", err); + return NULL; + } + + jobject m = env->ToReflectedMethod(klass, methods[i], (modifiers & 8) == 8); + if (array == NULL) { + printf("ToReflectedMethod failed\n"); + return NULL; + } + env->SetObjectArrayElement(array, i, m); + + env->DeleteLocalRef(m); + } + jvmti->Deallocate((unsigned char *)methods); + + return array; +} +#ifdef __cplusplus +} +#endif