OSDN Git Service

ART: Tolerate shallow call stack in VMStack_getCallingClassLoader
authorAndreas Gampe <agampe@google.com>
Tue, 12 Aug 2014 01:51:53 +0000 (18:51 -0700)
committerAndreas Gampe <agampe@google.com>
Wed, 13 Aug 2014 22:59:04 +0000 (15:59 -0700)
When the call stack does not have the three methods we expect,
the visitor will return a nullptr value.

Add a test to JniTest (and refactor the test a little for code reuse).

Bug: 16867274
Change-Id: I5fb8f91f372a41e0bc11ef9f70640834591afa53

runtime/native/dalvik_system_VMStack.cc
test/004-JniTest/jni_test.cc
test/004-JniTest/src/Main.java

index 5f718ba..047e9f6 100644 (file)
@@ -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());
 }
 
index 554712a..9a2fbdf 100644 (file)
 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);
+}
index ae133be..6d7d647 100644 (file)
@@ -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();
 }