diff options
| -rw-r--r-- | runtime/openjdkjvmti/OpenjdkJvmTi.cc | 30 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/ti_class.cc | 148 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/ti_class.h | 14 | ||||
| -rw-r--r-- | test/903-hello-tagging/tagging.cc | 3 | ||||
| -rw-r--r-- | test/904-object-allocation/tracking.cc | 2 | ||||
| -rw-r--r-- | test/905-object-free/tracking_free.cc | 2 | ||||
| -rw-r--r-- | test/906-iterate-heap/iterate_heap.cc | 1 | ||||
| -rw-r--r-- | test/907-get-loaded-classes/get_loaded_classes.cc | 1 | ||||
| -rw-r--r-- | test/908-gc-start-finish/gc_callbacks.cc | 3 | ||||
| -rw-r--r-- | test/910-methods/methods.cc | 4 | ||||
| -rw-r--r-- | test/911-get-stack-trace/stack_trace.cc | 3 | ||||
| -rw-r--r-- | test/912-classes/classes.cc | 88 | ||||
| -rw-r--r-- | test/912-classes/expected.txt | 23 | ||||
| -rw-r--r-- | test/912-classes/src/Main.java | 75 | ||||
| -rw-r--r-- | test/913-heaps/heaps.cc | 2 | ||||
| -rw-r--r-- | test/918-fields/fields.cc | 5 |
16 files changed, 394 insertions, 10 deletions
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index 98c4c3a511..faaeff361d 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -546,14 +546,14 @@ class JvmtiFunctions { } static jvmtiError GetClassModifiers(jvmtiEnv* env, jclass klass, jint* modifiers_ptr) { - return ERR(NOT_IMPLEMENTED); + return ClassUtil::GetClassModifiers(env, klass, modifiers_ptr); } static jvmtiError GetClassMethods(jvmtiEnv* env, jclass klass, jint* method_count_ptr, jmethodID** methods_ptr) { - return ERR(NOT_IMPLEMENTED); + return ClassUtil::GetClassMethods(env, klass, method_count_ptr, methods_ptr); } static jvmtiError GetClassFields(jvmtiEnv* env, @@ -567,7 +567,7 @@ class JvmtiFunctions { jclass klass, jint* interface_count_ptr, jclass** interfaces_ptr) { - return ERR(NOT_IMPLEMENTED); + return ClassUtil::GetImplementedInterfaces(env, klass, interface_count_ptr, interfaces_ptr); } static jvmtiError GetClassVersionNumbers(jvmtiEnv* env, @@ -602,7 +602,7 @@ class JvmtiFunctions { } static jvmtiError GetClassLoader(jvmtiEnv* env, jclass klass, jobject* classloader_ptr) { - return ERR(NOT_IMPLEMENTED); + return ClassUtil::GetClassLoader(env, klass, classloader_ptr); } static jvmtiError GetSourceDebugExtension(jvmtiEnv* env, @@ -1067,8 +1067,15 @@ class JvmtiFunctions { ENSURE_NON_NULL(name_ptr); switch (error) { #define ERROR_CASE(e) case (JVMTI_ERROR_ ## e) : do { \ - *name_ptr = const_cast<char*>("JVMTI_ERROR_"#e); \ - return OK; \ + jvmtiError res = CopyString(env, \ + "JVMTI_ERROR_"#e, \ + reinterpret_cast<unsigned char**>(name_ptr)); \ + if (res != OK) { \ + *name_ptr = nullptr; \ + return res; \ + } else { \ + return OK; \ + } \ } while (false) ERROR_CASE(NONE); ERROR_CASE(INVALID_THREAD); @@ -1120,8 +1127,15 @@ class JvmtiFunctions { ERROR_CASE(INVALID_ENVIRONMENT); #undef ERROR_CASE default: { - *name_ptr = const_cast<char*>("JVMTI_ERROR_UNKNOWN"); - return ERR(ILLEGAL_ARGUMENT); + jvmtiError res = CopyString(env, + "JVMTI_ERROR_UNKNOWN", + reinterpret_cast<unsigned char**>(name_ptr)); + if (res != OK) { + *name_ptr = nullptr; + return res; + } else { + return ERR(ILLEGAL_ARGUMENT); + } } } } diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc index 7b30a9da74..0d1704ca4d 100644 --- a/runtime/openjdkjvmti/ti_class.cc +++ b/runtime/openjdkjvmti/ti_class.cc @@ -52,7 +52,6 @@ jvmtiError ClassUtil::GetClassFields(jvmtiEnv* env, return ERR(NULL_POINTER); } - art::StackHandleScope<1> hs(soa.Self()); art::IterationRange<art::StrideIterator<art::ArtField>> ifields = klass->GetIFields(); art::IterationRange<art::StrideIterator<art::ArtField>> sfields = klass->GetSFields(); size_t array_size = klass->NumInstanceFields() + klass->NumStaticFields(); @@ -80,6 +79,99 @@ jvmtiError ClassUtil::GetClassFields(jvmtiEnv* env, return ERR(NONE); } +jvmtiError ClassUtil::GetClassMethods(jvmtiEnv* env, + jclass jklass, + jint* method_count_ptr, + jmethodID** methods_ptr) { + art::ScopedObjectAccess soa(art::Thread::Current()); + art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass); + if (klass == nullptr) { + return ERR(INVALID_CLASS); + } + + if (method_count_ptr == nullptr || methods_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + size_t array_size = klass->NumDeclaredVirtualMethods() + klass->NumDirectMethods(); + unsigned char* out_ptr; + jvmtiError allocError = env->Allocate(array_size * sizeof(jmethodID), &out_ptr); + if (allocError != ERR(NONE)) { + return allocError; + } + jmethodID* method_array = reinterpret_cast<jmethodID*>(out_ptr); + + if (art::kIsDebugBuild) { + size_t count = 0; + for (auto& m ATTRIBUTE_UNUSED : klass->GetDeclaredMethods(art::kRuntimePointerSize)) { + count++; + } + CHECK_EQ(count, klass->NumDirectMethods() + klass->NumDeclaredVirtualMethods()); + } + + size_t array_idx = 0; + for (auto& m : klass->GetDeclaredMethods(art::kRuntimePointerSize)) { + method_array[array_idx] = art::jni::EncodeArtMethod(&m); + ++array_idx; + } + + *method_count_ptr = static_cast<jint>(array_size); + *methods_ptr = method_array; + + return ERR(NONE); +} + +jvmtiError ClassUtil::GetImplementedInterfaces(jvmtiEnv* env, + jclass jklass, + jint* interface_count_ptr, + jclass** interfaces_ptr) { + art::ScopedObjectAccess soa(art::Thread::Current()); + art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass); + if (klass == nullptr) { + return ERR(INVALID_CLASS); + } + + if (interface_count_ptr == nullptr || interfaces_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + // Need to handle array specifically. Arrays implement Serializable and Cloneable, but the + // spec says these should not be reported. + if (klass->IsArrayClass()) { + *interface_count_ptr = 0; + *interfaces_ptr = nullptr; // TODO: Should we allocate a dummy here? + return ERR(NONE); + } + + size_t array_size = klass->NumDirectInterfaces(); + unsigned char* out_ptr; + jvmtiError allocError = env->Allocate(array_size * sizeof(jclass), &out_ptr); + if (allocError != ERR(NONE)) { + return allocError; + } + jclass* interface_array = reinterpret_cast<jclass*>(out_ptr); + + art::StackHandleScope<1> hs(soa.Self()); + art::Handle<art::mirror::Class> h_klass(hs.NewHandle(klass)); + + for (uint32_t idx = 0; idx != array_size; ++idx) { + art::ObjPtr<art::mirror::Class> inf_klass = + art::mirror::Class::ResolveDirectInterface(soa.Self(), h_klass, idx); + if (inf_klass == nullptr) { + soa.Self()->ClearException(); + env->Deallocate(out_ptr); + // TODO: What is the right error code here? + return ERR(INTERNAL); + } + interface_array[idx] = soa.AddLocalReference<jclass>(inf_klass); + } + + *interface_count_ptr = static_cast<jint>(array_size); + *interfaces_ptr = interface_array; + + return ERR(NONE); +} + jvmtiError ClassUtil::GetClassSignature(jvmtiEnv* env, jclass jklass, char** signature_ptr, @@ -182,4 +274,58 @@ jvmtiError ClassUtil::IsArrayClass(jvmtiEnv* env ATTRIBUTE_UNUSED, return ClassIsT(jklass, test, is_array_class_ptr); } +// Keep this in sync with Class.getModifiers(). +static uint32_t ClassGetModifiers(art::Thread* self, art::ObjPtr<art::mirror::Class> klass) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + if (klass->IsArrayClass()) { + uint32_t component_modifiers = ClassGetModifiers(self, klass->GetComponentType()); + if ((component_modifiers & art::kAccInterface) != 0) { + component_modifiers &= ~(art::kAccInterface | art::kAccStatic); + } + return art::kAccAbstract | art::kAccFinal | component_modifiers; + } + + uint32_t modifiers = klass->GetAccessFlags() & art::kAccJavaFlagsMask; + + art::StackHandleScope<1> hs(self); + art::Handle<art::mirror::Class> h_klass(hs.NewHandle(klass)); + return art::mirror::Class::GetInnerClassFlags(h_klass, modifiers); +} + +jvmtiError ClassUtil::GetClassModifiers(jvmtiEnv* env ATTRIBUTE_UNUSED, + jclass jklass, + jint* modifiers_ptr) { + art::ScopedObjectAccess soa(art::Thread::Current()); + art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass); + if (klass == nullptr) { + return ERR(INVALID_CLASS); + } + + if (modifiers_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + *modifiers_ptr = ClassGetModifiers(soa.Self(), klass); + + return ERR(NONE); +} + +jvmtiError ClassUtil::GetClassLoader(jvmtiEnv* env ATTRIBUTE_UNUSED, + jclass jklass, + jobject* classloader_ptr) { + art::ScopedObjectAccess soa(art::Thread::Current()); + art::ObjPtr<art::mirror::Class> klass = soa.Decode<art::mirror::Class>(jklass); + if (klass == nullptr) { + return ERR(INVALID_CLASS); + } + + if (classloader_ptr == nullptr) { + return ERR(NULL_POINTER); + } + + *classloader_ptr = soa.AddLocalReference<jobject>(klass->GetClassLoader()); + + return ERR(NONE); +} + } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_class.h b/runtime/openjdkjvmti/ti_class.h index 5ee64be77a..577fc8e866 100644 --- a/runtime/openjdkjvmti/ti_class.h +++ b/runtime/openjdkjvmti/ti_class.h @@ -44,6 +44,18 @@ class ClassUtil { jint* field_count_ptr, jfieldID** fields_ptr); + static jvmtiError GetClassMethods(jvmtiEnv* env, + jclass klass, + jint* method_count_ptr, + jmethodID** methods_ptr); + + static jvmtiError GetImplementedInterfaces(jvmtiEnv* env, + jclass klass, + jint* interface_count_ptr, + jclass** interfaces_ptr); + + static jvmtiError GetClassModifiers(jvmtiEnv* env, jclass klass, jint* modifiers_ptr); + static jvmtiError GetClassSignature(jvmtiEnv* env, jclass klass, char** signature_ptr, @@ -51,6 +63,8 @@ class ClassUtil { static jvmtiError GetClassStatus(jvmtiEnv* env, jclass klass, jint* status_ptr); + static jvmtiError GetClassLoader(jvmtiEnv* env, jclass klass, jobject* classloader_ptr); + static jvmtiError IsInterface(jvmtiEnv* env, jclass klass, jboolean* is_interface_ptr); static jvmtiError IsArrayClass(jvmtiEnv* env, jclass klass, jboolean* is_array_class_ptr); }; diff --git a/test/903-hello-tagging/tagging.cc b/test/903-hello-tagging/tagging.cc index 1557d45a6a..60a31bdd2d 100644 --- a/test/903-hello-tagging/tagging.cc +++ b/test/903-hello-tagging/tagging.cc @@ -44,6 +44,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_setTag(JNIEnv* env ATTRIBUTE_UNUSED, char* err; jvmti_env->GetErrorName(ret, &err); printf("Error setting tag: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); } } @@ -56,6 +57,7 @@ extern "C" JNIEXPORT jlong JNICALL Java_Main_getTag(JNIEnv* env ATTRIBUTE_UNUSED char* err; jvmti_env->GetErrorName(ret, &err); printf("Error getting tag: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); } return tag; } @@ -90,6 +92,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getTaggedObjects(JNIEnv* env char* err; jvmti_env->GetErrorName(ret, &err); printf("Failure running GetLoadedClasses: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); return nullptr; } diff --git a/test/904-object-allocation/tracking.cc b/test/904-object-allocation/tracking.cc index 9261a9f9a1..f993606b42 100644 --- a/test/904-object-allocation/tracking.cc +++ b/test/904-object-allocation/tracking.cc @@ -69,6 +69,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_setupObjectAllocCallback( char* err; jvmti_env->GetErrorName(ret, &err); printf("Error setting callbacks: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); } } @@ -84,6 +85,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_enableAllocationTracking(JNIEnv* env char* err; jvmti_env->GetErrorName(ret, &err); printf("Error enabling/disabling allocation tracking: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); } } diff --git a/test/905-object-free/tracking_free.cc b/test/905-object-free/tracking_free.cc index fc43acce79..7f295accb2 100644 --- a/test/905-object-free/tracking_free.cc +++ b/test/905-object-free/tracking_free.cc @@ -50,6 +50,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_setupObjectFreeCallback( char* err; jvmti_env->GetErrorName(ret, &err); printf("Error setting callbacks: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); } } @@ -64,6 +65,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_enableFreeTracking(JNIEnv* env ATTRI char* err; jvmti_env->GetErrorName(ret, &err); printf("Error enabling/disabling object-free callbacks: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); } } diff --git a/test/906-iterate-heap/iterate_heap.cc b/test/906-iterate-heap/iterate_heap.cc index 8dac89d1a5..a2fd59128f 100644 --- a/test/906-iterate-heap/iterate_heap.cc +++ b/test/906-iterate-heap/iterate_heap.cc @@ -61,6 +61,7 @@ static bool Run(jint heap_filter, jclass klass_filter, IterationConfig* config) char* err; jvmti_env->GetErrorName(ret, &err); printf("Failure running IterateThroughHeap: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); return false; } return true; diff --git a/test/907-get-loaded-classes/get_loaded_classes.cc b/test/907-get-loaded-classes/get_loaded_classes.cc index afbb77415e..36d33b63cc 100644 --- a/test/907-get-loaded-classes/get_loaded_classes.cc +++ b/test/907-get-loaded-classes/get_loaded_classes.cc @@ -48,6 +48,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getLoadedClasses( char* err; jvmti_env->GetErrorName(result, &err); printf("Failure running GetLoadedClasses: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); return nullptr; } diff --git a/test/908-gc-start-finish/gc_callbacks.cc b/test/908-gc-start-finish/gc_callbacks.cc index 771d1adae6..1fab79dcb1 100644 --- a/test/908-gc-start-finish/gc_callbacks.cc +++ b/test/908-gc-start-finish/gc_callbacks.cc @@ -51,6 +51,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_setupGcCallback( char* err; jvmti_env->GetErrorName(ret, &err); printf("Error setting callbacks: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); } } @@ -65,6 +66,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_enableGcTracking(JNIEnv* env ATTRIBU char* err; jvmti_env->GetErrorName(ret, &err); printf("Error enabling/disabling gc callbacks: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); } ret = jvmti_env->SetEventNotificationMode( enable ? JVMTI_ENABLE : JVMTI_DISABLE, @@ -74,6 +76,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_enableGcTracking(JNIEnv* env ATTRIBU char* err; jvmti_env->GetErrorName(ret, &err); printf("Error enabling/disabling gc callbacks: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); } } diff --git a/test/910-methods/methods.cc b/test/910-methods/methods.cc index 3ed91d7a17..b64952d62b 100644 --- a/test/910-methods/methods.cc +++ b/test/910-methods/methods.cc @@ -41,6 +41,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getMethodName( char* err; jvmti_env->GetErrorName(result, &err); printf("Failure running GetMethodName: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); return nullptr; } @@ -72,6 +73,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getMethodName( char* err; jvmti_env->GetErrorName(result2, &err); printf("Failure running GetMethodName(null, null, null): %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); return nullptr; } @@ -88,6 +90,7 @@ extern "C" JNIEXPORT jclass JNICALL Java_Main_getMethodDeclaringClass( char* err; jvmti_env->GetErrorName(result, &err); printf("Failure running GetMethodDeclaringClass: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); return nullptr; } @@ -104,6 +107,7 @@ extern "C" JNIEXPORT jint JNICALL Java_Main_getMethodModifiers( char* err; jvmti_env->GetErrorName(result, &err); printf("Failure running GetMethodModifiers: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); return 0; } diff --git a/test/911-get-stack-trace/stack_trace.cc b/test/911-get-stack-trace/stack_trace.cc index 57f6a927ea..b3e8bc3b1f 100644 --- a/test/911-get-stack-trace/stack_trace.cc +++ b/test/911-get-stack-trace/stack_trace.cc @@ -63,6 +63,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getStackTrace( char* err; jvmti_env->GetErrorName(result, &err); printf("Failure running GetStackTrace: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); return nullptr; } } @@ -77,6 +78,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getStackTrace( char* err; jvmti_env->GetErrorName(result2, &err); printf("Failure running GetMethodName: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); return nullptr; } } @@ -94,6 +96,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getStackTrace( char* err; jvmti_env->GetErrorName(line_result, &err); printf("Failure running GetLineNumberTable: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); return nullptr; } line_number_table = nullptr; diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc index 28c5931521..19d82c544c 100644 --- a/test/912-classes/classes.cc +++ b/test/912-classes/classes.cc @@ -38,6 +38,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassSignature( char* err; jvmti_env->GetErrorName(result, &err); printf("Failure running GetClassSignature: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); return nullptr; } @@ -69,6 +70,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isInterface( char* err; jvmti_env->GetErrorName(result, &err); printf("Failure running IsInterface: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); return JNI_FALSE; } return is_interface; @@ -82,11 +84,25 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isArrayClass( char* err; jvmti_env->GetErrorName(result, &err); printf("Failure running IsArrayClass: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); return JNI_FALSE; } return is_array_class; } +extern "C" JNIEXPORT jint JNICALL Java_Main_getClassModifiers( + JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) { + jint mod; + jvmtiError result = jvmti_env->GetClassModifiers(klass, &mod); + if (result != JVMTI_ERROR_NONE) { + char* err; + jvmti_env->GetErrorName(result, &err); + printf("Failure running GetClassModifiers: %s\n", err); + return JNI_FALSE; + } + return mod; +} + extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassFields( JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) { jint count = 0; @@ -96,6 +112,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassFields( char* err; jvmti_env->GetErrorName(result, &err); printf("Failure running GetClassFields: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); return nullptr; } @@ -108,7 +125,61 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassFields( fields[i], (modifiers & kStatic) != 0 ? JNI_TRUE : JNI_FALSE); }; - return CreateObjectArray(env, count, "java/lang/Object", callback); + jobjectArray ret = CreateObjectArray(env, count, "java/lang/Object", callback); + if (fields != nullptr) { + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(fields)); + } + return ret; +} + +extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassMethods( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) { + jint count = 0; + jmethodID* methods = nullptr; + jvmtiError result = jvmti_env->GetClassMethods(klass, &count, &methods); + if (result != JVMTI_ERROR_NONE) { + char* err; + jvmti_env->GetErrorName(result, &err); + printf("Failure running GetClassMethods: %s\n", err); + return nullptr; + } + + auto callback = [&](jint i) { + jint modifiers; + // Ignore any errors for simplicity. + jvmti_env->GetMethodModifiers(methods[i], &modifiers); + constexpr jint kStatic = 0x8; + return env->ToReflectedMethod(klass, + methods[i], + (modifiers & kStatic) != 0 ? JNI_TRUE : JNI_FALSE); + }; + jobjectArray ret = CreateObjectArray(env, count, "java/lang/Object", callback); + if (methods != nullptr) { + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(methods)); + } + return ret; +} + +extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getImplementedInterfaces( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) { + jint count = 0; + jclass* classes = nullptr; + jvmtiError result = jvmti_env->GetImplementedInterfaces(klass, &count, &classes); + if (result != JVMTI_ERROR_NONE) { + char* err; + jvmti_env->GetErrorName(result, &err); + printf("Failure running GetImplementedInterfaces: %s\n", err); + return nullptr; + } + + auto callback = [&](jint i) { + return classes[i]; + }; + jobjectArray ret = CreateObjectArray(env, count, "java/lang/Class", callback); + if (classes != nullptr) { + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(classes)); + } + return ret; } extern "C" JNIEXPORT jint JNICALL Java_Main_getClassStatus( @@ -119,11 +190,26 @@ extern "C" JNIEXPORT jint JNICALL Java_Main_getClassStatus( char* err; jvmti_env->GetErrorName(result, &err); printf("Failure running GetClassStatus: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); return JNI_FALSE; } return status; } +extern "C" JNIEXPORT jobject JNICALL Java_Main_getClassLoader( + JNIEnv* env ATTRIBUTE_UNUSED, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) { + jobject classloader; + jvmtiError result = jvmti_env->GetClassLoader(klass, &classloader); + if (result != JVMTI_ERROR_NONE) { + char* err; + jvmti_env->GetErrorName(result, &err); + printf("Failure running GetClassLoader: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); + return nullptr; + } + return classloader; +} + // Don't do anything jint OnLoad(JavaVM* vm, char* options ATTRIBUTE_UNUSED, diff --git a/test/912-classes/expected.txt b/test/912-classes/expected.txt index 44fed795ad..3507a1a676 100644 --- a/test/912-classes/expected.txt +++ b/test/912-classes/expected.txt @@ -1,10 +1,17 @@ [Ljava/lang/Object;, null] +1 [Ljava/lang/String;, null] +11 [Ljava/lang/Math;, null] +11 [Ljava/util/List;, null] +601 [L$Proxy0;, null] +11 [I, null] +411 [[D, null] +411 int interface=false array=false $Proxy0 interface=false array=false java.lang.Runnable interface=true array=false @@ -15,8 +22,24 @@ java.lang.String interface=false array=false [public static final int java.lang.Integer.BYTES, static final char[] java.lang.Integer.DigitOnes, static final char[] java.lang.Integer.DigitTens, public static final int java.lang.Integer.MAX_VALUE, public static final int java.lang.Integer.MIN_VALUE, public static final int java.lang.Integer.SIZE, private static final java.lang.String[] java.lang.Integer.SMALL_NEG_VALUES, private static final java.lang.String[] java.lang.Integer.SMALL_NONNEG_VALUES, public static final java.lang.Class java.lang.Integer.TYPE, static final char[] java.lang.Integer.digits, private static final long java.lang.Integer.serialVersionUID, static final int[] java.lang.Integer.sizeTable, private final int java.lang.Integer.value] [] [] +[java.lang.Integer(), public java.lang.Integer(int), public java.lang.Integer(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.bitCount(int), public static int java.lang.Integer.compare(int,int), public static int java.lang.Integer.compareUnsigned(int,int), public static java.lang.Integer java.lang.Integer.decode(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.divideUnsigned(int,int), static int java.lang.Integer.formatUnsignedInt(int,int,char[],int,int), static void java.lang.Integer.getChars(int,int,char[]), public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String), public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String,int), public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String,java.lang.Integer), public static int java.lang.Integer.hashCode(int), public static int java.lang.Integer.highestOneBit(int), public static int java.lang.Integer.lowestOneBit(int), public static int java.lang.Integer.max(int,int), public static int java.lang.Integer.min(int,int), public static int java.lang.Integer.numberOfLeadingZeros(int), public static int java.lang.Integer.numberOfTrailingZeros(int), public static int java.lang.Integer.parseInt(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseInt(java.lang.String,int) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseUnsignedInt(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseUnsignedInt(java.lang.String,int) throws java.lang.NumberFormatException, public static int java.lang.Integer.remainderUnsigned(int,int), public static int java.lang.Integer.reverse(int), public static int java.lang.Integer.reverseBytes(int), public static int java.lang.Integer.rotateLeft(int,int), public static int java.lang.Integer.rotateRight(int,int), public static int java.lang.Integer.signum(int), static int java.lang.Integer.stringSize(int), public static int java.lang.Integer.sum(int,int), public static java.lang.String java.lang.Integer.toBinaryString(int), public static java.lang.String java.lang.Integer.toHexString(int), public static java.lang.String java.lang.Integer.toOctalString(int), public static java.lang.String java.lang.Integer.toString(int), public static java.lang.String java.lang.Integer.toString(int,int), public static long java.lang.Integer.toUnsignedLong(int), public static java.lang.String java.lang.Integer.toUnsignedString(int), public static java.lang.String java.lang.Integer.toUnsignedString(int,int), private static java.lang.String java.lang.Integer.toUnsignedString0(int,int), public static java.lang.Integer java.lang.Integer.valueOf(int), public static java.lang.Integer java.lang.Integer.valueOf(java.lang.String) throws java.lang.NumberFormatException, public static java.lang.Integer java.lang.Integer.valueOf(java.lang.String,int) throws java.lang.NumberFormatException, public byte java.lang.Integer.byteValue(), public int java.lang.Integer.compareTo(java.lang.Integer), public int java.lang.Integer.compareTo(java.lang.Object), public double java.lang.Integer.doubleValue(), public boolean java.lang.Integer.equals(java.lang.Object), public float java.lang.Integer.floatValue(), public int java.lang.Integer.hashCode(), public int java.lang.Integer.intValue(), public long java.lang.Integer.longValue(), public short java.lang.Integer.shortValue(), public java.lang.String java.lang.Integer.toString()] +[] +[] int 100000 class [Ljava.lang.String; 10000 class java.lang.Object 111 class Main$TestForNonInit 11 class Main$TestForInitFail 1001 +int [] +class [Ljava.lang.String; [] +class java.lang.Object [] +interface Main$InfA [] +interface Main$InfB [interface Main$InfA] +interface Main$InfC [interface Main$InfB] +class Main$ClassA [interface Main$InfA] +class Main$ClassB [interface Main$InfB] +class Main$ClassC [interface Main$InfA, interface Main$InfC] +class java.lang.String null +class [Ljava.lang.String; null +interface Main$InfA dalvik.system.PathClassLoader +class $Proxy0 dalvik.system.PathClassLoader diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java index 0b41113410..69e5a4cc58 100644 --- a/test/912-classes/src/Main.java +++ b/test/912-classes/src/Main.java @@ -48,6 +48,10 @@ public class Main { testClassFields(int.class); testClassFields(String[].class); + testClassMethods(Integer.class); + testClassMethods(int.class); + testClassMethods(String[].class); + testClassStatus(int.class); testClassStatus(String[].class); testClassStatus(Object.class); @@ -57,6 +61,21 @@ public class Main { } catch (ExceptionInInitializerError e) { } testClassStatus(TestForInitFail.class); + + testInterfaces(int.class); + testInterfaces(String[].class); + testInterfaces(Object.class); + testInterfaces(InfA.class); + testInterfaces(InfB.class); + testInterfaces(InfC.class); + testInterfaces(ClassA.class); + testInterfaces(ClassB.class); + testInterfaces(ClassC.class); + + testClassLoader(String.class); + testClassLoader(String[].class); + testClassLoader(InfA.class); + testClassLoader(getProxyClass()); } private static Class<?> proxyClass = null; @@ -78,6 +97,11 @@ public class Main { private static void testClass(Class<?> base) throws Exception { String[] result = getClassSignature(base); System.out.println(Arrays.toString(result)); + int mod = getClassModifiers(base); + if (mod != base.getModifiers()) { + throw new RuntimeException("Unexpected modifiers: " + base.getModifiers() + " vs " + mod); + } + System.out.println(Integer.toHexString(mod)); } private static void testClassType(Class<?> c) throws Exception { @@ -90,19 +114,56 @@ public class Main { System.out.println(Arrays.toString(getClassFields(c))); } + private static void testClassMethods(Class<?> c) throws Exception { + System.out.println(Arrays.toString(getClassMethods(c))); + } + private static void testClassStatus(Class<?> c) { System.out.println(c + " " + Integer.toBinaryString(getClassStatus(c))); } + private static void testInterfaces(Class<?> c) { + System.out.println(c + " " + Arrays.toString(getImplementedInterfaces(c))); + } + + private static boolean IsBootClassLoader(ClassLoader l) { + // Hacky check for Android's fake boot classloader. + return l.getClass().getName().equals("java.lang.BootClassLoader"); + } + + private static void testClassLoader(Class<?> c) { + Object cl = getClassLoader(c); + System.out.println(c + " " + (cl != null ? cl.getClass().getName() : "null")); + if (cl == null) { + if (c.getClassLoader() != null && !IsBootClassLoader(c.getClassLoader())) { + throw new RuntimeException("Expected " + c.getClassLoader() + ", but got null."); + } + } else { + if (!(cl instanceof ClassLoader)) { + throw new RuntimeException("Unexpected \"classloader\": " + cl + " (" + cl.getClass() + + ")"); + } + if (cl != c.getClassLoader()) { + throw new RuntimeException("Unexpected classloader: " + c.getClassLoader() + " vs " + cl); + } + } + } + private static native String[] getClassSignature(Class<?> c); private static native boolean isInterface(Class<?> c); private static native boolean isArrayClass(Class<?> c); + private static native int getClassModifiers(Class<?> c); + private static native Object[] getClassFields(Class<?> c); + private static native Object[] getClassMethods(Class<?> c); + private static native Class[] getImplementedInterfaces(Class<?> c); private static native int getClassStatus(Class<?> c); + private static native Object getClassLoader(Class<?> c); + private static class TestForNonInit { public static double dummy = Math.random(); // So it can't be compile-time initialized. } @@ -110,4 +171,18 @@ public class Main { private static class TestForInitFail { public static int dummy = ((int)Math.random())/0; // So it throws when initializing. } + + public static interface InfA { + } + public static interface InfB extends InfA { + } + public static interface InfC extends InfB { + } + + public abstract static class ClassA implements InfA { + } + public abstract static class ClassB extends ClassA implements InfB { + } + public abstract static class ClassC implements InfA, InfC { + } } diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc index 49ab7dd83e..0b232af0df 100644 --- a/test/913-heaps/heaps.cc +++ b/test/913-heaps/heaps.cc @@ -49,6 +49,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_forceGarbageCollection(JNIEnv* env A char* err; jvmti_env->GetErrorName(ret, &err); printf("Error forcing a garbage collection: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); } } @@ -106,6 +107,7 @@ static bool Run(jint heap_filter, char* err; jvmti_env->GetErrorName(ret, &err); printf("Failure running FollowReferences: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); return false; } return true; diff --git a/test/918-fields/fields.cc b/test/918-fields/fields.cc index c7fca06747..4d2b34b94e 100644 --- a/test/918-fields/fields.cc +++ b/test/918-fields/fields.cc @@ -42,6 +42,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getFieldName( char* err; jvmti_env->GetErrorName(result, &err); printf("Failure running GetFieldName: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); return nullptr; } @@ -73,6 +74,7 @@ extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getFieldName( char* err; jvmti_env->GetErrorName(result2, &err); printf("Failure running GetFieldName(null, null, null): %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); return nullptr; } @@ -89,6 +91,7 @@ extern "C" JNIEXPORT jclass JNICALL Java_Main_getFieldDeclaringClass( char* err; jvmti_env->GetErrorName(result, &err); printf("Failure running GetFieldDeclaringClass: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); return nullptr; } @@ -105,6 +108,7 @@ extern "C" JNIEXPORT jint JNICALL Java_Main_getFieldModifiers( char* err; jvmti_env->GetErrorName(result, &err); printf("Failure running GetFieldModifiers: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); return 0; } @@ -121,6 +125,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isFieldSynthetic( char* err; jvmti_env->GetErrorName(result, &err); printf("Failure running IsFieldSynthetic: %s\n", err); + jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(err)); return 0; } |