Home | History | Annotate | Download | only in 004-JniTest
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <assert.h>
     18 #include <stdio.h>
     19 #include <pthread.h>
     20 #include <vector>
     21 
     22 #include "jni.h"
     23 
     24 #if defined(NDEBUG)
     25 #error test code compiled without NDEBUG
     26 #endif
     27 
     28 static JavaVM* jvm = NULL;
     29 
     30 extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *) {
     31   assert(vm != nullptr);
     32   assert(jvm == nullptr);
     33   jvm = vm;
     34   return JNI_VERSION_1_6;
     35 }
     36 
     37 static void* AttachHelper(void* arg) {
     38   assert(jvm != nullptr);
     39 
     40   JNIEnv* env = nullptr;
     41   JavaVMAttachArgs args = { JNI_VERSION_1_6, __FUNCTION__, NULL };
     42   int attach_result = jvm->AttachCurrentThread(&env, &args);
     43   assert(attach_result == 0);
     44 
     45   typedef void (*Fn)(JNIEnv*);
     46   Fn fn = reinterpret_cast<Fn>(arg);
     47   fn(env);
     48 
     49   int detach_result = jvm->DetachCurrentThread();
     50   assert(detach_result == 0);
     51   return nullptr;
     52 }
     53 
     54 static void PthreadHelper(void (*fn)(JNIEnv*)) {
     55   pthread_t pthread;
     56   int pthread_create_result = pthread_create(&pthread, nullptr, AttachHelper,
     57                                              reinterpret_cast<void*>(fn));
     58   assert(pthread_create_result == 0);
     59   int pthread_join_result = pthread_join(pthread, nullptr);
     60   assert(pthread_join_result == 0);
     61 }
     62 
     63 static void testFindClassOnAttachedNativeThread(JNIEnv* env) {
     64   jclass clazz = env->FindClass("Main");
     65   assert(clazz != nullptr);
     66   assert(!env->ExceptionCheck());
     67 
     68   jobjectArray array = env->NewObjectArray(0, clazz, nullptr);
     69   assert(array != nullptr);
     70   assert(!env->ExceptionCheck());
     71 }
     72 
     73 // http://b/10994325
     74 extern "C" JNIEXPORT void JNICALL Java_Main_testFindClassOnAttachedNativeThread(JNIEnv*, jclass) {
     75   PthreadHelper(&testFindClassOnAttachedNativeThread);
     76 }
     77 
     78 static void testFindFieldOnAttachedNativeThread(JNIEnv* env) {
     79   jclass clazz = env->FindClass("Main");
     80   assert(clazz != nullptr);
     81   assert(!env->ExceptionCheck());
     82 
     83   jfieldID field = env->GetStaticFieldID(clazz, "testFindFieldOnAttachedNativeThreadField", "Z");
     84   assert(field != nullptr);
     85   assert(!env->ExceptionCheck());
     86 
     87   env->SetStaticBooleanField(clazz, field, JNI_TRUE);
     88 }
     89 
     90 extern "C" JNIEXPORT void JNICALL Java_Main_testFindFieldOnAttachedNativeThreadNative(JNIEnv*,
     91                                                                                       jclass) {
     92   PthreadHelper(&testFindFieldOnAttachedNativeThread);
     93 }
     94 
     95 static void testReflectFieldGetFromAttachedNativeThread(JNIEnv* env) {
     96   jclass clazz = env->FindClass("Main");
     97   assert(clazz != nullptr);
     98   assert(!env->ExceptionCheck());
     99 
    100   jclass class_clazz = env->FindClass("java/lang/Class");
    101   assert(class_clazz != nullptr);
    102   assert(!env->ExceptionCheck());
    103 
    104   jmethodID getFieldMetodId = env->GetMethodID(class_clazz, "getField",
    105                                                "(Ljava/lang/String;)Ljava/lang/reflect/Field;");
    106   assert(getFieldMetodId != nullptr);
    107   assert(!env->ExceptionCheck());
    108 
    109   jstring field_name = env->NewStringUTF("testReflectFieldGetFromAttachedNativeThreadField");
    110   assert(field_name != nullptr);
    111   assert(!env->ExceptionCheck());
    112 
    113   jobject field = env->CallObjectMethod(clazz, getFieldMetodId, field_name);
    114   assert(field != nullptr);
    115   assert(!env->ExceptionCheck());
    116 
    117   jclass field_clazz = env->FindClass("java/lang/reflect/Field");
    118   assert(field_clazz != nullptr);
    119   assert(!env->ExceptionCheck());
    120 
    121   jmethodID getBooleanMetodId = env->GetMethodID(field_clazz, "getBoolean",
    122                                                  "(Ljava/lang/Object;)Z");
    123   assert(getBooleanMetodId != nullptr);
    124   assert(!env->ExceptionCheck());
    125 
    126   jboolean value = env->CallBooleanMethod(field, getBooleanMetodId, /* ignored */ clazz);
    127   assert(value == false);
    128   assert(!env->ExceptionCheck());
    129 }
    130 
    131 // http://b/15539150
    132 extern "C" JNIEXPORT void JNICALL Java_Main_testReflectFieldGetFromAttachedNativeThreadNative(
    133     JNIEnv*, jclass) {
    134   PthreadHelper(&testReflectFieldGetFromAttachedNativeThread);
    135 }
    136 
    137 
    138 // http://b/11243757
    139 extern "C" JNIEXPORT void JNICALL Java_Main_testCallStaticVoidMethodOnSubClassNative(JNIEnv* env,
    140                                                                                      jclass) {
    141   jclass super_class = env->FindClass("Main$testCallStaticVoidMethodOnSubClass_SuperClass");
    142   assert(super_class != nullptr);
    143 
    144   jmethodID execute = env->GetStaticMethodID(super_class, "execute", "()V");
    145   assert(execute != nullptr);
    146 
    147   jclass sub_class = env->FindClass("Main$testCallStaticVoidMethodOnSubClass_SubClass");
    148   assert(sub_class != nullptr);
    149 
    150   env->CallStaticVoidMethod(sub_class, execute);
    151 }
    152 
    153 extern "C" JNIEXPORT jobject JNICALL Java_Main_testGetMirandaMethodNative(JNIEnv* env, jclass) {
    154   jclass abstract_class = env->FindClass("Main$testGetMirandaMethod_MirandaAbstract");
    155   assert(abstract_class != nullptr);
    156   jmethodID miranda_method = env->GetMethodID(abstract_class, "inInterface", "()Z");
    157   assert(miranda_method != nullptr);
    158   return env->ToReflectedMethod(abstract_class, miranda_method, JNI_FALSE);
    159 }
    160 
    161 // https://code.google.com/p/android/issues/detail?id=63055
    162 extern "C" void JNICALL Java_Main_testZeroLengthByteBuffers(JNIEnv* env, jclass) {
    163   std::vector<uint8_t> buffer(1);
    164   jobject byte_buffer = env->NewDirectByteBuffer(&buffer[0], 0);
    165   assert(byte_buffer != nullptr);
    166   assert(!env->ExceptionCheck());
    167 
    168   assert(env->GetDirectBufferAddress(byte_buffer) == &buffer[0]);
    169   assert(env->GetDirectBufferCapacity(byte_buffer) == 0);
    170 }
    171 
    172 constexpr size_t kByteReturnSize = 7;
    173 jbyte byte_returns[kByteReturnSize] = { 0, 1, 2, 127, -1, -2, -128 };
    174 
    175 extern "C" jbyte JNICALL Java_Main_byteMethod(JNIEnv* env, jclass klass, jbyte b1, jbyte b2,
    176                                               jbyte b3, jbyte b4, jbyte b5, jbyte b6,
    177                                               jbyte b7, jbyte b8, jbyte b9, jbyte b10) {
    178   // We use b1 to drive the output.
    179   assert(b2 == 2);
    180   assert(b3 == -3);
    181   assert(b4 == 4);
    182   assert(b5 == -5);
    183   assert(b6 == 6);
    184   assert(b7 == -7);
    185   assert(b8 == 8);
    186   assert(b9 == -9);
    187   assert(b10 == 10);
    188 
    189   assert(0 <= b1);
    190   assert(b1 < static_cast<jbyte>(kByteReturnSize));
    191 
    192   return byte_returns[b1];
    193 }
    194 
    195 constexpr size_t kShortReturnSize = 9;
    196 jshort short_returns[kShortReturnSize] = { 0, 1, 2, 127, 32767, -1, -2, -128,
    197     static_cast<jshort>(0x8000) };
    198 // The weird static_cast is because short int is only guaranteed down to -32767, not Java's -32768.
    199 
    200 extern "C" jshort JNICALL Java_Main_shortMethod(JNIEnv* env, jclass klass, jshort s1, jshort s2,
    201                                                 jshort s3, jshort s4, jshort s5, jshort s6,
    202                                                 jshort s7, jshort s8, jshort s9, jshort s10) {
    203   // We use s1 to drive the output.
    204   assert(s2 == 2);
    205   assert(s3 == -3);
    206   assert(s4 == 4);
    207   assert(s5 == -5);
    208   assert(s6 == 6);
    209   assert(s7 == -7);
    210   assert(s8 == 8);
    211   assert(s9 == -9);
    212   assert(s10 == 10);
    213 
    214   assert(0 <= s1);
    215   assert(s1 < static_cast<jshort>(kShortReturnSize));
    216 
    217   return short_returns[s1];
    218 }
    219 
    220 extern "C" jboolean JNICALL Java_Main_booleanMethod(JNIEnv* env, jclass klass, jboolean b1,
    221                                                     jboolean b2, jboolean b3, jboolean b4,
    222                                                     jboolean b5, jboolean b6, jboolean b7,
    223                                                     jboolean b8, jboolean b9, jboolean b10) {
    224   // We use b1 to drive the output.
    225   assert(b2 == JNI_TRUE);
    226   assert(b3 == JNI_FALSE);
    227   assert(b4 == JNI_TRUE);
    228   assert(b5 == JNI_FALSE);
    229   assert(b6 == JNI_TRUE);
    230   assert(b7 == JNI_FALSE);
    231   assert(b8 == JNI_TRUE);
    232   assert(b9 == JNI_FALSE);
    233   assert(b10 == JNI_TRUE);
    234 
    235   assert(b1 == JNI_TRUE || b1 == JNI_FALSE);
    236   return b1;
    237 }
    238 
    239 constexpr size_t kCharReturnSize = 8;
    240 jchar char_returns[kCharReturnSize] = { 0, 1, 2, 127, 255, 256, 15000, 34000 };
    241 
    242 extern "C" jchar JNICALL Java_Main_charMethod(JNIEnv* env, jclass klacc, jchar c1, jchar c2,
    243                                               jchar c3, jchar c4, jchar c5, jchar c6, jchar c7,
    244                                               jchar c8, jchar c9, jchar c10) {
    245   // We use c1 to drive the output.
    246   assert(c2 == 'a');
    247   assert(c3 == 'b');
    248   assert(c4 == 'c');
    249   assert(c5 == '0');
    250   assert(c6 == '1');
    251   assert(c7 == '2');
    252   assert(c8 == 1234);
    253   assert(c9 == 2345);
    254   assert(c10 == 3456);
    255 
    256   assert(c1 < static_cast<jchar>(kCharReturnSize));
    257 
    258   return char_returns[c1];
    259 }
    260 
    261 extern "C" JNIEXPORT jboolean JNICALL Java_Main_nativeIsAssignableFrom(JNIEnv* env, jclass,
    262                                                                        jclass from, jclass to) {
    263   return env->IsAssignableFrom(from, to);
    264 }
    265 
    266 static void testShallowGetCallingClassLoader(JNIEnv* env) {
    267   // Test direct call.
    268   {
    269     jclass vmstack_clazz = env->FindClass("dalvik/system/VMStack");
    270     assert(vmstack_clazz != nullptr);
    271     assert(!env->ExceptionCheck());
    272 
    273     jmethodID getCallingClassLoaderMethodId = env->GetStaticMethodID(vmstack_clazz,
    274                                                                      "getCallingClassLoader",
    275                                                                      "()Ljava/lang/ClassLoader;");
    276     assert(getCallingClassLoaderMethodId != nullptr);
    277     assert(!env->ExceptionCheck());
    278 
    279     jobject class_loader = env->CallStaticObjectMethod(vmstack_clazz,
    280                                                        getCallingClassLoaderMethodId);
    281     assert(class_loader == nullptr);
    282     assert(!env->ExceptionCheck());
    283   }
    284 
    285   // Test one-level call. Use System.loadLibrary().
    286   {
    287     jclass system_clazz = env->FindClass("java/lang/System");
    288     assert(system_clazz != nullptr);
    289     assert(!env->ExceptionCheck());
    290 
    291     jmethodID loadLibraryMethodId = env->GetStaticMethodID(system_clazz, "loadLibrary",
    292                                                            "(Ljava/lang/String;)V");
    293     assert(loadLibraryMethodId != nullptr);
    294     assert(!env->ExceptionCheck());
    295 
    296     // Create a string object.
    297     jobject library_string = env->NewStringUTF("arttest");
    298     assert(library_string != nullptr);
    299     assert(!env->ExceptionCheck());
    300 
    301     env->CallStaticVoidMethod(system_clazz, loadLibraryMethodId, library_string);
    302     if (env->ExceptionCheck()) {
    303       // At most we expect UnsatisfiedLinkError.
    304       jthrowable thrown = env->ExceptionOccurred();
    305       env->ExceptionClear();
    306 
    307       jclass unsatisfied_link_error_clazz = env->FindClass("java/lang/UnsatisfiedLinkError");
    308       jclass thrown_class = env->GetObjectClass(thrown);
    309       assert(env->IsSameObject(unsatisfied_link_error_clazz, thrown_class));
    310     }
    311   }
    312 }
    313 
    314 // http://b/16867274
    315 extern "C" JNIEXPORT void JNICALL Java_Main_nativeTestShallowGetCallingClassLoader(JNIEnv* env,
    316                                                                                    jclass) {
    317   PthreadHelper(&testShallowGetCallingClassLoader);
    318 }
    319 
    320 static void testShallowGetStackClass2(JNIEnv* env) {
    321   jclass vmstack_clazz = env->FindClass("dalvik/system/VMStack");
    322   assert(vmstack_clazz != nullptr);
    323   assert(!env->ExceptionCheck());
    324 
    325   // Test direct call.
    326   {
    327     jmethodID getStackClass2MethodId = env->GetStaticMethodID(vmstack_clazz, "getStackClass2",
    328                                                               "()Ljava/lang/Class;");
    329     assert(getStackClass2MethodId != nullptr);
    330     assert(!env->ExceptionCheck());
    331 
    332     jobject caller_class = env->CallStaticObjectMethod(vmstack_clazz, getStackClass2MethodId);
    333     assert(caller_class == nullptr);
    334     assert(!env->ExceptionCheck());
    335   }
    336 
    337   // Test one-level call. Use VMStack.getStackClass1().
    338   {
    339     jmethodID getStackClass1MethodId = env->GetStaticMethodID(vmstack_clazz, "getStackClass1",
    340                                                               "()Ljava/lang/Class;");
    341     assert(getStackClass1MethodId != nullptr);
    342     assert(!env->ExceptionCheck());
    343 
    344     jobject caller_class = env->CallStaticObjectMethod(vmstack_clazz, getStackClass1MethodId);
    345     assert(caller_class == nullptr);
    346     assert(!env->ExceptionCheck());
    347   }
    348 
    349   // For better testing we would need to compile against libcore and have a two-deep stack
    350   // ourselves.
    351 }
    352 
    353 extern "C" JNIEXPORT void JNICALL Java_Main_nativeTestShallowGetStackClass2(JNIEnv* env, jclass) {
    354   PthreadHelper(&testShallowGetStackClass2);
    355 }
    356