blob: 59d8c2bf511fb3fa3d763f910bfc135960733b4c [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
166TEST_F(JniCompilerTest, CompileAndRunNoArgMethod) {
167 scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
168 scoped_ptr<ClassLinker> linker(ClassLinker::Create());
169 linker->AppendToClassPath(dex.get());
170 Class* klass = linker->FindClass("LMyClass;", NULL);
171 Method* method = klass->FindVirtualMethod("foo");
172
173 Assembler jni_asm;
174 JniCompiler jni_compiler;
175 jni_compiler.Compile(&jni_asm, method);
176
177 // TODO: should really use JNIEnv to RegisterNative, but missing a
178 // complete story on this, so hack the RegisterNative below
179 // JNIEnv* env = Thread::Current()->GetJniEnv();
180 // JNINativeMethod methods[] = {{"foo", "()V", (void*)&Java_MyClass_foo}};
181 method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_foo));
182
183 jvalue a;
184 a.l = (jobject)NULL;
185 EXPECT_EQ(0, gJava_MyClass_foo_calls);
186 RunMethod(method, a, a, a, a);
187 EXPECT_EQ(1, gJava_MyClass_foo_calls);
188 RunMethod(method, a, a, a, a);
189 EXPECT_EQ(2, gJava_MyClass_foo_calls);
190}
191
192TEST_F(JniCompilerTest, CompileAndRunIntMethod) {
193 scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
194 scoped_ptr<ClassLinker> linker(ClassLinker::Create());
195 linker->AppendToClassPath(dex.get());
196 Class* klass = linker->FindClass("LMyClass;", NULL);
197 Method* method = klass->FindVirtualMethod("fooI");
198
199 Assembler jni_asm;
200 JniCompiler jni_compiler;
201 jni_compiler.Compile(&jni_asm, method);
202
203 // TODO: should really use JNIEnv to RegisterNative, but missing a
204 // complete story on this, so hack the RegisterNative below
205 method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooI));
206
207 jvalue a, b, c;
208 a.l = (jobject)NULL;
209 b.i = 42;
210 EXPECT_EQ(0, gJava_MyClass_fooI_calls);
211 c = RunMethod(method, a, b, a, a);
212 ASSERT_EQ(42, c.i);
213 EXPECT_EQ(1, gJava_MyClass_fooI_calls);
214 b.i = 0xCAFED00D;
215 c = RunMethod(method, a, b, a, a);
216 ASSERT_EQ((jint)0xCAFED00D, c.i);
217 EXPECT_EQ(2, gJava_MyClass_fooI_calls);
218}
219
220TEST_F(JniCompilerTest, CompileAndRunIntIntMethod) {
221 scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
222 scoped_ptr<ClassLinker> linker(ClassLinker::Create());
223 linker->AppendToClassPath(dex.get());
224 Class* klass = linker->FindClass("LMyClass;", NULL);
225 Method* method = klass->FindVirtualMethod("fooII");
226
227 Assembler jni_asm;
228 JniCompiler jni_compiler;
229 jni_compiler.Compile(&jni_asm, method);
230
231 // TODO: should really use JNIEnv to RegisterNative, but missing a
232 // complete story on this, so hack the RegisterNative below
233 method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooII));
234
235 jvalue a, b, c, d;
236 a.l = (jobject)NULL;
237 b.i = 99;
238 c.i = 10;
239 EXPECT_EQ(0, gJava_MyClass_fooII_calls);
240 d = RunMethod(method, a, b, c, a);
241 ASSERT_EQ(99 - 10, d.i);
242 EXPECT_EQ(1, gJava_MyClass_fooII_calls);
243 b.i = 0xCAFEBABE;
244 c.i = 0xCAFED00D;
245 d = RunMethod(method, a, b, c, a);
246 ASSERT_EQ((jint)(0xCAFEBABE - 0xCAFED00D), d.i);
247 EXPECT_EQ(2, gJava_MyClass_fooII_calls);
248}
249
250
251TEST_F(JniCompilerTest, CompileAndRunDoubleDoubleMethod) {
252 scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
253 scoped_ptr<ClassLinker> linker(ClassLinker::Create());
254 linker->AppendToClassPath(dex.get());
255 Class* klass = linker->FindClass("LMyClass;", NULL);
256 Method* method = klass->FindVirtualMethod("fooDD");
257
258 Assembler jni_asm;
259 JniCompiler jni_compiler;
260 jni_compiler.Compile(&jni_asm, method);
261
262 // TODO: should really use JNIEnv to RegisterNative, but missing a
263 // complete story on this, so hack the RegisterNative below
264 method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooDD));
265
266 jvalue a, b, c, d;
267 a.l = (jobject)NULL;
268 b.d = 99;
269 c.d = 10;
270 EXPECT_EQ(0, gJava_MyClass_fooDD_calls);
271 d = RunMethodD(method, a, b, c);
272 ASSERT_EQ(b.d - c.d, d.d);
273 EXPECT_EQ(1, gJava_MyClass_fooDD_calls);
274 b.d = 3.14159265358979323846;
275 c.d = 0.69314718055994530942;
276 d = RunMethodD(method, a, b, c);
277 ASSERT_EQ(b.d - c.d, d.d);
278 EXPECT_EQ(2, gJava_MyClass_fooDD_calls);
279}
280
281TEST_F(JniCompilerTest, CompileAndRunIntObjectObjectMethod) {
282 scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
283 scoped_ptr<ClassLinker> linker(ClassLinker::Create());
284 linker->AppendToClassPath(dex.get());
285 Class* klass = linker->FindClass("LMyClass;", NULL);
286 Method* method = klass->FindVirtualMethod("fooIOO");
287
288 Assembler jni_asm;
289 JniCompiler jni_compiler;
290 jni_compiler.Compile(&jni_asm, method);
291
292 // TODO: should really use JNIEnv to RegisterNative, but missing a
293 // complete story on this, so hack the RegisterNative below
294 method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooIOO));
295
296 jvalue a, b, c, d, e;
297 a.l = (jobject)NULL;
298 b.i = 0;
299 c.l = (jobject)NULL;
300 d.l = (jobject)NULL;
301 EXPECT_EQ(0, gJava_MyClass_fooIOO_calls);
302 e = RunMethod(method, a, b, c, d);
303 ASSERT_EQ((jobject)NULL, e.l);
304 EXPECT_EQ(1, gJava_MyClass_fooIOO_calls);
305 a.l = (jobject)8;
306 b.i = 0;
307 c.l = (jobject)NULL;
308 d.l = (jobject)16;
309 e = RunMethod(method, a, b, c, d);
310 ASSERT_EQ((jobject)8, e.l);
311 EXPECT_EQ(2, gJava_MyClass_fooIOO_calls);
312 b.i = 1;
313 e = RunMethod(method, a, b, c, d);
314 ASSERT_EQ((jobject)NULL, e.l);
315 EXPECT_EQ(3, gJava_MyClass_fooIOO_calls);
316 b.i = 2;
317 e = RunMethod(method, a, b, c, d);
318 ASSERT_EQ((jobject)16, e.l);
319 EXPECT_EQ(4, gJava_MyClass_fooIOO_calls);
320 a.l = (jobject)8;
321 b.i = 0;
322 c.l = (jobject)16;
323 d.l = (jobject)NULL;
324 e = RunMethod(method, a, b, c, d);
325 ASSERT_EQ((jobject)8, e.l);
326 EXPECT_EQ(5, gJava_MyClass_fooIOO_calls);
327 b.i = 1;
328 e = RunMethod(method, a, b, c, d);
329 ASSERT_EQ((jobject)16, e.l);
330 EXPECT_EQ(6, gJava_MyClass_fooIOO_calls);
331 b.i = 2;
332 e = RunMethod(method, a, b, c, d);
333 ASSERT_EQ((jobject)NULL, e.l);
334 EXPECT_EQ(7, gJava_MyClass_fooIOO_calls);
335}
336
337TEST_F(JniCompilerTest, CompileAndRunStaticIntObjectObjectMethod) {
338 scoped_ptr<DexFile> dex(OpenDexFileBase64(kMyClassNativesDex));
339 scoped_ptr<ClassLinker> linker(ClassLinker::Create());
340 linker->AppendToClassPath(dex.get());
341 Class* klass = linker->FindClass("LMyClass;", NULL);
342 Method* method = klass->FindDirectMethod("fooSIOO");
343
344 Assembler jni_asm;
345 JniCompiler jni_compiler;
346 jni_compiler.Compile(&jni_asm, method);
347
348 // TODO: should really use JNIEnv to RegisterNative, but missing a
349 // complete story on this, so hack the RegisterNative below
350 method->RegisterNative(reinterpret_cast<void*>(&Java_MyClass_fooSIOO));
351
352 jvalue a, b, c, d;
353 a.i = 0;
354 b.l = (jobject)NULL;
355 c.l = (jobject)NULL;
356 EXPECT_EQ(0, gJava_MyClass_fooSIOO_calls);
357 d = RunMethod(method, a, b, c, a);
358 ASSERT_EQ((jobject)method->GetClass(), d.l);
359 EXPECT_EQ(1, gJava_MyClass_fooSIOO_calls);
360 a.i = 0;
361 b.l = (jobject)NULL;
362 c.l = (jobject)16;
363 d = RunMethod(method, a, b, c, a);
364 ASSERT_EQ((jobject)method->GetClass(), d.l);
365 EXPECT_EQ(2, gJava_MyClass_fooSIOO_calls);
366 a.i = 1;
367 d = RunMethod(method, a, b, c, a);
368 ASSERT_EQ((jobject)NULL, d.l);
369 EXPECT_EQ(3, gJava_MyClass_fooSIOO_calls);
370 a.i = 2;
371 d = RunMethod(method, a, b, c, a);
372 ASSERT_EQ((jobject)16, d.l);
373 EXPECT_EQ(4, gJava_MyClass_fooSIOO_calls);
374 a.i = 0;
375 b.l = (jobject)16;
376 c.l = (jobject)NULL;
377 d = RunMethod(method, a, b, c, a);
378 ASSERT_EQ((jobject)method->GetClass(), d.l);
379 EXPECT_EQ(5, gJava_MyClass_fooSIOO_calls);
380 a.i = 1;
381 d = RunMethod(method, a, b, c, a);
382 ASSERT_EQ((jobject)16, d.l);
383 EXPECT_EQ(6, gJava_MyClass_fooSIOO_calls);
384 a.i = 2;
385 d = RunMethod(method, a, b, c, a);
386 ASSERT_EQ((jobject)NULL, d.l);
387 EXPECT_EQ(7, gJava_MyClass_fooSIOO_calls);
388}
389
390} // namespace art