diff options
| -rw-r--r-- | runtime/class_linker.h | 8 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/OpenjdkJvmTi.cc | 2 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/ti_class.cc | 89 | ||||
| -rw-r--r-- | runtime/openjdkjvmti/ti_class.h | 5 | ||||
| -rw-r--r-- | test/912-classes/classes.cc | 19 | ||||
| -rw-r--r-- | test/912-classes/expected.txt | 16 | ||||
| -rw-r--r-- | test/912-classes/src-ex/A.java | 18 | ||||
| -rw-r--r-- | test/912-classes/src/B.java | 18 | ||||
| -rw-r--r-- | test/912-classes/src/Main.java | 116 |
9 files changed, 285 insertions, 6 deletions
diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 580acb7068..cad674e472 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -645,6 +645,10 @@ class ClassLinker { mirror::Class* GetHoldingClassOfCopiedMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); + // Returns null if not found. + ClassTable* ClassTableForClassLoader(ObjPtr<mirror::ClassLoader> class_loader) + REQUIRES_SHARED(Locks::mutator_lock_); + struct DexCacheData { // Weak root to the DexCache. Note: Do not decode this unnecessarily or else class unloading may // not work properly. @@ -1039,10 +1043,6 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::classlinker_classes_lock_); - // Returns null if not found. - ClassTable* ClassTableForClassLoader(ObjPtr<mirror::ClassLoader> class_loader) - REQUIRES_SHARED(Locks::mutator_lock_); - // Insert a new class table if not found. ClassTable* InsertClassTableForClassLoader(ObjPtr<mirror::ClassLoader> class_loader) REQUIRES_SHARED(Locks::mutator_lock_) diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index 367b60d630..3825c41e76 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -537,7 +537,7 @@ class JvmtiFunctions { jobject initiating_loader, jint* class_count_ptr, jclass** classes_ptr) { - return ERR(NOT_IMPLEMENTED); + return ClassUtil::GetClassLoaderClasses(env, initiating_loader, class_count_ptr, classes_ptr); } static jvmtiError GetClassSignature(jvmtiEnv* env, diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc index 0d1704ca4d..d1324bc13f 100644 --- a/runtime/openjdkjvmti/ti_class.cc +++ b/runtime/openjdkjvmti/ti_class.cc @@ -32,7 +32,10 @@ #include "ti_class.h" #include "art_jvmti.h" +#include "class_table-inl.h" +#include "class_linker.h" #include "jni_internal.h" +#include "runtime.h" #include "scoped_thread_state_change-inl.h" #include "thread-inl.h" @@ -328,4 +331,90 @@ jvmtiError ClassUtil::GetClassLoader(jvmtiEnv* env ATTRIBUTE_UNUSED, return ERR(NONE); } +jvmtiError ClassUtil::GetClassLoaderClasses(jvmtiEnv* env, + jobject initiating_loader, + jint* class_count_ptr, + jclass** classes_ptr) { + UNUSED(env, initiating_loader, class_count_ptr, classes_ptr); + + if (class_count_ptr == nullptr || classes_ptr == nullptr) { + return ERR(NULL_POINTER); + } + art::Thread* self = art::Thread::Current(); + if (!self->GetJniEnv()->IsInstanceOf(initiating_loader, + art::WellKnownClasses::java_lang_ClassLoader)) { + return ERR(ILLEGAL_ARGUMENT); + } + if (self->GetJniEnv()->IsInstanceOf(initiating_loader, + art::WellKnownClasses::java_lang_BootClassLoader)) { + // Need to use null for the BootClassLoader. + initiating_loader = nullptr; + } + + art::ScopedObjectAccess soa(self); + art::ObjPtr<art::mirror::ClassLoader> class_loader = + soa.Decode<art::mirror::ClassLoader>(initiating_loader); + + art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker(); + + art::ReaderMutexLock mu(self, *art::Locks::classlinker_classes_lock_); + + art::ClassTable* class_table = class_linker->ClassTableForClassLoader(class_loader); + if (class_table == nullptr) { + // Nothing loaded. + *class_count_ptr = 0; + *classes_ptr = nullptr; + return ERR(NONE); + } + + struct ClassTableCount { + bool operator()(art::ObjPtr<art::mirror::Class> klass) { + DCHECK(klass != nullptr); + ++count; + return true; + } + + size_t count = 0; + }; + ClassTableCount ctc; + class_table->Visit(ctc); + + if (ctc.count == 0) { + // Nothing loaded. + *class_count_ptr = 0; + *classes_ptr = nullptr; + return ERR(NONE); + } + + unsigned char* data; + jvmtiError data_result = env->Allocate(ctc.count * sizeof(jclass), &data); + if (data_result != ERR(NONE)) { + return data_result; + } + jclass* class_array = reinterpret_cast<jclass*>(data); + + struct ClassTableFill { + bool operator()(art::ObjPtr<art::mirror::Class> klass) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + DCHECK(klass != nullptr); + DCHECK_LT(count, ctc_ref.count); + local_class_array[count++] = soa_ptr->AddLocalReference<jclass>(klass); + return true; + } + + jclass* local_class_array; + const ClassTableCount& ctc_ref; + art::ScopedObjectAccess* soa_ptr; + size_t count; + }; + ClassTableFill ctf = { class_array, ctc, &soa, 0 }; + class_table->Visit(ctf); + DCHECK_EQ(ctc.count, ctf.count); + + *class_count_ptr = ctc.count; + *classes_ptr = class_array; + + return ERR(NONE); +} + } // namespace openjdkjvmti diff --git a/runtime/openjdkjvmti/ti_class.h b/runtime/openjdkjvmti/ti_class.h index 577fc8e866..7a0fafbc83 100644 --- a/runtime/openjdkjvmti/ti_class.h +++ b/runtime/openjdkjvmti/ti_class.h @@ -65,6 +65,11 @@ class ClassUtil { static jvmtiError GetClassLoader(jvmtiEnv* env, jclass klass, jobject* classloader_ptr); + static jvmtiError GetClassLoaderClasses(jvmtiEnv* env, + jobject initiating_loader, + jint* class_count_ptr, + jclass** classes_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/912-classes/classes.cc b/test/912-classes/classes.cc index 69301c7925..a22d1d72d8 100644 --- a/test/912-classes/classes.cc +++ b/test/912-classes/classes.cc @@ -222,5 +222,24 @@ extern "C" JNIEXPORT jobject JNICALL Java_Main_getClassLoader( return classloader; } +extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getClassLoaderClasses( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jobject jclassloader) { + jint count = 0; + jclass* classes = nullptr; + jvmtiError result = jvmti_env->GetClassLoaderClasses(jclassloader, &count, &classes); + if (JvmtiErrorToException(env, result)) { + 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; +} + } // namespace Test912Classes } // namespace art diff --git a/test/912-classes/expected.txt b/test/912-classes/expected.txt index 44c861a3b7..a95a465860 100644 --- a/test/912-classes/expected.txt +++ b/test/912-classes/expected.txt @@ -43,3 +43,19 @@ class java.lang.String null class [Ljava.lang.String; null interface Main$InfA dalvik.system.PathClassLoader class $Proxy0 dalvik.system.PathClassLoader + +boot <- src <- src-ex (A,B) +912-classes-ex.jar+ -> 912-classes.jar+ -> +[class A, class B, class java.lang.Object] +912-classes.jar+ -> +[class B, class java.lang.Object] + +boot <- src (B) <- src-ex (A, List) +912-classes-ex.jar+ -> 912-classes.jar+ -> +[class A, class java.lang.Object, interface java.util.List] +912-classes.jar+ -> +[class B, class java.lang.Object] + +boot <- src+src-ex (A,B) +912-classes.jar+ -> +[class A, class B, class java.lang.Object] diff --git a/test/912-classes/src-ex/A.java b/test/912-classes/src-ex/A.java new file mode 100644 index 0000000000..64acb2fcfe --- /dev/null +++ b/test/912-classes/src-ex/A.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class A { +}
\ No newline at end of file diff --git a/test/912-classes/src/B.java b/test/912-classes/src/B.java new file mode 100644 index 0000000000..f1458c3bca --- /dev/null +++ b/test/912-classes/src/B.java @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class B { +}
\ No newline at end of file diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java index e627d4227a..ea3c49c87b 100644 --- a/test/912-classes/src/Main.java +++ b/test/912-classes/src/Main.java @@ -14,8 +14,10 @@ * limitations under the License. */ +import java.lang.reflect.Constructor; import java.lang.reflect.Proxy; import java.util.Arrays; +import java.util.Comparator; public class Main { public static void main(String[] args) throws Exception { @@ -76,6 +78,8 @@ public class Main { testClassLoader(String[].class); testClassLoader(InfA.class); testClassLoader(getProxyClass()); + + testClassLoaderClasses(); } private static Class<?> proxyClass = null; @@ -151,6 +155,95 @@ public class Main { } } + private static void testClassLoaderClasses() throws Exception { + ClassLoader boot = ClassLoader.getSystemClassLoader().getParent(); + while (boot.getParent() != null) { + boot = boot.getParent(); + } + + System.out.println(); + System.out.println("boot <- src <- src-ex (A,B)"); + ClassLoader cl1 = create(create(boot, DEX1), DEX2); + Class.forName("B", false, cl1); + Class.forName("A", false, cl1); + printClassLoaderClasses(cl1); + + System.out.println(); + System.out.println("boot <- src (B) <- src-ex (A, List)"); + ClassLoader cl2 = create(create(boot, DEX1), DEX2); + Class.forName("A", false, cl2); + Class.forName("java.util.List", false, cl2); + Class.forName("B", false, cl2.getParent()); + printClassLoaderClasses(cl2); + + System.out.println(); + System.out.println("boot <- src+src-ex (A,B)"); + ClassLoader cl3 = create(boot, DEX1, DEX2); + Class.forName("B", false, cl3); + Class.forName("A", false, cl3); + printClassLoaderClasses(cl3); + + // Check that the boot classloader dumps something non-empty. + Class<?>[] bootClasses = getClassLoaderClasses(boot); + if (bootClasses.length == 0) { + throw new RuntimeException("No classes initiated by boot classloader."); + } + // Check that at least java.util.List is loaded. + boolean foundList = false; + for (Class<?> c : bootClasses) { + if (c == java.util.List.class) { + foundList = true; + break; + } + } + if (!foundList) { + System.out.println(Arrays.toString(bootClasses)); + throw new RuntimeException("Could not find class java.util.List."); + } + } + + private static void printClassLoaderClasses(ClassLoader cl) { + for (;;) { + if (cl == null || !cl.getClass().getName().startsWith("dalvik.system")) { + break; + } + + ClassLoader saved = cl; + for (;;) { + if (cl == null || !cl.getClass().getName().startsWith("dalvik.system")) { + break; + } + String s = cl.toString(); + int index1 = s.indexOf("zip file"); + int index2 = s.indexOf(']', index1); + if (index2 < 0) { + throw new RuntimeException("Unexpected classloader " + s); + } + String zip_file = s.substring(index1, index2); + int index3 = zip_file.indexOf('"'); + int index4 = zip_file.indexOf('"', index3 + 1); + if (index4 < 0) { + throw new RuntimeException("Unexpected classloader " + s); + } + String paths = zip_file.substring(index3 + 1, index4); + String pathArray[] = paths.split(":"); + for (String path : pathArray) { + int index5 = path.lastIndexOf('/'); + System.out.print(path.substring(index5 + 1)); + System.out.print('+'); + } + System.out.print(" -> "); + cl = cl.getParent(); + } + System.out.println(); + Class<?> classes[] = getClassLoaderClasses(saved); + Arrays.sort(classes, new ClassNameComparator()); + System.out.println(Arrays.toString(classes)); + + cl = saved.getParent(); + } + } + private static native boolean isModifiableClass(Class<?> c); private static native String[] getClassSignature(Class<?> c); @@ -161,12 +254,14 @@ public class Main { private static native Object[] getClassFields(Class<?> c); private static native Object[] getClassMethods(Class<?> c); - private static native Class[] getImplementedInterfaces(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 native Class<?>[] getClassLoaderClasses(ClassLoader cl); + private static class TestForNonInit { public static double dummy = Math.random(); // So it can't be compile-time initialized. } @@ -188,4 +283,23 @@ public class Main { } public abstract static class ClassC implements InfA, InfC { } + + private static final String DEX1 = System.getenv("DEX_LOCATION") + "/912-classes.jar"; + private static final String DEX2 = System.getenv("DEX_LOCATION") + "/912-classes-ex.jar"; + + private static ClassLoader create(ClassLoader parent, String... elements) throws Exception { + // Note: We use a PathClassLoader, as we do not care about code performance. We only load + // the classes, and they're empty. + Class<?> pathClassLoaderClass = Class.forName("dalvik.system.PathClassLoader"); + Constructor<?> pathClassLoaderInit = pathClassLoaderClass.getConstructor(String.class, + ClassLoader.class); + String path = String.join(":", elements); + return (ClassLoader) pathClassLoaderInit.newInstance(path, parent); + } + + private static class ClassNameComparator implements Comparator<Class<?>> { + public int compare(Class<?> c1, Class<?> c2) { + return c1.getName().compareTo(c2.getName()); + } + } } |