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
JNI_OnLoad(JavaVM * vm,void *)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
AttachHelper(void * arg)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
PthreadHelper(void (* fn)(JNIEnv *))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
testFindClassOnAttachedNativeThread(JNIEnv * env)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
Java_Main_testFindClassOnAttachedNativeThread(JNIEnv *,jclass)74 extern "C" JNIEXPORT void JNICALL Java_Main_testFindClassOnAttachedNativeThread(JNIEnv*, jclass) {
75 PthreadHelper(&testFindClassOnAttachedNativeThread);
76 }
77
testFindFieldOnAttachedNativeThread(JNIEnv * env)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
Java_Main_testFindFieldOnAttachedNativeThreadNative(JNIEnv *,jclass)90 extern "C" JNIEXPORT void JNICALL Java_Main_testFindFieldOnAttachedNativeThreadNative(JNIEnv*,
91 jclass) {
92 PthreadHelper(&testFindFieldOnAttachedNativeThread);
93 }
94
testReflectFieldGetFromAttachedNativeThread(JNIEnv * env)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
Java_Main_testReflectFieldGetFromAttachedNativeThreadNative(JNIEnv *,jclass)132 extern "C" JNIEXPORT void JNICALL Java_Main_testReflectFieldGetFromAttachedNativeThreadNative(
133 JNIEnv*, jclass) {
134 PthreadHelper(&testReflectFieldGetFromAttachedNativeThread);
135 }
136
137
138 // http://b/11243757
Java_Main_testCallStaticVoidMethodOnSubClassNative(JNIEnv * env,jclass)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
Java_Main_testGetMirandaMethodNative(JNIEnv * env,jclass)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
Java_Main_testZeroLengthByteBuffers(JNIEnv * env,jclass)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
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)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
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)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
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)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
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)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
Java_Main_nativeIsAssignableFrom(JNIEnv * env,jclass,jclass from,jclass to)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
testShallowGetCallingClassLoader(JNIEnv * env)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
Java_Main_nativeTestShallowGetCallingClassLoader(JNIEnv * env,jclass)315 extern "C" JNIEXPORT void JNICALL Java_Main_nativeTestShallowGetCallingClassLoader(JNIEnv* env,
316 jclass) {
317 PthreadHelper(&testShallowGetCallingClassLoader);
318 }
319
testShallowGetStackClass2(JNIEnv * env)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
Java_Main_nativeTestShallowGetStackClass2(JNIEnv * env,jclass)353 extern "C" JNIEXPORT void JNICALL Java_Main_nativeTestShallowGetStackClass2(JNIEnv* env, jclass) {
354 PthreadHelper(&testShallowGetStackClass2);
355 }
356