diff options
| -rw-r--r-- | runtime/class_linker.cc | 161 | ||||
| -rw-r--r-- | runtime/native/dalvik_system_VMStack.cc | 4 | ||||
| -rw-r--r-- | test/004-JniTest/jni_test.cc | 195 | ||||
| -rw-r--r-- | test/004-JniTest/src/Main.java | 7 |
4 files changed, 203 insertions, 164 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index db42146ffe..91cd11b0e8 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -4853,7 +4853,7 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t Handle<mirror::ClassLoader> class_loader, Handle<mirror::ArtMethod> referrer, InvokeType type) { - DCHECK(dex_cache.Get() != NULL); + DCHECK(dex_cache.Get() != nullptr); // Check for hit in the dex cache. mirror::ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx); if (resolved != nullptr && !resolved->IsRuntimeMethod()) { @@ -4862,9 +4862,9 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t // Fail, get the declaring class. const DexFile::MethodId& method_id = dex_file.GetMethodId(method_idx); mirror::Class* klass = ResolveType(dex_file, method_id.class_idx_, dex_cache, class_loader); - if (klass == NULL) { + if (klass == nullptr) { DCHECK(Thread::Current()->IsExceptionPending()); - return NULL; + return nullptr; } // Scan using method_idx, this saves string compares but will only hit for matching dex // caches/files. @@ -4875,7 +4875,7 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t break; case kInterface: resolved = klass->FindInterfaceMethod(dex_cache.Get(), method_idx); - DCHECK(resolved == NULL || resolved->GetDeclaringClass()->IsInterface()); + DCHECK(resolved == nullptr || resolved->GetDeclaringClass()->IsInterface()); break; case kSuper: // Fall-through. case kVirtual: @@ -4884,7 +4884,7 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t default: LOG(FATAL) << "Unreachable - invocation type: " << type; } - if (resolved == NULL) { + if (resolved == nullptr) { // Search by name, which works across dex files. const char* name = dex_file.StringDataByIdx(method_id.name_idx_); const Signature signature = dex_file.GetMethodSignature(method_id); @@ -4895,7 +4895,7 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t break; case kInterface: resolved = klass->FindInterfaceMethod(name, signature); - DCHECK(resolved == NULL || resolved->GetDeclaringClass()->IsInterface()); + DCHECK(resolved == nullptr || resolved->GetDeclaringClass()->IsInterface()); break; case kSuper: // Fall-through. case kVirtual: @@ -4903,94 +4903,97 @@ mirror::ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, uint32_t break; } } - if (resolved != NULL) { - // We found a method, check for incompatible class changes. - if (resolved->CheckIncompatibleClassChange(type)) { - resolved = NULL; - } - } - if (resolved != NULL) { + // If we found a method, check for incompatible class changes. + if (LIKELY(resolved != nullptr && !resolved->CheckIncompatibleClassChange(type))) { // Be a good citizen and update the dex cache to speed subsequent calls. dex_cache->SetResolvedMethod(method_idx, resolved); return resolved; } else { - // We failed to find the method which means either an access error, an incompatible class - // change, or no such method. First try to find the method among direct and virtual methods. - const char* name = dex_file.StringDataByIdx(method_id.name_idx_); - const Signature signature = dex_file.GetMethodSignature(method_id); - switch (type) { - case kDirect: - case kStatic: - resolved = klass->FindVirtualMethod(name, signature); - break; - case kInterface: - case kVirtual: - case kSuper: - resolved = klass->FindDirectMethod(name, signature); - break; - } + // If we had a method, it's an incompatible-class-change error. + if (resolved != nullptr) { + ThrowIncompatibleClassChangeError(type, resolved->GetInvokeType(), resolved, referrer.Get()); + } else { + // We failed to find the method which means either an access error, an incompatible class + // change, or no such method. First try to find the method among direct and virtual methods. + const char* name = dex_file.StringDataByIdx(method_id.name_idx_); + const Signature signature = dex_file.GetMethodSignature(method_id); + switch (type) { + case kDirect: + case kStatic: + resolved = klass->FindVirtualMethod(name, signature); + // Note: kDirect and kStatic are also mutually exclusive, but in that case we would + // have had a resolved method before, which triggers the "true" branch above. + break; + case kInterface: + case kVirtual: + case kSuper: + resolved = klass->FindDirectMethod(name, signature); + break; + } - // If we found something, check that it can be accessed by the referrer. - if (resolved != NULL && referrer.Get() != NULL) { - mirror::Class* methods_class = resolved->GetDeclaringClass(); - mirror::Class* referring_class = referrer->GetDeclaringClass(); - if (!referring_class->CanAccess(methods_class)) { - ThrowIllegalAccessErrorClassForMethodDispatch(referring_class, methods_class, - resolved, type); - return NULL; - } else if (!referring_class->CanAccessMember(methods_class, - resolved->GetAccessFlags())) { - ThrowIllegalAccessErrorMethod(referring_class, resolved); - return NULL; + // If we found something, check that it can be accessed by the referrer. + if (resolved != nullptr && referrer.Get() != nullptr) { + mirror::Class* methods_class = resolved->GetDeclaringClass(); + mirror::Class* referring_class = referrer->GetDeclaringClass(); + if (!referring_class->CanAccess(methods_class)) { + ThrowIllegalAccessErrorClassForMethodDispatch(referring_class, methods_class, + resolved, type); + return nullptr; + } else if (!referring_class->CanAccessMember(methods_class, + resolved->GetAccessFlags())) { + ThrowIllegalAccessErrorMethod(referring_class, resolved); + return nullptr; + } } - } - // Otherwise, throw an IncompatibleClassChangeError if we found something, and check interface - // methods and throw if we find the method there. If we find nothing, throw a NoSuchMethodError. - switch (type) { - case kDirect: - case kStatic: - if (resolved != NULL) { - ThrowIncompatibleClassChangeError(type, kVirtual, resolved, referrer.Get()); - } else { - resolved = klass->FindInterfaceMethod(name, signature); - if (resolved != NULL) { - ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer.Get()); + // Otherwise, throw an IncompatibleClassChangeError if we found something, and check interface + // methods and throw if we find the method there. If we find nothing, throw a + // NoSuchMethodError. + switch (type) { + case kDirect: + case kStatic: + if (resolved != nullptr) { + ThrowIncompatibleClassChangeError(type, kVirtual, resolved, referrer.Get()); } else { - ThrowNoSuchMethodError(type, klass, name, signature); + resolved = klass->FindInterfaceMethod(name, signature); + if (resolved != nullptr) { + ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer.Get()); + } else { + ThrowNoSuchMethodError(type, klass, name, signature); + } } - } - break; - case kInterface: - if (resolved != NULL) { - ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer.Get()); - } else { - resolved = klass->FindVirtualMethod(name, signature); - if (resolved != NULL) { - ThrowIncompatibleClassChangeError(type, kVirtual, resolved, referrer.Get()); + break; + case kInterface: + if (resolved != nullptr) { + ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer.Get()); } else { - ThrowNoSuchMethodError(type, klass, name, signature); + resolved = klass->FindVirtualMethod(name, signature); + if (resolved != nullptr) { + ThrowIncompatibleClassChangeError(type, kVirtual, resolved, referrer.Get()); + } else { + ThrowNoSuchMethodError(type, klass, name, signature); + } } - } - break; - case kSuper: - ThrowNoSuchMethodError(type, klass, name, signature); - break; - case kVirtual: - if (resolved != NULL) { - ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer.Get()); - } else { - resolved = klass->FindInterfaceMethod(name, signature); - if (resolved != NULL) { - ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer.Get()); + break; + case kSuper: + ThrowNoSuchMethodError(type, klass, name, signature); + break; + case kVirtual: + if (resolved != nullptr) { + ThrowIncompatibleClassChangeError(type, kDirect, resolved, referrer.Get()); } else { - ThrowNoSuchMethodError(type, klass, name, signature); + resolved = klass->FindInterfaceMethod(name, signature); + if (resolved != nullptr) { + ThrowIncompatibleClassChangeError(type, kInterface, resolved, referrer.Get()); + } else { + ThrowNoSuchMethodError(type, klass, name, signature); + } } - } - break; + break; + } } DCHECK(Thread::Current()->IsExceptionPending()); - return NULL; + return nullptr; } } diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc index 5f718ba213..047e9f6d3b 100644 --- a/runtime/native/dalvik_system_VMStack.cc +++ b/runtime/native/dalvik_system_VMStack.cc @@ -76,6 +76,10 @@ static jobject VMStack_getCallingClassLoader(JNIEnv* env, jclass) { ScopedFastNativeObjectAccess soa(env); NthCallerVisitor visitor(soa.Self(), 2); visitor.WalkStack(); + if (UNLIKELY(visitor.caller == nullptr)) { + // The caller is an attached native thread. + return nullptr; + } return soa.AddLocalReference<jobject>(visitor.caller->GetDeclaringClass()->GetClassLoader()); } diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc index 554712aa55..9a2fbdf267 100644 --- a/test/004-JniTest/jni_test.cc +++ b/test/004-JniTest/jni_test.cc @@ -28,162 +28,133 @@ static JavaVM* jvm = NULL; extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *) { - assert(vm != NULL); - assert(jvm == NULL); + assert(vm != nullptr); + assert(jvm == nullptr); jvm = vm; return JNI_VERSION_1_6; } -static void* testFindClassOnAttachedNativeThread(void*) { - assert(jvm != NULL); +static void* AttachHelper(void* arg) { + assert(jvm != nullptr); - JNIEnv* env = NULL; + JNIEnv* env = nullptr; JavaVMAttachArgs args = { JNI_VERSION_1_6, __FUNCTION__, NULL }; int attach_result = jvm->AttachCurrentThread(&env, &args); assert(attach_result == 0); - jclass clazz = env->FindClass("Main"); - assert(clazz != NULL); - assert(!env->ExceptionCheck()); - - jobjectArray array = env->NewObjectArray(0, clazz, NULL); - assert(array != NULL); - assert(!env->ExceptionCheck()); + typedef void (*Fn)(JNIEnv*); + Fn fn = reinterpret_cast<Fn>(arg); + fn(env); int detach_result = jvm->DetachCurrentThread(); assert(detach_result == 0); - return NULL; + return nullptr; } -// http://b/10994325 -extern "C" JNIEXPORT void JNICALL Java_Main_testFindClassOnAttachedNativeThread(JNIEnv*, - jclass) { +static void PthreadHelper(void (*fn)(JNIEnv*)) { pthread_t pthread; - int pthread_create_result = pthread_create(&pthread, - NULL, - testFindClassOnAttachedNativeThread, - NULL); + int pthread_create_result = pthread_create(&pthread, nullptr, AttachHelper, + reinterpret_cast<void*>(fn)); assert(pthread_create_result == 0); - int pthread_join_result = pthread_join(pthread, NULL); + int pthread_join_result = pthread_join(pthread, nullptr); assert(pthread_join_result == 0); } -static void* testFindFieldOnAttachedNativeThread(void*) { - assert(jvm != NULL); +static void testFindClassOnAttachedNativeThread(JNIEnv* env) { + jclass clazz = env->FindClass("Main"); + assert(clazz != nullptr); + assert(!env->ExceptionCheck()); - JNIEnv* env = NULL; - JavaVMAttachArgs args = { JNI_VERSION_1_6, __FUNCTION__, NULL }; - int attach_result = jvm->AttachCurrentThread(&env, &args); - assert(attach_result == 0); + jobjectArray array = env->NewObjectArray(0, clazz, nullptr); + assert(array != nullptr); + assert(!env->ExceptionCheck()); +} +// http://b/10994325 +extern "C" JNIEXPORT void JNICALL Java_Main_testFindClassOnAttachedNativeThread(JNIEnv*, jclass) { + PthreadHelper(&testFindClassOnAttachedNativeThread); +} + +static void testFindFieldOnAttachedNativeThread(JNIEnv* env) { jclass clazz = env->FindClass("Main"); - assert(clazz != NULL); + assert(clazz != nullptr); assert(!env->ExceptionCheck()); jfieldID field = env->GetStaticFieldID(clazz, "testFindFieldOnAttachedNativeThreadField", "Z"); - assert(field != NULL); + assert(field != nullptr); assert(!env->ExceptionCheck()); env->SetStaticBooleanField(clazz, field, JNI_TRUE); - - int detach_result = jvm->DetachCurrentThread(); - assert(detach_result == 0); - return NULL; } extern "C" JNIEXPORT void JNICALL Java_Main_testFindFieldOnAttachedNativeThreadNative(JNIEnv*, - jclass) { - pthread_t pthread; - int pthread_create_result = pthread_create(&pthread, - NULL, - testFindFieldOnAttachedNativeThread, - NULL); - assert(pthread_create_result == 0); - int pthread_join_result = pthread_join(pthread, NULL); - assert(pthread_join_result == 0); + jclass) { + PthreadHelper(&testFindFieldOnAttachedNativeThread); } -static void* testReflectFieldGetFromAttachedNativeThread(void*) { - assert(jvm != NULL); - - JNIEnv* env = NULL; - JavaVMAttachArgs args = { JNI_VERSION_1_6, __FUNCTION__, NULL }; - int attach_result = jvm->AttachCurrentThread(&env, &args); - assert(attach_result == 0); - +static void testReflectFieldGetFromAttachedNativeThread(JNIEnv* env) { jclass clazz = env->FindClass("Main"); - assert(clazz != NULL); + assert(clazz != nullptr); assert(!env->ExceptionCheck()); jclass class_clazz = env->FindClass("java/lang/Class"); - assert(class_clazz != NULL); + assert(class_clazz != nullptr); assert(!env->ExceptionCheck()); jmethodID getFieldMetodId = env->GetMethodID(class_clazz, "getField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;"); - assert(getFieldMetodId != NULL); + assert(getFieldMetodId != nullptr); assert(!env->ExceptionCheck()); jstring field_name = env->NewStringUTF("testReflectFieldGetFromAttachedNativeThreadField"); - assert(field_name != NULL); + assert(field_name != nullptr); assert(!env->ExceptionCheck()); jobject field = env->CallObjectMethod(clazz, getFieldMetodId, field_name); - assert(field != NULL); + assert(field != nullptr); assert(!env->ExceptionCheck()); jclass field_clazz = env->FindClass("java/lang/reflect/Field"); - assert(field_clazz != NULL); + assert(field_clazz != nullptr); assert(!env->ExceptionCheck()); jmethodID getBooleanMetodId = env->GetMethodID(field_clazz, "getBoolean", "(Ljava/lang/Object;)Z"); - assert(getBooleanMetodId != NULL); + assert(getBooleanMetodId != nullptr); assert(!env->ExceptionCheck()); jboolean value = env->CallBooleanMethod(field, getBooleanMetodId, /* ignored */ clazz); assert(value == false); assert(!env->ExceptionCheck()); - - int detach_result = jvm->DetachCurrentThread(); - assert(detach_result == 0); - return NULL; } // http://b/15539150 extern "C" JNIEXPORT void JNICALL Java_Main_testReflectFieldGetFromAttachedNativeThreadNative( JNIEnv*, jclass) { - pthread_t pthread; - int pthread_create_result = pthread_create(&pthread, - NULL, - testReflectFieldGetFromAttachedNativeThread, - NULL); - assert(pthread_create_result == 0); - int pthread_join_result = pthread_join(pthread, NULL); - assert(pthread_join_result == 0); + PthreadHelper(&testReflectFieldGetFromAttachedNativeThread); } // http://b/11243757 extern "C" JNIEXPORT void JNICALL Java_Main_testCallStaticVoidMethodOnSubClassNative(JNIEnv* env, - jclass) { + jclass) { jclass super_class = env->FindClass("Main$testCallStaticVoidMethodOnSubClass_SuperClass"); - assert(super_class != NULL); + assert(super_class != nullptr); jmethodID execute = env->GetStaticMethodID(super_class, "execute", "()V"); - assert(execute != NULL); + assert(execute != nullptr); jclass sub_class = env->FindClass("Main$testCallStaticVoidMethodOnSubClass_SubClass"); - assert(sub_class != NULL); + assert(sub_class != nullptr); env->CallStaticVoidMethod(sub_class, execute); } extern "C" JNIEXPORT jobject JNICALL Java_Main_testGetMirandaMethodNative(JNIEnv* env, jclass) { jclass abstract_class = env->FindClass("Main$testGetMirandaMethod_MirandaAbstract"); - assert(abstract_class != NULL); + assert(abstract_class != nullptr); jmethodID miranda_method = env->GetMethodID(abstract_class, "inInterface", "()Z"); - assert(miranda_method != NULL); + assert(miranda_method != nullptr); return env->ToReflectedMethod(abstract_class, miranda_method, JNI_FALSE); } @@ -191,7 +162,7 @@ extern "C" JNIEXPORT jobject JNICALL Java_Main_testGetMirandaMethodNative(JNIEnv extern "C" void JNICALL Java_Main_testZeroLengthByteBuffers(JNIEnv* env, jclass) { std::vector<uint8_t> buffer(1); jobject byte_buffer = env->NewDirectByteBuffer(&buffer[0], 0); - assert(byte_buffer != NULL); + assert(byte_buffer != nullptr); assert(!env->ExceptionCheck()); assert(env->GetDirectBufferAddress(byte_buffer) == &buffer[0]); @@ -202,8 +173,8 @@ constexpr size_t kByteReturnSize = 7; jbyte byte_returns[kByteReturnSize] = { 0, 1, 2, 127, -1, -2, -128 }; extern "C" jbyte JNICALL Java_Main_byteMethod(JNIEnv* env, jclass klass, jbyte b1, jbyte b2, - jbyte b3, jbyte b4, jbyte b5, jbyte b6, - jbyte b7, jbyte b8, jbyte b9, jbyte b10) { + jbyte b3, jbyte b4, jbyte b5, jbyte b6, + jbyte b7, jbyte b8, jbyte b9, jbyte b10) { // We use b1 to drive the output. assert(b2 == 2); assert(b3 == -3); @@ -227,8 +198,8 @@ jshort short_returns[kShortReturnSize] = { 0, 1, 2, 127, 32767, -1, -2, -128, // The weird static_cast is because short int is only guaranteed down to -32767, not Java's -32768. extern "C" jshort JNICALL Java_Main_shortMethod(JNIEnv* env, jclass klass, jshort s1, jshort s2, - jshort s3, jshort s4, jshort s5, jshort s6, - jshort s7, jshort s8, jshort s9, jshort s10) { + jshort s3, jshort s4, jshort s5, jshort s6, + jshort s7, jshort s8, jshort s9, jshort s10) { // We use s1 to drive the output. assert(s2 == 2); assert(s3 == -3); @@ -247,9 +218,9 @@ extern "C" jshort JNICALL Java_Main_shortMethod(JNIEnv* env, jclass klass, jshor } extern "C" jboolean JNICALL Java_Main_booleanMethod(JNIEnv* env, jclass klass, jboolean b1, - jboolean b2, jboolean b3, jboolean b4, - jboolean b5, jboolean b6, jboolean b7, - jboolean b8, jboolean b9, jboolean b10) { + jboolean b2, jboolean b3, jboolean b4, + jboolean b5, jboolean b6, jboolean b7, + jboolean b8, jboolean b9, jboolean b10) { // We use b1 to drive the output. assert(b2 == JNI_TRUE); assert(b3 == JNI_FALSE); @@ -269,8 +240,8 @@ constexpr size_t kCharReturnSize = 8; jchar char_returns[kCharReturnSize] = { 0, 1, 2, 127, 255, 256, 15000, 34000 }; extern "C" jchar JNICALL Java_Main_charMethod(JNIEnv* env, jclass klacc, jchar c1, jchar c2, - jchar c3, jchar c4, jchar c5, jchar c6, - jchar c7, jchar c8, jchar c9, jchar c10) { + jchar c3, jchar c4, jchar c5, jchar c6, jchar c7, + jchar c8, jchar c9, jchar c10) { // We use c1 to drive the output. assert(c2 == 'a'); assert(c3 == 'b'); @@ -291,3 +262,57 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_nativeIsAssignableFrom(JNIEnv* e jclass from, jclass to) { return env->IsAssignableFrom(from, to); } + +static void testShallowGetCallingClassLoader(JNIEnv* env) { + // Test direct call. + { + jclass vmstack_clazz = env->FindClass("dalvik/system/VMStack"); + assert(vmstack_clazz != nullptr); + assert(!env->ExceptionCheck()); + + jmethodID getCallingClassLoaderMethodId = env->GetStaticMethodID(vmstack_clazz, + "getCallingClassLoader", + "()Ljava/lang/ClassLoader;"); + assert(getCallingClassLoaderMethodId != nullptr); + assert(!env->ExceptionCheck()); + + jobject class_loader = env->CallStaticObjectMethod(vmstack_clazz, + getCallingClassLoaderMethodId); + assert(class_loader == nullptr); + assert(!env->ExceptionCheck()); + } + + // Test one-level call. Use System.loadLibrary(). + { + jclass system_clazz = env->FindClass("java/lang/System"); + assert(system_clazz != nullptr); + assert(!env->ExceptionCheck()); + + jmethodID loadLibraryMethodId = env->GetStaticMethodID(system_clazz, "loadLibrary", + "(Ljava/lang/String;)V"); + assert(loadLibraryMethodId != nullptr); + assert(!env->ExceptionCheck()); + + // Create a string object. + jobject library_string = env->NewStringUTF("arttest"); + assert(library_string != nullptr); + assert(!env->ExceptionCheck()); + + env->CallStaticVoidMethod(system_clazz, loadLibraryMethodId, library_string); + if (env->ExceptionCheck()) { + // At most we expect UnsatisfiedLinkError. + jthrowable thrown = env->ExceptionOccurred(); + env->ExceptionClear(); + + jclass unsatisfied_link_error_clazz = env->FindClass("java/lang/UnsatisfiedLinkError"); + jclass thrown_class = env->GetObjectClass(thrown); + assert(env->IsSameObject(unsatisfied_link_error_clazz, thrown_class)); + } + } +} + +// http://b/16867274 +extern "C" JNIEXPORT void JNICALL Java_Main_nativeTestShallowGetCallingClassLoader(JNIEnv* env, + jclass) { + PthreadHelper(&testShallowGetCallingClassLoader); +} diff --git a/test/004-JniTest/src/Main.java b/test/004-JniTest/src/Main.java index ae133becbc..6d7d647d07 100644 --- a/test/004-JniTest/src/Main.java +++ b/test/004-JniTest/src/Main.java @@ -30,6 +30,7 @@ public class Main { testBooleanMethod(); testCharMethod(); testIsAssignableFromOnPrimitiveTypes(); + testShallowGetCallingClassLoader(); } private static native void testFindClassOnAttachedNativeThread(); @@ -167,4 +168,10 @@ public class Main { } native static boolean nativeIsAssignableFrom(Class<?> from, Class<?> to); + + static void testShallowGetCallingClassLoader() { + nativeTestShallowGetCallingClassLoader(); + } + + native static void nativeTestShallowGetCallingClassLoader(); } |