blob: 23615ed07ac8d10eedc9f78f328cdecedae98be1 [file] [log] [blame]
Ian Rogersb033c752011-07-20 12:22:35 -07001// Copyright 2011 Google Inc. All Rights Reserved.
2// Author: irogers@google.com (Ian Rogers)
3
4#include <sys/mman.h>
5#include "src/assembler.h"
6#include "src/class_linker.h"
7#include "src/common_test.h"
8#include "src/dex_file.h"
9#include "src/jni_compiler.h"
10#include "src/runtime.h"
11#include "src/thread.h"
12#include "gtest/gtest.h"
13
14namespace art {
15
16class JniCompilerTest : public testing::Test {
17 protected:
18 virtual void SetUp() {
19 // Create runtime and attach thread
20 runtime_ = Runtime::Create();
21 CHECK(runtime_->AttachCurrentThread());
22 // Create thunk code that performs the native to managed transition
23 thunk_code_size_ = 4096;
24 thunk_ = mmap(NULL, thunk_code_size_, PROT_READ | PROT_WRITE | PROT_EXEC,
25 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
26 CHECK_NE(MAP_FAILED, thunk_);
27 Assembler thk_asm;
28 // TODO: shouldn't have machine specific code in a general purpose file
29#if defined(__i386__)
30 thk_asm.pushl(EDI); // preserve EDI
31 thk_asm.movl(EAX, Address(ESP, 8)); // EAX = method->GetCode()
32 thk_asm.movl(EDI, Address(ESP, 12)); // EDI = method
33 thk_asm.pushl(Immediate(0)); // push pad
34 thk_asm.pushl(Immediate(0)); // push pad
35 thk_asm.pushl(Address(ESP, 40)); // push pad or jlong high
36 thk_asm.pushl(Address(ESP, 40)); // push jint or jlong low
37 thk_asm.pushl(Address(ESP, 40)); // push jint or jlong high
38 thk_asm.pushl(Address(ESP, 40)); // push jint or jlong low
39 thk_asm.pushl(Address(ESP, 40)); // push jobject
40 thk_asm.call(EAX); // Continue in method->GetCode()
41 thk_asm.addl(ESP, Immediate(28)); // pop arguments
42 thk_asm.popl(EDI); // restore EDI
43 thk_asm.ret();
44#else
45 LOG(FATAL) << "Unimplemented";
46#endif
47 size_t cs = thk_asm.CodeSize();
48 MemoryRegion code(thunk_, cs);
49 thk_asm.FinalizeInstructions(code);
50 thunk_entry1_ = reinterpret_cast<jint (*)(const void*, art::Method*,
51 jobject, jint, jint, jint)
52 >(code.pointer());
53 thunk_entry2_ = reinterpret_cast<jdouble (*)(const void*, art::Method*,
54 jobject, jdouble, jdouble)
55 >(code.pointer());
56 }
57
58 virtual void TearDown() {
59 // Release thunk code
60 CHECK(runtime_->DetachCurrentThread());
61 CHECK_EQ(0, munmap(thunk_, thunk_code_size_));
62 }
63
64 // Run generated code associated with method passing and returning int size
65 // arguments
66 jvalue RunMethod(Method* method, jvalue a, jvalue b, jvalue c, jvalue d) {
67 jvalue result;
68 // sanity checks
69 EXPECT_NE(static_cast<void*>(NULL), method->GetCode());
70 EXPECT_EQ(0u, Thread::Current()->NumShbHandles());
71 EXPECT_EQ(Thread::kRunnable, Thread::Current()->GetState());
72 // perform call
73 result.i = (*thunk_entry1_)(method->GetCode(), method, a.l, b.i, c.i, d.i);
74 // sanity check post-call
75 EXPECT_EQ(0u, Thread::Current()->NumShbHandles());
76 EXPECT_EQ(Thread::kRunnable, Thread::Current()->GetState());
77 return result;
78 }
79
80 // Run generated code associated with method passing and returning double size
81 // arguments
82 jvalue RunMethodD(Method* method, jvalue a, jvalue b, jvalue c) {
83 jvalue result;
84 // sanity checks
85 EXPECT_NE(static_cast<void*>(NULL), method->GetCode());
86 EXPECT_EQ(0u, Thread::Current()->NumShbHandles());
87 EXPECT_EQ(Thread::kRunnable, Thread::Current()->GetState());
88 // perform call
89 result.d = (*thunk_entry2_)(method->GetCode(), method, a.l, b.d, c.d);
90 // sanity check post-call
91 EXPECT_EQ(0u, Thread::Current()->NumShbHandles());
92 EXPECT_EQ(Thread::kRunnable, Thread::Current()->GetState());
93 return result;
94 }
95
96 Runtime* runtime_;
97 void* thunk_;
98 size_t thunk_code_size_;
99 jint (*thunk_entry1_)(const void*, Method*, jobject, jint, jint, jint);
100 jdouble (*thunk_entry2_)(const void*, Method*, jobject, jdouble, jdouble);
101};
102
103int gJava_MyClass_foo_calls = 0;
104void Java_MyClass_foo(JNIEnv*, jobject) {
105 EXPECT_EQ(1u, Thread::Current()->NumShbHandles());
106 EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
107 gJava_MyClass_foo_calls++;
108}
109
110int gJava_MyClass_fooI_calls = 0;
111jint Java_MyClass_fooI(JNIEnv*, jobject, jint x) {
112 EXPECT_EQ(1u, Thread::Current()->NumShbHandles());
113 EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
114 gJava_MyClass_fooI_calls++;
115 return x;
116}
117
118int gJava_MyClass_fooII_calls = 0;
119jint Java_MyClass_fooII(JNIEnv*, jobject, jint x, jint y) {
120 EXPECT_EQ(1u, Thread::Current()->NumShbHandles());
121 EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
122 gJava_MyClass_fooII_calls++;
123 return x - y; // non-commutative operator
124}
125
126int gJava_MyClass_fooDD_calls = 0;
127jdouble Java_MyClass_fooDD(JNIEnv*, jobject, jdouble x, jdouble y) {
128 EXPECT_EQ(1u, Thread::Current()->NumShbHandles());
129 EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
130 gJava_MyClass_fooDD_calls++;
131 return x - y; // non-commutative operator
132}
133
134int gJava_MyClass_fooIOO_calls = 0;
135jobject Java_MyClass_fooIOO(JNIEnv*, jobject thisObject, jint x, jobject y,
136 jobject z) {
137 EXPECT_EQ(3u, Thread::Current()->NumShbHandles());
138 EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
139 gJava_MyClass_fooIOO_calls++;
140 switch (x) {
141 case 1:
142 return y;
143 case 2:
144 return z;
145 default:
146 return thisObject;
147 }
148}
149
150int gJava_MyClass_fooSIOO_calls = 0;
151jobject Java_MyClass_fooSIOO(JNIEnv*, jclass klass, jint x, jobject y,
152 jobject z) {
153 EXPECT_EQ(3u, Thread::Current()->NumShbHandles());
154 EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
155 gJava_MyClass_fooSIOO_calls++;
156 switch (x) {
157 case 1:
158 return y;
159 case 2:
160 return z;
161 default:
162 return klass;
163 }
164}
165
Ian Rogersdf20fe02011-07-20 20:34:16 -0700166int gJava_MyClass_fooSSIOO_calls = 0;
167jobject Java_MyClass_fooSSIOO(JNIEnv*, jclass klass, jint x, jobject y,
168 jobject z) {
169 EXPECT_EQ(3u, Thread::Current()->NumShbHandles());
170 EXPECT_EQ(Thread::kNative, Thread::Current()->GetState());
171 gJava_MyClass_fooSSIOO_calls++;
172 switch (x) {
173 case 1:
174 return y;
175 case 2:
176 return z;
177 default:
178 return klass;
179 }
180}
181
Ian Rogersb033c752011-07-20 12:22:35 -0700182TEST_F(JniCompilerTest, CompileAndRunNoArgMethod) {
183 scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
184 scoped_ptr<ClassLinker> linker(ClassLinker::Create());
185 linker->AppendToClassPath(dex.get());
186 Class* klass = linker->FindClass("LMyClass;", NULL);
187 Method* method = klass->FindVirtualMethod("foo");
188
189 Assembler jni_asm;
190 JniCompiler jni_compiler;
191 jni_compiler.Compile(&jni_asm, method);
192
193 // TODO: should really use JNIEnv to RegisterNative, but missing a
194 // complete story on this, so hack the RegisterNative below
195 // JNIEnv* env = Thread::Current()->GetJniEnv();
196 // JNINativeMethod methods[] = {{"foo", "()V", (void*)&Java_MyClass_foo}};
197 method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_foo));
198
199 jvalue a;
200 a.l = (jobject)NULL;
201 EXPECT_EQ(0, gJava_MyClass_foo_calls);
202 RunMethod(method, a, a, a, a);
203 EXPECT_EQ(1, gJava_MyClass_foo_calls);
204 RunMethod(method, a, a, a, a);
205 EXPECT_EQ(2, gJava_MyClass_foo_calls);
206}
207
208TEST_F(JniCompilerTest, CompileAndRunIntMethod) {
209 scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
210 scoped_ptr<ClassLinker> linker(ClassLinker::Create());
211 linker->AppendToClassPath(dex.get());
212 Class* klass = linker->FindClass("LMyClass;", NULL);
213 Method* method = klass->FindVirtualMethod("fooI");
214
215 Assembler jni_asm;
216 JniCompiler jni_compiler;
217 jni_compiler.Compile(&jni_asm, method);
218
219 // TODO: should really use JNIEnv to RegisterNative, but missing a
220 // complete story on this, so hack the RegisterNative below
221 method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooI));
222
223 jvalue a, b, c;
224 a.l = (jobject)NULL;
225 b.i = 42;
226 EXPECT_EQ(0, gJava_MyClass_fooI_calls);
227 c = RunMethod(method, a, b, a, a);
228 ASSERT_EQ(42, c.i);
229 EXPECT_EQ(1, gJava_MyClass_fooI_calls);
230 b.i = 0xCAFED00D;
231 c = RunMethod(method, a, b, a, a);
232 ASSERT_EQ((jint)0xCAFED00D, c.i);
233 EXPECT_EQ(2, gJava_MyClass_fooI_calls);
234}
235
236TEST_F(JniCompilerTest, CompileAndRunIntIntMethod) {
237 scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
238 scoped_ptr<ClassLinker> linker(ClassLinker::Create());
239 linker->AppendToClassPath(dex.get());
240 Class* klass = linker->FindClass("LMyClass;", NULL);
241 Method* method = klass->FindVirtualMethod("fooII");
242
243 Assembler jni_asm;
244 JniCompiler jni_compiler;
245 jni_compiler.Compile(&jni_asm, method);
246
247 // TODO: should really use JNIEnv to RegisterNative, but missing a
248 // complete story on this, so hack the RegisterNative below
249 method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooII));
250
251 jvalue a, b, c, d;
252 a.l = (jobject)NULL;
253 b.i = 99;
254 c.i = 10;
255 EXPECT_EQ(0, gJava_MyClass_fooII_calls);
256 d = RunMethod(method, a, b, c, a);
257 ASSERT_EQ(99 - 10, d.i);
258 EXPECT_EQ(1, gJava_MyClass_fooII_calls);
259 b.i = 0xCAFEBABE;
260 c.i = 0xCAFED00D;
261 d = RunMethod(method, a, b, c, a);
262 ASSERT_EQ((jint)(0xCAFEBABE - 0xCAFED00D), d.i);
263 EXPECT_EQ(2, gJava_MyClass_fooII_calls);
264}
265
266
267TEST_F(JniCompilerTest, CompileAndRunDoubleDoubleMethod) {
268 scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
269 scoped_ptr<ClassLinker> linker(ClassLinker::Create());
270 linker->AppendToClassPath(dex.get());
271 Class* klass = linker->FindClass("LMyClass;", NULL);
272 Method* method = klass->FindVirtualMethod("fooDD");
273
274 Assembler jni_asm;
275 JniCompiler jni_compiler;
276 jni_compiler.Compile(&jni_asm, method);
277
278 // TODO: should really use JNIEnv to RegisterNative, but missing a
279 // complete story on this, so hack the RegisterNative below
280 method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooDD));
281
282 jvalue a, b, c, d;
283 a.l = (jobject)NULL;
284 b.d = 99;
285 c.d = 10;
286 EXPECT_EQ(0, gJava_MyClass_fooDD_calls);
287 d = RunMethodD(method, a, b, c);
288 ASSERT_EQ(b.d - c.d, d.d);
289 EXPECT_EQ(1, gJava_MyClass_fooDD_calls);
290 b.d = 3.14159265358979323846;
291 c.d = 0.69314718055994530942;
292 d = RunMethodD(method, a, b, c);
293 ASSERT_EQ(b.d - c.d, d.d);
294 EXPECT_EQ(2, gJava_MyClass_fooDD_calls);
295}
296
297TEST_F(JniCompilerTest, CompileAndRunIntObjectObjectMethod) {
298 scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
299 scoped_ptr<ClassLinker> linker(ClassLinker::Create());
300 linker->AppendToClassPath(dex.get());
301 Class* klass = linker->FindClass("LMyClass;", NULL);
302 Method* method = klass->FindVirtualMethod("fooIOO");
303
304 Assembler jni_asm;
305 JniCompiler jni_compiler;
306 jni_compiler.Compile(&jni_asm, method);
307
308 // TODO: should really use JNIEnv to RegisterNative, but missing a
309 // complete story on this, so hack the RegisterNative below
310 method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooIOO));
311
312 jvalue a, b, c, d, e;
313 a.l = (jobject)NULL;
314 b.i = 0;
315 c.l = (jobject)NULL;
316 d.l = (jobject)NULL;
317 EXPECT_EQ(0, gJava_MyClass_fooIOO_calls);
318 e = RunMethod(method, a, b, c, d);
319 ASSERT_EQ((jobject)NULL, e.l);
320 EXPECT_EQ(1, gJava_MyClass_fooIOO_calls);
321 a.l = (jobject)8;
322 b.i = 0;
323 c.l = (jobject)NULL;
324 d.l = (jobject)16;
325 e = RunMethod(method, a, b, c, d);
326 ASSERT_EQ((jobject)8, e.l);
327 EXPECT_EQ(2, gJava_MyClass_fooIOO_calls);
328 b.i = 1;
329 e = RunMethod(method, a, b, c, d);
330 ASSERT_EQ((jobject)NULL, e.l);
331 EXPECT_EQ(3, gJava_MyClass_fooIOO_calls);
332 b.i = 2;
333 e = RunMethod(method, a, b, c, d);
334 ASSERT_EQ((jobject)16, e.l);
335 EXPECT_EQ(4, gJava_MyClass_fooIOO_calls);
336 a.l = (jobject)8;
337 b.i = 0;
338 c.l = (jobject)16;
339 d.l = (jobject)NULL;
340 e = RunMethod(method, a, b, c, d);
341 ASSERT_EQ((jobject)8, e.l);
342 EXPECT_EQ(5, gJava_MyClass_fooIOO_calls);
343 b.i = 1;
344 e = RunMethod(method, a, b, c, d);
345 ASSERT_EQ((jobject)16, e.l);
346 EXPECT_EQ(6, gJava_MyClass_fooIOO_calls);
347 b.i = 2;
348 e = RunMethod(method, a, b, c, d);
349 ASSERT_EQ((jobject)NULL, e.l);
350 EXPECT_EQ(7, gJava_MyClass_fooIOO_calls);
351}
352
353TEST_F(JniCompilerTest, CompileAndRunStaticIntObjectObjectMethod) {
354 scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
355 scoped_ptr<ClassLinker> linker(ClassLinker::Create());
356 linker->AppendToClassPath(dex.get());
357 Class* klass = linker->FindClass("LMyClass;", NULL);
358 Method* method = klass->FindDirectMethod("fooSIOO");
359
360 Assembler jni_asm;
361 JniCompiler jni_compiler;
362 jni_compiler.Compile(&jni_asm, method);
363
364 // TODO: should really use JNIEnv to RegisterNative, but missing a
365 // complete story on this, so hack the RegisterNative below
366 method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooSIOO));
367
368 jvalue a, b, c, d;
369 a.i = 0;
370 b.l = (jobject)NULL;
371 c.l = (jobject)NULL;
372 EXPECT_EQ(0, gJava_MyClass_fooSIOO_calls);
373 d = RunMethod(method, a, b, c, a);
374 ASSERT_EQ((jobject)method->GetClass(), d.l);
375 EXPECT_EQ(1, gJava_MyClass_fooSIOO_calls);
376 a.i = 0;
377 b.l = (jobject)NULL;
378 c.l = (jobject)16;
379 d = RunMethod(method, a, b, c, a);
380 ASSERT_EQ((jobject)method->GetClass(), d.l);
381 EXPECT_EQ(2, gJava_MyClass_fooSIOO_calls);
382 a.i = 1;
383 d = RunMethod(method, a, b, c, a);
384 ASSERT_EQ((jobject)NULL, d.l);
385 EXPECT_EQ(3, gJava_MyClass_fooSIOO_calls);
386 a.i = 2;
387 d = RunMethod(method, a, b, c, a);
388 ASSERT_EQ((jobject)16, d.l);
389 EXPECT_EQ(4, gJava_MyClass_fooSIOO_calls);
390 a.i = 0;
391 b.l = (jobject)16;
392 c.l = (jobject)NULL;
393 d = RunMethod(method, a, b, c, a);
394 ASSERT_EQ((jobject)method->GetClass(), d.l);
395 EXPECT_EQ(5, gJava_MyClass_fooSIOO_calls);
396 a.i = 1;
397 d = RunMethod(method, a, b, c, a);
398 ASSERT_EQ((jobject)16, d.l);
399 EXPECT_EQ(6, gJava_MyClass_fooSIOO_calls);
400 a.i = 2;
401 d = RunMethod(method, a, b, c, a);
402 ASSERT_EQ((jobject)NULL, d.l);
403 EXPECT_EQ(7, gJava_MyClass_fooSIOO_calls);
404}
405
Ian Rogersdf20fe02011-07-20 20:34:16 -0700406TEST_F(JniCompilerTest, CompileAndRunStaticSynchronizedIntObjectObjectMethod) {
407 scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
408 scoped_ptr<ClassLinker> linker(ClassLinker::Create());
409 linker->AppendToClassPath(dex.get());
410 Class* klass = linker->FindClass("LMyClass;", NULL);
411 Method* method = klass->FindDirectMethod("fooSSIOO");
412
413 Assembler jni_asm;
414 JniCompiler jni_compiler;
415 jni_compiler.Compile(&jni_asm, method);
416
417 // TODO: should really use JNIEnv to RegisterNative, but missing a
418 // complete story on this, so hack the RegisterNative below
419 method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooSSIOO));
420
421 jvalue a, b, c, d;
422 a.i = 0;
423 b.l = (jobject)NULL;
424 c.l = (jobject)NULL;
425 EXPECT_EQ(0, gJava_MyClass_fooSSIOO_calls);
426 d = RunMethod(method, a, b, c, a);
427 ASSERT_EQ((jobject)method->GetClass(), d.l);
428 EXPECT_EQ(1, gJava_MyClass_fooSSIOO_calls);
429 a.i = 0;
430 b.l = (jobject)NULL;
431 c.l = (jobject)16;
432 d = RunMethod(method, a, b, c, a);
433 ASSERT_EQ((jobject)method->GetClass(), d.l);
434 EXPECT_EQ(2, gJava_MyClass_fooSSIOO_calls);
435 a.i = 1;
436 d = RunMethod(method, a, b, c, a);
437 ASSERT_EQ((jobject)NULL, d.l);
438 EXPECT_EQ(3, gJava_MyClass_fooSSIOO_calls);
439 a.i = 2;
440 d = RunMethod(method, a, b, c, a);
441 ASSERT_EQ((jobject)16, d.l);
442 EXPECT_EQ(4, gJava_MyClass_fooSSIOO_calls);
443 a.i = 0;
444 b.l = (jobject)16;
445 c.l = (jobject)NULL;
446 d = RunMethod(method, a, b, c, a);
447 ASSERT_EQ((jobject)method->GetClass(), d.l);
448 EXPECT_EQ(5, gJava_MyClass_fooSSIOO_calls);
449 a.i = 1;
450 d = RunMethod(method, a, b, c, a);
451 ASSERT_EQ((jobject)16, d.l);
452 EXPECT_EQ(6, gJava_MyClass_fooSSIOO_calls);
453 a.i = 2;
454 d = RunMethod(method, a, b, c, a);
455 ASSERT_EQ((jobject)NULL, d.l);
456 EXPECT_EQ(7, gJava_MyClass_fooSSIOO_calls);
457}
458
Ian Rogersb033c752011-07-20 12:22:35 -0700459} // namespace art