1 /*
2 * Copyright (C) 2008 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 /*
18 * Dalvik implementation of JNI interfaces.
19 */
20 #include "Dalvik.h"
21 #include "JniInternal.h"
22
23 #include <stdlib.h>
24 #include <stdarg.h>
25 #include <limits.h>
26
27 /*
28 Native methods and interaction with the GC
29
30 All JNI methods must start by changing their thread status to
31 THREAD_RUNNING, and finish by changing it back to THREAD_NATIVE before
32 returning to native code. The switch to "running" triggers a thread
33 suspension check.
34
35 With a rudimentary GC we should be able to skip the status change for
36 simple functions, e.g. IsSameObject, GetJavaVM, GetStringLength, maybe
37 even access to fields with primitive types. Our options are more limited
38 with a compacting GC, so we should replace JNI_ENTER with JNI_ENTER_NCGC
39 or somesuch on the "lite" functions if we want to try this optimization.
40
41 For performance reasons we do as little error-checking as possible here.
42 For example, we don't check to make sure the correct type of Object is
43 passed in when setting a field, and we don't prevent you from storing
44 new values in a "final" field. Such things are best handled in the
45 "check" version. For actions that are common, dangerous, and must be
46 checked at runtime, such as array bounds checks, we do the tests here.
47
48
49 General notes on local/global reference tracking
50
51 JNI provides explicit control over natively-held references that the GC
52 needs to know about. These can be local, in which case they're released
53 when the native method returns into the VM, or global, which are held
54 until explicitly released. (There are also weak-global references,
55 which have the lifespan and visibility of global references, but the
56 object they refer to may be collected.)
57
58 The references can be created with explicit JNI NewLocalRef / NewGlobalRef
59 calls. The former is very unusual, the latter is reasonably common
60 (e.g. for caching references to class objects).
61
62 Local references are most often created as a side-effect of JNI functions.
63 For example, the AllocObject/NewObject functions must create local
64 references to the objects returned, because nothing else in the GC root
65 set has a reference to the new objects.
66
67 The most common mode of operation is for a method to create zero or
68 more local references and return. Explicit "local delete" operations
69 are expected to be exceedingly rare, except when walking through an
70 object array, and the Push/PopLocalFrame calls are expected to be used
71 infrequently. For efficient operation, we want to add new local refs
72 with a simple store/increment operation; to avoid infinite growth in
73 pathological situations, we need to reclaim the space used by deleted
74 entries.
75
76 If we just want to maintain a list for the GC root set, we can use an
77 expanding append-only array that compacts when objects are deleted.
78 In typical situations, e.g. running through an array of objects, we will
79 be deleting one of the most recently added entries, so we can minimize
80 the number of elements moved (or avoid having to move any).
81
82 If we want to conceal the pointer values from native code, which is
83 necessary to allow the GC to move JNI-referenced objects around, then we
84 have to use a more complicated indirection mechanism.
85
86 The spec says, "Local references are only valid in the thread in which
87 they are created. The native code must not pass local references from
88 one thread to another."
89
90
91 Pinned objects
92
93 For some large chunks of data, notably primitive arrays and String data,
94 JNI allows the VM to choose whether it wants to pin the array object or
95 make a copy. We currently pin the memory for better execution performance.
96
97 TODO: we're using simple root set references to pin primitive array data,
98 because they have the property we need (i.e. the pointer we return is
99 guaranteed valid until we explicitly release it). However, if we have a
100 compacting GC and don't want to pin all memory held by all global refs,
101 we need to treat these differently.
102
103
104 Global reference tracking
105
106 There should be a small "active" set centered around the most-recently
107 added items.
108
109 Because it's global, access to it has to be synchronized. Additions and
110 removals require grabbing a mutex. If the table serves as an indirection
111 mechanism (i.e. it's not just a list for the benefit of the garbage
112 collector), reference lookups may also require grabbing a mutex.
113
114 The JNI spec does not define any sort of limit, so the list must be able
115 to expand to a reasonable size. It may be useful to log significant
116 increases in usage to help identify resource leaks.
117
118
119 Weak-global reference tracking
120
121 [TBD]
122
123
124 Local reference tracking
125
126 The table of local references can be stored on the interpreted stack or
127 in a parallel data structure (one per thread).
128
129 *** Approach #1: use the interpreted stack
130
131 The easiest place to tuck it is between the frame ptr and the first saved
132 register, which is always in0. (See the ASCII art in Stack.h.) We can
133 shift the "VM-specific goop" and frame ptr down, effectively inserting
134 the JNI local refs in the space normally occupied by local variables.
135
136 (Three things are accessed from the frame pointer:
137 (1) framePtr[N] is register vN, used to get at "ins" and "locals".
138 (2) framePtr - sizeof(StackSaveArea) is the VM frame goop.
139 (3) framePtr - sizeof(StackSaveArea) - numOuts is where the "outs" go.
140 The only thing that isn't determined by an offset from the current FP
141 is the previous frame. However, tucking things below the previous frame
142 can be problematic because the "outs" of the previous frame overlap with
143 the "ins" of the current frame. If the "ins" are altered they must be
144 restored before we return. For a native method call, the easiest and
145 safest thing to disrupt is #1, because there are no locals and the "ins"
146 are all copied to the native stack.)
147
148 We can implement Push/PopLocalFrame with the existing stack frame calls,
149 making sure we copy some goop from the previous frame (notably the method
150 ptr, so that dvmGetCurrentJNIMethod() doesn't require extra effort).
151
152 We can pre-allocate the storage at the time the stack frame is first
153 set up, but we have to be careful. When calling from interpreted code
154 the frame ptr points directly at the arguments we're passing, but we can
155 offset the args pointer when calling the native bridge.
156
157 To manage the local ref collection, we need to be able to find three
158 things: (1) the start of the region, (2) the end of the region, and (3)
159 the next available entry. The last is only required for quick adds.
160 We currently have two easily-accessible pointers, the current FP and the
161 previous frame's FP. (The "stack pointer" shown in the ASCII art doesn't
162 actually exist in the interpreted world.)
163
164 We can't use the current FP to find the first "in", because we want to
165 insert the variable-sized local refs table between them. It's awkward
166 to use the previous frame's FP because native methods invoked via
167 dvmCallMethod() or dvmInvokeMethod() don't have "ins", but native methods
168 invoked from interpreted code do. We can either track the local refs
169 table size with a field in the stack frame, or insert unnecessary items
170 so that all native stack frames have "ins".
171
172 Assuming we can find the region bounds, we still need pointer #3
173 for an efficient implementation. This can be stored in an otherwise
174 unused-for-native field in the frame goop.
175
176 When we run out of room we have to make more space. If we start allocating
177 locals immediately below in0 and grow downward, we will detect end-of-space
178 by running into the current frame's FP. We then memmove() the goop down
179 (memcpy if we guarantee the additional size is larger than the frame).
180 This is nice because we only have to move sizeof(StackSaveArea) bytes
181 each time.
182
183 Stack walking should be okay so long as nothing tries to access the
184 "ins" by an offset from the FP. In theory the "ins" could be read by
185 the debugger or SIGQUIT handler looking for "this" or other arguments,
186 but in practice this behavior isn't expected to work for native methods,
187 so we can simply disallow it.
188
189 A conservative GC can just scan the entire stack from top to bottom to find
190 all references. An exact GC will need to understand the actual layout.
191
192 *** Approach #2: use a parallel stack
193
194 Each Thread/JNIEnv points to a reference table. The struct has
195 a system-heap-allocated array of references and a pointer to the
196 next-available entry ("nextEntry").
197
198 Each stack frame has a pointer to what it sees as the "bottom" element
199 in the array (we can double-up the "currentPc" field). This is set to
200 "nextEntry" when the frame is pushed on. As local references are added
201 or removed, "nextEntry" is updated.
202
203 We implement Push/PopLocalFrame with actual stack frames. Before a JNI
204 frame gets popped, we set "nextEntry" to the "top" pointer of the current
205 frame, effectively releasing the references.
206
207 The GC will scan all references from the start of the table to the
208 "nextEntry" pointer.
209
210 *** Comparison
211
212 All approaches will return a failure result when they run out of local
213 reference space. For #1 that means blowing out the stack, for #2 it's
214 running out of room in the array.
215
216 Compared to #1, approach #2:
217 - Needs only one pointer in the stack frame goop.
218 - Makes pre-allocating storage unnecessary.
219 - Doesn't contend with interpreted stack depth for space. In most
220 cases, if something blows out the local ref storage, it's because the
221 JNI code was misbehaving rather than called from way down.
222 - Allows the GC to do a linear scan per thread in a buffer that is 100%
223 references. The GC can be slightly less smart when scanning the stack.
224 - Will be easier to work with if we combine native and interpeted stacks.
225
226 - Isn't as clean, especially when popping frames, since we have to do
227 explicit work. Fortunately we only have to do it when popping native
228 method calls off, so it doesn't add overhead to interpreted code paths.
229 - Is awkward to expand dynamically. We'll want to pre-allocate the full
230 amount of space; this is fine, since something on the order of 1KB should
231 be plenty. The JNI spec allows us to limit this.
232 - Requires the GC to scan even more memory. With the references embedded
233 in the stack we get better locality of reference.
234
235 */
236
237 /* fwd */
238 static const struct JNINativeInterface gNativeInterface;
239 static jobject addGlobalReference(Object* obj);
240
241 #ifdef WITH_JNI_STACK_CHECK
242 # define COMPUTE_STACK_SUM(_self) computeStackSum(_self);
243 # define CHECK_STACK_SUM(_self) checkStackSum(_self);
244 //static void computeStackSum(Thread* self);
245 //static void checkStackSum(Thread* self);
246 #else
247 # define COMPUTE_STACK_SUM(_self) ((void)0)
248 # define CHECK_STACK_SUM(_self) ((void)0)
249 #endif
250
251
252 /*
253 * ===========================================================================
254 * Utility functions
255 * ===========================================================================
256 */
257
258 /*
259 * Entry/exit processing for all JNI calls.
260 *
261 * If TRUSTED_JNIENV is set, we get to skip the (curiously expensive)
262 * thread-local storage lookup on our Thread*. If the caller has passed
263 * the wrong JNIEnv in, we're going to be accessing unsynchronized
264 * structures from more than one thread, and things are going to fail
265 * in bizarre ways. This is only sensible if the native code has been
266 * fully exercised with CheckJNI enabled.
267 */
268 #define TRUSTED_JNIENV
269 #ifdef TRUSTED_JNIENV
270 # define JNI_ENTER() \
271 Thread* _self = ((JNIEnvExt*)env)->self; \
272 CHECK_STACK_SUM(_self); \
273 dvmChangeStatus(_self, THREAD_RUNNING)
274 #else
275 # define JNI_ENTER() \
276 Thread* _self = dvmThreadSelf(); \
277 UNUSED_PARAMETER(env); \
278 CHECK_STACK_SUM(_self); \
279 dvmChangeStatus(_self, THREAD_RUNNING)
280 #endif
281 #define JNI_EXIT() \
282 dvmChangeStatus(_self, THREAD_NATIVE); \
283 COMPUTE_STACK_SUM(_self)
284
285 #define kGlobalRefsTableInitialSize 512
286 #define kGlobalRefsTableMaxSize 51200 /* arbitrary, must be < 64K */
287 #define kGrefWaterInterval 100
288 #define kTrackGrefUsage true
289
290 #define kPinTableInitialSize 16
291 #define kPinTableMaxSize 1024
292 #define kPinComplainThreshold 10
293
294 /*
295 * Allocate the global references table, and look up some classes for
296 * the benefit of direct buffer access.
297 */
dvmJniStartup(void)298 bool dvmJniStartup(void)
299 {
300 #ifdef USE_INDIRECT_REF
301 if (!dvmInitIndirectRefTable(&gDvm.jniGlobalRefTable,
302 kGlobalRefsTableInitialSize, kGlobalRefsTableMaxSize,
303 kIndirectKindGlobal))
304 return false;
305 #else
306 if (!dvmInitReferenceTable(&gDvm.jniGlobalRefTable,
307 kGlobalRefsTableInitialSize, kGlobalRefsTableMaxSize))
308 return false;
309 #endif
310
311 dvmInitMutex(&gDvm.jniGlobalRefLock);
312 gDvm.jniGlobalRefLoMark = 0;
313 gDvm.jniGlobalRefHiMark = kGrefWaterInterval * 2;
314
315 if (!dvmInitReferenceTable(&gDvm.jniPinRefTable,
316 kPinTableInitialSize, kPinTableMaxSize))
317 return false;
318
319 dvmInitMutex(&gDvm.jniPinRefLock);
320
321 Method* meth;
322
323 /*
324 * Grab the PhantomReference constructor.
325 */
326 gDvm.classJavaLangRefPhantomReference =
327 dvmFindSystemClassNoInit("Ljava/lang/ref/PhantomReference;");
328 if (gDvm.classJavaLangRefPhantomReference == NULL) {
329 LOGE("Unable to find PhantomReference class\n");
330 return false;
331 }
332 meth= dvmFindDirectMethodByDescriptor(gDvm.classJavaLangRefPhantomReference,
333 "<init>", "(Ljava/lang/Object;Ljava/lang/ref/ReferenceQueue;)V");
334 if (meth == NULL) {
335 LOGE("Unable to find constructor for PhantomReference\n");
336 return false;
337 }
338 gDvm.methJavaLangRefPhantomReference_init = meth;
339
340
341 /*
342 * Look up and cache pointers to some direct buffer classes, fields,
343 * and methods.
344 */
345 ClassObject* platformAddressClass =
346 dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddress;");
347 ClassObject* platformAddressFactoryClass =
348 dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddressFactory;");
349 ClassObject* directBufferClass =
350 dvmFindSystemClassNoInit("Lorg/apache/harmony/nio/internal/DirectBuffer;");
351 ClassObject* readWriteBufferClass =
352 dvmFindSystemClassNoInit("Ljava/nio/ReadWriteDirectByteBuffer;");
353 ClassObject* bufferClass =
354 dvmFindSystemClassNoInit("Ljava/nio/Buffer;");
355
356 if (platformAddressClass == NULL || platformAddressFactoryClass == NULL ||
357 directBufferClass == NULL || readWriteBufferClass == NULL ||
358 bufferClass == NULL)
359 {
360 LOGE("Unable to find internal direct buffer classes\n");
361 return false;
362 }
363 gDvm.classJavaNioReadWriteDirectByteBuffer = readWriteBufferClass;
364 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer = directBufferClass;
365 /* need a global reference for extended CheckJNI tests */
366 gDvm.jclassOrgApacheHarmonyNioInternalDirectBuffer =
367 addGlobalReference((Object*) directBufferClass);
368
369 /*
370 * We need a Method* here rather than a vtable offset, because
371 * DirectBuffer is an interface class.
372 */
373 meth = dvmFindVirtualMethodByDescriptor(
374 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer,
375 "getEffectiveAddress",
376 "()Lorg/apache/harmony/luni/platform/PlatformAddress;");
377 if (meth == NULL) {
378 LOGE("Unable to find PlatformAddress.getEffectiveAddress\n");
379 return false;
380 }
381 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress = meth;
382
383 meth = dvmFindVirtualMethodByDescriptor(platformAddressClass,
384 "toLong", "()J");
385 if (meth == NULL) {
386 LOGE("Unable to find PlatformAddress.toLong\n");
387 return false;
388 }
389 gDvm.voffOrgApacheHarmonyLuniPlatformPlatformAddress_toLong =
390 meth->methodIndex;
391
392 meth = dvmFindDirectMethodByDescriptor(platformAddressFactoryClass,
393 "on",
394 "(I)Lorg/apache/harmony/luni/platform/PlatformAddress;");
395 if (meth == NULL) {
396 LOGE("Unable to find PlatformAddressFactory.on\n");
397 return false;
398 }
399 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on = meth;
400
401 meth = dvmFindDirectMethodByDescriptor(readWriteBufferClass,
402 "<init>",
403 "(Lorg/apache/harmony/luni/platform/PlatformAddress;II)V");
404 if (meth == NULL) {
405 LOGE("Unable to find ReadWriteDirectByteBuffer.<init>\n");
406 return false;
407 }
408 gDvm.methJavaNioReadWriteDirectByteBuffer_init = meth;
409
410 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr =
411 dvmFindFieldOffset(platformAddressClass, "osaddr", "I");
412 if (gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr < 0) {
413 LOGE("Unable to find PlatformAddress.osaddr\n");
414 return false;
415 }
416
417 gDvm.offJavaNioBuffer_capacity =
418 dvmFindFieldOffset(bufferClass, "capacity", "I");
419 if (gDvm.offJavaNioBuffer_capacity < 0) {
420 LOGE("Unable to find Buffer.capacity\n");
421 return false;
422 }
423
424 gDvm.offJavaNioBuffer_effectiveDirectAddress =
425 dvmFindFieldOffset(bufferClass, "effectiveDirectAddress", "I");
426 if (gDvm.offJavaNioBuffer_effectiveDirectAddress < 0) {
427 LOGE("Unable to find Buffer.effectiveDirectAddress\n");
428 return false;
429 }
430
431 return true;
432 }
433
434 /*
435 * Free the global references table.
436 */
dvmJniShutdown(void)437 void dvmJniShutdown(void)
438 {
439 #ifdef USE_INDIRECT_REF
440 dvmClearIndirectRefTable(&gDvm.jniGlobalRefTable);
441 #else
442 dvmClearReferenceTable(&gDvm.jniGlobalRefTable);
443 #endif
444 dvmClearReferenceTable(&gDvm.jniPinRefTable);
445 }
446
447
448 /*
449 * Find the JNIEnv associated with the current thread.
450 *
451 * Currently stored in the Thread struct. Could also just drop this into
452 * thread-local storage.
453 */
dvmGetJNIEnvForThread(void)454 JNIEnvExt* dvmGetJNIEnvForThread(void)
455 {
456 Thread* self = dvmThreadSelf();
457 if (self == NULL)
458 return NULL;
459 return (JNIEnvExt*) dvmGetThreadJNIEnv(self);
460 }
461
462 /*
463 * Create a new JNIEnv struct and add it to the VM's list.
464 *
465 * "self" will be NULL for the main thread, since the VM hasn't started
466 * yet; the value will be filled in later.
467 */
dvmCreateJNIEnv(Thread * self)468 JNIEnv* dvmCreateJNIEnv(Thread* self)
469 {
470 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
471 JNIEnvExt* newEnv;
472
473 //if (self != NULL)
474 // LOGI("Ent CreateJNIEnv: threadid=%d %p\n", self->threadId, self);
475
476 assert(vm != NULL);
477
478 newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt));
479 newEnv->funcTable = &gNativeInterface;
480 newEnv->vm = vm;
481 newEnv->forceDataCopy = vm->forceDataCopy;
482 if (self != NULL) {
483 dvmSetJniEnvThreadId((JNIEnv*) newEnv, self);
484 assert(newEnv->envThreadId != 0);
485 } else {
486 /* make it obvious if we fail to initialize these later */
487 newEnv->envThreadId = 0x77777775;
488 newEnv->self = (Thread*) 0x77777779;
489 }
490 if (vm->useChecked)
491 dvmUseCheckedJniEnv(newEnv);
492
493 dvmLockMutex(&vm->envListLock);
494
495 /* insert at head of list */
496 newEnv->next = vm->envList;
497 assert(newEnv->prev == NULL);
498 if (vm->envList == NULL) // rare, but possible
499 vm->envList = newEnv;
500 else
501 vm->envList->prev = newEnv;
502 vm->envList = newEnv;
503
504 dvmUnlockMutex(&vm->envListLock);
505
506 //if (self != NULL)
507 // LOGI("Xit CreateJNIEnv: threadid=%d %p\n", self->threadId, self);
508 return (JNIEnv*) newEnv;
509 }
510
511 /*
512 * Remove a JNIEnv struct from the list and free it.
513 */
dvmDestroyJNIEnv(JNIEnv * env)514 void dvmDestroyJNIEnv(JNIEnv* env)
515 {
516 JNIEnvExt* extEnv = (JNIEnvExt*) env;
517 JavaVMExt* vm = extEnv->vm;
518 Thread* self;
519
520 if (env == NULL)
521 return;
522
523 self = dvmThreadSelf();
524 assert(self != NULL);
525
526 //LOGI("Ent DestroyJNIEnv: threadid=%d %p\n", self->threadId, self);
527
528 dvmLockMutex(&vm->envListLock);
529
530 if (extEnv == vm->envList) {
531 assert(extEnv->prev == NULL);
532 vm->envList = extEnv->next;
533 } else {
534 assert(extEnv->prev != NULL);
535 extEnv->prev->next = extEnv->next;
536 }
537 if (extEnv->next != NULL)
538 extEnv->next->prev = extEnv->prev;
539
540 dvmUnlockMutex(&extEnv->vm->envListLock);
541
542 free(env);
543 //LOGI("Xit DestroyJNIEnv: threadid=%d %p\n", self->threadId, self);
544 }
545
546
547 /*
548 * Retrieve the ReferenceTable struct for the current thread.
549 *
550 * Going through "env" rather than dvmThreadSelf() is faster but will
551 * get weird if the JNI code is passing the wrong JNIEnv around.
552 */
553 #ifdef USE_INDIRECT_REF
getLocalRefTable(JNIEnv * env)554 static inline IndirectRefTable* getLocalRefTable(JNIEnv* env)
555 #else
556 static inline ReferenceTable* getLocalRefTable(JNIEnv* env)
557 #endif
558 {
559 //return &dvmThreadSelf()->jniLocalRefTable;
560 return &((JNIEnvExt*)env)->self->jniLocalRefTable;
561 }
562
563 #ifdef USE_INDIRECT_REF
564 /*
565 * Convert an indirect reference to an Object reference. The indirect
566 * reference may be local, global, or weak-global.
567 *
568 * If "jobj" is NULL or an invalid indirect reference, this returns NULL.
569 */
dvmDecodeIndirectRef(JNIEnv * env,jobject jobj)570 Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj)
571 {
572 if (jobj == NULL)
573 return NULL;
574
575 Object* result;
576
577 switch (dvmGetIndirectRefType(jobj)) {
578 case kIndirectKindLocal:
579 {
580 IndirectRefTable* pRefTable = getLocalRefTable(env);
581 result = dvmGetFromIndirectRefTable(pRefTable, jobj);
582 }
583 break;
584 case kIndirectKindGlobal:
585 {
586 // TODO: find a way to avoid the mutex activity here
587 IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
588 dvmLockMutex(&gDvm.jniGlobalRefLock);
589 result = dvmGetFromIndirectRefTable(pRefTable, jobj);
590 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
591 }
592 break;
593 case kIndirectKindWeakGlobal:
594 {
595 // TODO: implement
596 LOGE("weak-global not yet supported\n");
597 result = NULL;
598 dvmAbort();
599 }
600 break;
601 case kIndirectKindInvalid:
602 default:
603 LOGW("Invalid indirect reference %p in decodeIndirectRef\n", jobj);
604 dvmAbort();
605 result = NULL;
606 break;
607 }
608
609 return result;
610 }
611 #else
612 /* use trivial inline in JniInternal.h for performance */
613 #endif
614
615 /*
616 * Add a local reference for an object to the current stack frame. When
617 * the native function returns, the reference will be discarded.
618 *
619 * We need to allow the same reference to be added multiple times.
620 *
621 * This will be called on otherwise unreferenced objects. We cannot do
622 * GC allocations here, and it's best if we don't grab a mutex.
623 *
624 * Returns the local reference (currently just the same pointer that was
625 * passed in), or NULL on failure.
626 */
addLocalReference(JNIEnv * env,Object * obj)627 static jobject addLocalReference(JNIEnv* env, Object* obj)
628 {
629 if (obj == NULL)
630 return NULL;
631
632 jobject jobj;
633
634 #ifdef USE_INDIRECT_REF
635 IndirectRefTable* pRefTable = getLocalRefTable(env);
636 void* curFrame = ((JNIEnvExt*)env)->self->curFrame;
637 u4 cookie = SAVEAREA_FROM_FP(curFrame)->xtra.localRefCookie;
638
639 jobj = (jobject) dvmAddToIndirectRefTable(pRefTable, cookie, obj);
640 if (jobj == NULL) {
641 dvmDumpIndirectRefTable(pRefTable, "JNI local");
642 LOGE("Failed adding to JNI local ref table (has %d entries)\n",
643 (int) dvmIndirectRefTableEntries(pRefTable));
644 dvmDumpThread(dvmThreadSelf(), false);
645 dvmAbort(); // spec says call FatalError; this is equivalent
646 } else {
647 LOGVV("LREF add %p (%s.%s) (ent=%d)\n", obj,
648 dvmGetCurrentJNIMethod()->clazz->descriptor,
649 dvmGetCurrentJNIMethod()->name,
650 (int) dvmReferenceTableEntries(pRefTable));
651 }
652 #else
653 ReferenceTable* pRefTable = getLocalRefTable(env);
654
655 if (!dvmAddToReferenceTable(pRefTable, obj)) {
656 dvmDumpReferenceTable(pRefTable, "JNI local");
657 LOGE("Failed adding to JNI local ref table (has %d entries)\n",
658 (int) dvmReferenceTableEntries(pRefTable));
659 dvmDumpThread(dvmThreadSelf(), false);
660 dvmAbort(); // spec says call FatalError; this is equivalent
661 } else {
662 LOGVV("LREF add %p (%s.%s) (ent=%d)\n", obj,
663 dvmGetCurrentJNIMethod()->clazz->descriptor,
664 dvmGetCurrentJNIMethod()->name,
665 (int) dvmReferenceTableEntries(pRefTable));
666 }
667
668 jobj = (jobject) obj;
669 #endif
670
671 return jobj;
672 }
673
674 /*
675 * Ensure that at least "capacity" references can be held in the local
676 * refs table of the current thread.
677 */
ensureLocalCapacity(JNIEnv * env,int capacity)678 static bool ensureLocalCapacity(JNIEnv* env, int capacity)
679 {
680 #ifdef USE_INDIRECT_REF
681 IndirectRefTable* pRefTable = getLocalRefTable(env);
682 int numEntries = dvmIndirectRefTableEntries(pRefTable);
683 // TODO: this isn't quite right, since "numEntries" includes holes
684 return ((kJniLocalRefMax - numEntries) >= capacity);
685 #else
686 ReferenceTable* pRefTable = getLocalRefTable(env);
687
688 return (kJniLocalRefMax - (pRefTable->nextEntry - pRefTable->table) >= capacity);
689 #endif
690 }
691
692 /*
693 * Explicitly delete a reference from the local list.
694 */
deleteLocalReference(JNIEnv * env,jobject jobj)695 static void deleteLocalReference(JNIEnv* env, jobject jobj)
696 {
697 if (jobj == NULL)
698 return;
699
700 #ifdef USE_INDIRECT_REF
701 IndirectRefTable* pRefTable = getLocalRefTable(env);
702 Thread* self = ((JNIEnvExt*)env)->self;
703 u4 cookie = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefCookie;
704
705 if (!dvmRemoveFromIndirectRefTable(pRefTable, cookie, jobj)) {
706 /*
707 * Attempting to delete a local reference that is not in the
708 * topmost local reference frame is a no-op. DeleteLocalRef returns
709 * void and doesn't throw any exceptions, but we should probably
710 * complain about it so the user will notice that things aren't
711 * going quite the way they expect.
712 */
713 LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry\n", jobj);
714 }
715 #else
716 ReferenceTable* pRefTable = getLocalRefTable(env);
717 Thread* self = ((JNIEnvExt*)env)->self;
718 Object** bottom = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefCookie;
719
720 if (!dvmRemoveFromReferenceTable(pRefTable, bottom, (Object*) jobj)) {
721 /*
722 * Attempting to delete a local reference that is not in the
723 * topmost local reference frame is a no-op. DeleteLocalRef returns
724 * void and doesn't throw any exceptions, but we should probably
725 * complain about it so the user will notice that things aren't
726 * going quite the way they expect.
727 */
728 LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry (valid=%d)\n",
729 jobj, dvmIsValidObject((Object*) jobj));
730 }
731 #endif
732 }
733
734 /*
735 * Add a global reference for an object.
736 *
737 * We may add the same object more than once. Add/remove calls are paired,
738 * so it needs to appear on the list multiple times.
739 */
addGlobalReference(Object * obj)740 static jobject addGlobalReference(Object* obj)
741 {
742 if (obj == NULL)
743 return NULL;
744
745 //LOGI("adding obj=%p\n", obj);
746 //dvmDumpThread(dvmThreadSelf(), false);
747
748 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangClass) {
749 ClassObject* clazz = (ClassObject*) obj;
750 LOGI("-------\n");
751 LOGI("Adding global ref on class %s\n", clazz->descriptor);
752 dvmDumpThread(dvmThreadSelf(), false);
753 }
754 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangString) {
755 StringObject* strObj = (StringObject*) obj;
756 char* str = dvmCreateCstrFromString(strObj);
757 if (strcmp(str, "sync-response") == 0) {
758 LOGI("-------\n");
759 LOGI("Adding global ref on string '%s'\n", str);
760 dvmDumpThread(dvmThreadSelf(), false);
761 //dvmAbort();
762 }
763 free(str);
764 }
765 if (false && ((Object*)obj)->clazz == gDvm.classArrayByte) {
766 ArrayObject* arrayObj = (ArrayObject*) obj;
767 if (arrayObj->length == 8192 /*&&
768 dvmReferenceTableEntries(&gDvm.jniGlobalRefTable) > 400*/)
769 {
770 LOGI("Adding global ref on byte array %p (len=%d)\n",
771 arrayObj, arrayObj->length);
772 dvmDumpThread(dvmThreadSelf(), false);
773 }
774 }
775
776 jobject jobj;
777
778 dvmLockMutex(&gDvm.jniGlobalRefLock);
779
780 /*
781 * Throwing an exception on failure is problematic, because JNI code
782 * may not be expecting an exception, and things sort of cascade. We
783 * want to have a hard limit to catch leaks during debugging, but this
784 * otherwise needs to expand until memory is consumed. As a practical
785 * matter, if we have many thousands of global references, chances are
786 * we're either leaking global ref table entries or we're going to
787 * run out of space in the GC heap.
788 */
789 #ifdef USE_INDIRECT_REF
790 jobj = dvmAddToIndirectRefTable(&gDvm.jniGlobalRefTable, IRT_FIRST_SEGMENT,
791 obj);
792 if (jobj == NULL) {
793 dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, "JNI global");
794 LOGE("Failed adding to JNI global ref table (%d entries)\n",
795 (int) dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable));
796 dvmAbort();
797 }
798
799 LOGVV("GREF add %p (%s.%s)\n", obj,
800 dvmGetCurrentJNIMethod()->clazz->descriptor,
801 dvmGetCurrentJNIMethod()->name);
802
803 /* GREF usage tracking; should probably be disabled for production env */
804 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
805 int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable);
806 // TODO: adjust for "holes"
807 if (count > gDvm.jniGlobalRefHiMark) {
808 LOGD("GREF has increased to %d\n", count);
809 gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
810 gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
811
812 /* watch for "excessive" use; not generally appropriate */
813 if (count >= gDvm.jniGrefLimit) {
814 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
815 if (vm->warnError) {
816 dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable,
817 "JNI global");
818 LOGE("Excessive JNI global references (%d)\n", count);
819 dvmAbort();
820 } else {
821 LOGW("Excessive JNI global references (%d)\n", count);
822 }
823 }
824 }
825 }
826 #else
827 if (!dvmAddToReferenceTable(&gDvm.jniGlobalRefTable, obj)) {
828 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
829 LOGE("Failed adding to JNI global ref table (%d entries)\n",
830 (int) dvmReferenceTableEntries(&gDvm.jniGlobalRefTable));
831 dvmAbort();
832 }
833 jobj = (jobject) obj;
834
835 LOGVV("GREF add %p (%s.%s)\n", obj,
836 dvmGetCurrentJNIMethod()->clazz->descriptor,
837 dvmGetCurrentJNIMethod()->name);
838
839 /* GREF usage tracking; should probably be disabled for production env */
840 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
841 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
842 if (count > gDvm.jniGlobalRefHiMark) {
843 LOGD("GREF has increased to %d\n", count);
844 gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
845 gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
846
847 /* watch for "excessive" use; not generally appropriate */
848 if (count >= gDvm.jniGrefLimit) {
849 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
850 if (vm->warnError) {
851 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable,"JNI global");
852 LOGE("Excessive JNI global references (%d)\n", count);
853 dvmAbort();
854 } else {
855 LOGW("Excessive JNI global references (%d)\n", count);
856 }
857 }
858 }
859 }
860 #endif
861
862 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
863 return jobj;
864 }
865
866 /*
867 * Remove a global reference. In most cases it's the entry most recently
868 * added, which makes this pretty quick.
869 *
870 * Thought: if it's not the most recent entry, just null it out. When we
871 * fill up, do a compaction pass before we expand the list.
872 */
deleteGlobalReference(jobject jobj)873 static void deleteGlobalReference(jobject jobj)
874 {
875 if (jobj == NULL)
876 return;
877
878 dvmLockMutex(&gDvm.jniGlobalRefLock);
879
880 #ifdef USE_INDIRECT_REF
881 if (!dvmRemoveFromIndirectRefTable(&gDvm.jniGlobalRefTable,
882 IRT_FIRST_SEGMENT, jobj))
883 {
884 LOGW("JNI: DeleteGlobalRef(%p) failed to find entry\n", jobj);
885 goto bail;
886 }
887
888 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
889 int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable);
890 // TODO: not quite right, need to subtract holes
891 if (count < gDvm.jniGlobalRefLoMark) {
892 LOGD("GREF has decreased to %d\n", count);
893 gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
894 gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
895 }
896 }
897 #else
898 if (!dvmRemoveFromReferenceTable(&gDvm.jniGlobalRefTable,
899 gDvm.jniGlobalRefTable.table, jobj))
900 {
901 LOGW("JNI: DeleteGlobalRef(%p) failed to find entry (valid=%d)\n",
902 jobj, dvmIsValidObject((Object*) jobj));
903 goto bail;
904 }
905
906 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
907 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
908 if (count < gDvm.jniGlobalRefLoMark) {
909 LOGD("GREF has decreased to %d\n", count);
910 gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
911 gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
912 }
913 }
914 #endif
915
916 bail:
917 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
918 }
919
920 /*
921 * We create a PhantomReference that references the object, add a
922 * global reference to it, and then flip some bits before returning it.
923 * The last step ensures that we detect it as special and that only
924 * appropriate calls will accept it.
925 *
926 * On failure, returns NULL with an exception pending.
927 */
createWeakGlobalRef(JNIEnv * env,jobject jobj)928 static jweak createWeakGlobalRef(JNIEnv* env, jobject jobj)
929 {
930 if (jobj == NULL)
931 return NULL;
932
933 Thread* self = ((JNIEnvExt*)env)->self;
934 Object* obj = dvmDecodeIndirectRef(env, jobj);
935 Object* phantomObj;
936 jobject phantomRef;
937
938 /*
939 * Allocate a PhantomReference, then call the constructor to set
940 * the referent and the reference queue.
941 *
942 * We use a "magic" reference queue that the GC knows about; it behaves
943 * more like a queueless WeakReference, clearing the referent and
944 * not calling enqueue().
945 */
946 if (!dvmIsClassInitialized(gDvm.classJavaLangRefPhantomReference))
947 dvmInitClass(gDvm.classJavaLangRefPhantomReference);
948 phantomObj = dvmAllocObject(gDvm.classJavaLangRefPhantomReference,
949 ALLOC_DEFAULT);
950 if (phantomObj == NULL) {
951 assert(dvmCheckException(self));
952 LOGW("Failed on WeakGlobalRef alloc\n");
953 return NULL;
954 }
955
956 JValue unused;
957 dvmCallMethod(self, gDvm.methJavaLangRefPhantomReference_init, phantomObj,
958 &unused, obj, NULL);
959 dvmReleaseTrackedAlloc(phantomObj, self);
960
961 if (dvmCheckException(self)) {
962 LOGW("PhantomReference init failed\n");
963 return NULL;
964 }
965
966 LOGV("+++ WGR: created phantom ref %p for object %p\n", phantomObj, obj);
967
968 /*
969 * Add it to the global reference table, and mangle the pointer.
970 */
971 phantomRef = addGlobalReference(phantomObj);
972 return dvmObfuscateWeakGlobalRef(phantomRef);
973 }
974
975 /*
976 * Delete the global reference that's keeping the PhantomReference around.
977 * The PhantomReference will eventually be discarded by the GC.
978 */
deleteWeakGlobalRef(JNIEnv * env,jweak wref)979 static void deleteWeakGlobalRef(JNIEnv* env, jweak wref)
980 {
981 if (wref == NULL)
982 return;
983
984 jobject phantomRef = dvmNormalizeWeakGlobalRef(wref);
985 deleteGlobalReference(phantomRef);
986 }
987
988 /*
989 * Extract the referent from a PhantomReference. Used for weak global
990 * references.
991 *
992 * "jwobj" is a "mangled" WGR pointer.
993 */
getPhantomReferent(JNIEnv * env,jweak jwobj)994 static Object* getPhantomReferent(JNIEnv* env, jweak jwobj)
995 {
996 jobject jobj = dvmNormalizeWeakGlobalRef(jwobj);
997 Object* obj = dvmDecodeIndirectRef(env, jobj);
998
999 if (obj->clazz != gDvm.classJavaLangRefPhantomReference) {
1000 LOGE("%p is not a phantom reference (%s)\n",
1001 jwobj, obj->clazz->descriptor);
1002 return NULL;
1003 }
1004
1005 return dvmGetFieldObject(obj, gDvm.offJavaLangRefReference_referent);
1006 }
1007
1008
1009 /*
1010 * Objects don't currently move, so we just need to create a reference
1011 * that will ensure the array object isn't collected.
1012 *
1013 * We use a separate reference table, which is part of the GC root set.
1014 */
pinPrimitiveArray(ArrayObject * arrayObj)1015 static void pinPrimitiveArray(ArrayObject* arrayObj)
1016 {
1017 if (arrayObj == NULL)
1018 return;
1019
1020 dvmLockMutex(&gDvm.jniPinRefLock);
1021 if (!dvmAddToReferenceTable(&gDvm.jniPinRefTable, (Object*)arrayObj)) {
1022 dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
1023 LOGE("Failed adding to JNI pinned array ref table (%d entries)\n",
1024 (int) dvmReferenceTableEntries(&gDvm.jniPinRefTable));
1025 dvmDumpThread(dvmThreadSelf(), false);
1026 dvmAbort();
1027 }
1028
1029 /*
1030 * If we're watching global ref usage, also keep an eye on these.
1031 *
1032 * The total number of pinned primitive arrays should be pretty small.
1033 * A single array should not be pinned more than once or twice; any
1034 * more than that is a strong indicator that a Release function is
1035 * not being called.
1036 */
1037 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
1038 int count = 0;
1039 Object** ppObj = gDvm.jniPinRefTable.table;
1040 while (ppObj < gDvm.jniPinRefTable.nextEntry) {
1041 if (*ppObj++ == (Object*) arrayObj)
1042 count++;
1043 }
1044
1045 if (count > kPinComplainThreshold) {
1046 LOGW("JNI: pin count on array %p (%s) is now %d\n",
1047 arrayObj, arrayObj->obj.clazz->descriptor, count);
1048 /* keep going */
1049 }
1050 }
1051
1052 dvmUnlockMutex(&gDvm.jniPinRefLock);
1053 }
1054
1055 /*
1056 * Un-pin the array object. If an object was pinned twice, it must be
1057 * unpinned twice before it's free to move.
1058 */
unpinPrimitiveArray(ArrayObject * arrayObj)1059 static void unpinPrimitiveArray(ArrayObject* arrayObj)
1060 {
1061 if (arrayObj == NULL)
1062 return;
1063
1064 dvmLockMutex(&gDvm.jniPinRefLock);
1065 if (!dvmRemoveFromReferenceTable(&gDvm.jniPinRefTable,
1066 gDvm.jniPinRefTable.table, (Object*) arrayObj))
1067 {
1068 LOGW("JNI: unpinPrimitiveArray(%p) failed to find entry (valid=%d)\n",
1069 arrayObj, dvmIsValidObject((Object*) arrayObj));
1070 goto bail;
1071 }
1072
1073 bail:
1074 dvmUnlockMutex(&gDvm.jniPinRefLock);
1075 }
1076
1077 /*
1078 * Dump the contents of the JNI reference tables to the log file.
1079 *
1080 * We only dump the local refs associated with the current thread.
1081 */
dvmDumpJniReferenceTables(void)1082 void dvmDumpJniReferenceTables(void)
1083 {
1084 Thread* self = dvmThreadSelf();
1085 JNIEnv* env = self->jniEnv;
1086 ReferenceTable* pLocalRefs = getLocalRefTable(env);
1087
1088 #ifdef USE_INDIRECT_REF
1089 dvmDumpIndirectRefTable(pLocalRefs, "JNI local");
1090 dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, "JNI global");
1091 #else
1092 dvmDumpReferenceTable(pLocalRefs, "JNI local");
1093 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
1094 #endif
1095 dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
1096 }
1097
1098 /*
1099 * GC helper function to mark all JNI global references.
1100 *
1101 * We're currently handling the "pin" table here too.
1102 */
dvmGcMarkJniGlobalRefs()1103 void dvmGcMarkJniGlobalRefs()
1104 {
1105 Object** op;
1106
1107 dvmLockMutex(&gDvm.jniGlobalRefLock);
1108
1109 #ifdef USE_INDIRECT_REF
1110 IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
1111 op = pRefTable->table;
1112 int numEntries = dvmIndirectRefTableEntries(pRefTable);
1113 int i;
1114
1115 for (i = 0; i < numEntries; i++) {
1116 Object* obj = *op;
1117 if (obj != NULL)
1118 dvmMarkObjectNonNull(obj);
1119 op++;
1120 }
1121 #else
1122 op = gDvm.jniGlobalRefTable.table;
1123 while ((uintptr_t)op < (uintptr_t)gDvm.jniGlobalRefTable.nextEntry) {
1124 dvmMarkObjectNonNull(*(op++));
1125 }
1126 #endif
1127
1128 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
1129
1130
1131 dvmLockMutex(&gDvm.jniPinRefLock);
1132
1133 op = gDvm.jniPinRefTable.table;
1134 while ((uintptr_t)op < (uintptr_t)gDvm.jniPinRefTable.nextEntry) {
1135 dvmMarkObjectNonNull(*(op++));
1136 }
1137
1138 dvmUnlockMutex(&gDvm.jniPinRefLock);
1139 }
1140
1141
1142 #ifndef USE_INDIRECT_REF
1143 /*
1144 * Determine if "obj" appears in the argument list for the native method.
1145 *
1146 * We use the "shorty" signature to determine which argument slots hold
1147 * reference types.
1148 */
findInArgList(Thread * self,Object * obj)1149 static bool findInArgList(Thread* self, Object* obj)
1150 {
1151 const Method* meth;
1152 u4* fp;
1153 int i;
1154
1155 fp = self->curFrame;
1156 while (1) {
1157 /*
1158 * Back up over JNI PushLocalFrame frames. This works because the
1159 * previous frame on the interpreted stack is either a break frame
1160 * (if we called here via native code) or an interpreted method (if
1161 * we called here via the interpreter). In both cases the method
1162 * pointer won't match.
1163 */
1164 StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
1165 meth = saveArea->method;
1166 if (meth != SAVEAREA_FROM_FP(saveArea->prevFrame)->method)
1167 break;
1168 fp = saveArea->prevFrame;
1169 }
1170
1171 LOGVV("+++ scanning %d args in %s (%s)\n",
1172 meth->insSize, meth->name, meth->shorty);
1173 const char* shorty = meth->shorty +1; /* skip return type char */
1174 for (i = 0; i < meth->insSize; i++) {
1175 if (i == 0 && !dvmIsStaticMethod(meth)) {
1176 /* first arg is "this" ref, not represented in "shorty" */
1177 if (fp[i] == (u4) obj)
1178 return true;
1179 } else {
1180 /* if this is a reference type, see if it matches */
1181 switch (*shorty) {
1182 case 'L':
1183 if (fp[i] == (u4) obj)
1184 return true;
1185 break;
1186 case 'D':
1187 case 'J':
1188 i++;
1189 break;
1190 case '\0':
1191 LOGE("Whoops! ran off the end of %s (%d)\n",
1192 meth->shorty, meth->insSize);
1193 break;
1194 default:
1195 if (fp[i] == (u4) obj)
1196 LOGI("NOTE: ref %p match on arg type %c\n", obj, *shorty);
1197 break;
1198 }
1199 shorty++;
1200 }
1201 }
1202
1203 /*
1204 * For static methods, we also pass a class pointer in.
1205 */
1206 if (dvmIsStaticMethod(meth)) {
1207 //LOGI("+++ checking class pointer in %s\n", meth->name);
1208 if ((void*)obj == (void*)meth->clazz)
1209 return true;
1210 }
1211 return false;
1212 }
1213 #endif
1214
1215 /*
1216 * Verify that a reference passed in from native code is one that the
1217 * code is allowed to have.
1218 *
1219 * It's okay for native code to pass us a reference that:
1220 * - was passed in as an argument when invoked by native code (and hence
1221 * is in the JNI local refs table)
1222 * - was returned to it from JNI (and is now in the local refs table)
1223 * - is present in the JNI global refs table
1224 *
1225 * Used by -Xcheck:jni and GetObjectRefType.
1226 *
1227 * NOTE: in the current VM, global and local references are identical. If
1228 * something is both global and local, we can't tell them apart, and always
1229 * return "local".
1230 */
dvmGetJNIRefType(JNIEnv * env,jobject jobj)1231 jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj)
1232 {
1233 #ifdef USE_INDIRECT_REF
1234 /*
1235 * IndirectRefKind is currently defined as an exact match of
1236 * jobjectRefType, so this is easy. We have to decode it to determine
1237 * if it's a valid reference and not merely valid-looking.
1238 */
1239 Object* obj = dvmDecodeIndirectRef(env, jobj);
1240
1241 if (obj == NULL) {
1242 /* invalid ref, or jobj was NULL */
1243 return JNIInvalidRefType;
1244 } else {
1245 return (jobjectRefType) dvmGetIndirectRefType(jobj);
1246 }
1247 #else
1248 ReferenceTable* pRefTable = getLocalRefTable(env);
1249 Thread* self = dvmThreadSelf();
1250
1251 if (dvmIsWeakGlobalRef(jobj)) {
1252 return JNIWeakGlobalRefType;
1253 }
1254
1255 /* check args */
1256 if (findInArgList(self, jobj)) {
1257 //LOGI("--- REF found %p on stack\n", jobj);
1258 return JNILocalRefType;
1259 }
1260
1261 /* check locals */
1262 if (dvmFindInReferenceTable(pRefTable, pRefTable->table, jobj) != NULL) {
1263 //LOGI("--- REF found %p in locals\n", jobj);
1264 return JNILocalRefType;
1265 }
1266
1267 /* check globals */
1268 dvmLockMutex(&gDvm.jniGlobalRefLock);
1269 if (dvmFindInReferenceTable(&gDvm.jniGlobalRefTable,
1270 gDvm.jniGlobalRefTable.table, jobj))
1271 {
1272 //LOGI("--- REF found %p in globals\n", jobj);
1273 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
1274 return JNIGlobalRefType;
1275 }
1276 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
1277
1278 /* not found! */
1279 return JNIInvalidRefType;
1280 #endif
1281 }
1282
1283 /*
1284 * Register a method that uses JNI calling conventions.
1285 */
dvmRegisterJNIMethod(ClassObject * clazz,const char * methodName,const char * signature,void * fnPtr)1286 static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName,
1287 const char* signature, void* fnPtr)
1288 {
1289 Method* method;
1290 bool result = false;
1291
1292 if (fnPtr == NULL)
1293 goto bail;
1294
1295 method = dvmFindDirectMethodByDescriptor(clazz, methodName, signature);
1296 if (method == NULL)
1297 method = dvmFindVirtualMethodByDescriptor(clazz, methodName, signature);
1298 if (method == NULL) {
1299 LOGW("ERROR: Unable to find decl for native %s.%s:%s\n",
1300 clazz->descriptor, methodName, signature);
1301 goto bail;
1302 }
1303
1304 if (!dvmIsNativeMethod(method)) {
1305 LOGW("Unable to register: not native: %s.%s:%s\n",
1306 clazz->descriptor, methodName, signature);
1307 goto bail;
1308 }
1309
1310 if (method->nativeFunc != dvmResolveNativeMethod) {
1311 /* this is allowed, but unusual */
1312 LOGV("Note: %s.%s:%s was already registered\n",
1313 clazz->descriptor, methodName, signature);
1314 }
1315
1316 dvmUseJNIBridge(method, fnPtr);
1317
1318 LOGV("JNI-registered %s.%s:%s\n", clazz->descriptor, methodName,
1319 signature);
1320 result = true;
1321
1322 bail:
1323 return result;
1324 }
1325
1326 /*
1327 * Returns "true" if CheckJNI is enabled in the VM.
1328 */
dvmIsCheckJNIEnabled(void)1329 static bool dvmIsCheckJNIEnabled(void)
1330 {
1331 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
1332 return vm->useChecked;
1333 }
1334
1335 /*
1336 * Returns the appropriate JNI bridge for 'method', also taking into account
1337 * the -Xcheck:jni setting.
1338 */
dvmSelectJNIBridge(const Method * method)1339 static DalvikBridgeFunc dvmSelectJNIBridge(const Method* method)
1340 {
1341 enum {
1342 kJNIGeneral = 0,
1343 kJNISync = 1,
1344 kJNIVirtualNoRef = 2,
1345 kJNIStaticNoRef = 3,
1346 } kind;
1347 static const DalvikBridgeFunc stdFunc[] = {
1348 dvmCallJNIMethod_general,
1349 dvmCallJNIMethod_synchronized,
1350 dvmCallJNIMethod_virtualNoRef,
1351 dvmCallJNIMethod_staticNoRef
1352 };
1353 static const DalvikBridgeFunc checkFunc[] = {
1354 dvmCheckCallJNIMethod_general,
1355 dvmCheckCallJNIMethod_synchronized,
1356 dvmCheckCallJNIMethod_virtualNoRef,
1357 dvmCheckCallJNIMethod_staticNoRef
1358 };
1359
1360 bool hasRefArg = false;
1361
1362 if (dvmIsSynchronizedMethod(method)) {
1363 /* use version with synchronization; calls into general handler */
1364 kind = kJNISync;
1365 } else {
1366 /*
1367 * Do a quick scan through the "shorty" signature to see if the method
1368 * takes any reference arguments.
1369 */
1370 const char* cp = method->shorty;
1371 while (*++cp != '\0') { /* pre-incr to skip return type */
1372 if (*cp == 'L') {
1373 /* 'L' used for both object and array references */
1374 hasRefArg = true;
1375 break;
1376 }
1377 }
1378
1379 if (hasRefArg) {
1380 /* use general handler to slurp up reference args */
1381 kind = kJNIGeneral;
1382 } else {
1383 /* virtual methods have a ref in args[0] (not in signature) */
1384 if (dvmIsStaticMethod(method))
1385 kind = kJNIStaticNoRef;
1386 else
1387 kind = kJNIVirtualNoRef;
1388 }
1389 }
1390
1391 return dvmIsCheckJNIEnabled() ? checkFunc[kind] : stdFunc[kind];
1392 }
1393
1394 /*
1395 * Trace a call into native code.
1396 */
dvmTraceCallJNIMethod(const u4 * args,JValue * pResult,const Method * method,Thread * self)1397 static void dvmTraceCallJNIMethod(const u4* args, JValue* pResult,
1398 const Method* method, Thread* self)
1399 {
1400 dvmLogNativeMethodEntry(method, args);
1401 DalvikBridgeFunc bridge = dvmSelectJNIBridge(method);
1402 (*bridge)(args, pResult, method, self);
1403 dvmLogNativeMethodExit(method, self, *pResult);
1404 }
1405
1406 /**
1407 * Returns true if the -Xjnitrace setting implies we should trace 'method'.
1408 */
shouldTrace(Method * method)1409 static bool shouldTrace(Method* method)
1410 {
1411 return gDvm.jniTrace && strstr(method->clazz->descriptor, gDvm.jniTrace);
1412 }
1413
1414 /*
1415 * Point "method->nativeFunc" at the JNI bridge, and overload "method->insns"
1416 * to point at the actual function.
1417 */
dvmUseJNIBridge(Method * method,void * func)1418 void dvmUseJNIBridge(Method* method, void* func)
1419 {
1420 DalvikBridgeFunc bridge = shouldTrace(method)
1421 ? dvmTraceCallJNIMethod
1422 : dvmSelectJNIBridge(method);
1423 dvmSetNativeFunc(method, bridge, func);
1424 }
1425
1426 /*
1427 * Get the method currently being executed by examining the interp stack.
1428 */
dvmGetCurrentJNIMethod(void)1429 const Method* dvmGetCurrentJNIMethod(void)
1430 {
1431 assert(dvmThreadSelf() != NULL);
1432
1433 void* fp = dvmThreadSelf()->curFrame;
1434 const Method* meth = SAVEAREA_FROM_FP(fp)->method;
1435
1436 assert(meth != NULL);
1437 assert(dvmIsNativeMethod(meth));
1438 return meth;
1439 }
1440
1441
1442 /*
1443 * Track a JNI MonitorEnter in the current thread.
1444 *
1445 * The goal is to be able to "implicitly" release all JNI-held monitors
1446 * when the thread detaches.
1447 *
1448 * Monitors may be entered multiple times, so we add a new entry for each
1449 * enter call. It would be more efficient to keep a counter. At present
1450 * there's no real motivation to improve this however.
1451 */
trackMonitorEnter(Thread * self,Object * obj)1452 static void trackMonitorEnter(Thread* self, Object* obj)
1453 {
1454 static const int kInitialSize = 16;
1455 ReferenceTable* refTable = &self->jniMonitorRefTable;
1456
1457 /* init table on first use */
1458 if (refTable->table == NULL) {
1459 assert(refTable->maxEntries == 0);
1460
1461 if (!dvmInitReferenceTable(refTable, kInitialSize, INT_MAX)) {
1462 LOGE("Unable to initialize monitor tracking table\n");
1463 dvmAbort();
1464 }
1465 }
1466
1467 if (!dvmAddToReferenceTable(refTable, obj)) {
1468 /* ran out of memory? could throw exception instead */
1469 LOGE("Unable to add entry to monitor tracking table\n");
1470 dvmAbort();
1471 } else {
1472 LOGVV("--- added monitor %p\n", obj);
1473 }
1474 }
1475
1476
1477 /*
1478 * Track a JNI MonitorExit in the current thread.
1479 */
trackMonitorExit(Thread * self,Object * obj)1480 static void trackMonitorExit(Thread* self, Object* obj)
1481 {
1482 ReferenceTable* pRefTable = &self->jniMonitorRefTable;
1483
1484 if (!dvmRemoveFromReferenceTable(pRefTable, pRefTable->table, obj)) {
1485 LOGE("JNI monitor %p not found in tracking list\n", obj);
1486 /* keep going? */
1487 } else {
1488 LOGVV("--- removed monitor %p\n", obj);
1489 }
1490 }
1491
1492 /*
1493 * Release all monitors held by the jniMonitorRefTable list.
1494 */
dvmReleaseJniMonitors(Thread * self)1495 void dvmReleaseJniMonitors(Thread* self)
1496 {
1497 ReferenceTable* pRefTable = &self->jniMonitorRefTable;
1498 Object** top = pRefTable->table;
1499
1500 if (top == NULL)
1501 return;
1502
1503 Object** ptr = pRefTable->nextEntry;
1504 while (--ptr >= top) {
1505 if (!dvmUnlockObject(self, *ptr)) {
1506 LOGW("Unable to unlock monitor %p at thread detach\n", *ptr);
1507 } else {
1508 LOGVV("--- detach-releasing monitor %p\n", *ptr);
1509 }
1510 }
1511
1512 /* zap it */
1513 pRefTable->nextEntry = pRefTable->table;
1514 }
1515
1516 /*
1517 * Determine if the specified class can be instantiated from JNI. This
1518 * is used by AllocObject / NewObject, which are documented as throwing
1519 * an exception for abstract and interface classes, and not accepting
1520 * array classes. We also want to reject attempts to create new Class
1521 * objects, since only DefineClass should do that.
1522 */
canAllocClass(ClassObject * clazz)1523 static bool canAllocClass(ClassObject* clazz)
1524 {
1525 if (dvmIsAbstractClass(clazz) || dvmIsInterfaceClass(clazz)) {
1526 /* JNI spec defines what this throws */
1527 dvmThrowExceptionFmt("Ljava/lang/InstantiationException;",
1528 "Can't instantiate %s (abstract or interface)", clazz->descriptor);
1529 return false;
1530 } else if (dvmIsArrayClass(clazz) || clazz == gDvm.classJavaLangClass) {
1531 /* spec says "must not" for arrays, ignores Class */
1532 dvmThrowExceptionFmt("Ljava/lang/IllegalArgumentException;",
1533 "Can't instantiate %s (array or Class) with this JNI function",
1534 clazz->descriptor);
1535 return false;
1536 }
1537
1538 return true;
1539 }
1540
1541 #ifdef WITH_JNI_STACK_CHECK
1542 /*
1543 * Compute a CRC on the entire interpreted stack.
1544 *
1545 * Would be nice to compute it on "self" as well, but there are parts of
1546 * the Thread that can be altered by other threads (e.g. prev/next pointers).
1547 */
computeStackSum(Thread * self)1548 static void computeStackSum(Thread* self)
1549 {
1550 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
1551 u4 crc = dvmInitCrc32();
1552 self->stackCrc = 0;
1553 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
1554 self->stackCrc = crc;
1555 }
1556
1557 /*
1558 * Compute a CRC on the entire interpreted stack, and compare it to what
1559 * we previously computed.
1560 *
1561 * We can execute JNI directly from native code without calling in from
1562 * interpreted code during VM initialization and immediately after JNI
1563 * thread attachment. Another opportunity exists during JNI_OnLoad. Rather
1564 * than catching these cases we just ignore them here, which is marginally
1565 * less accurate but reduces the amount of code we have to touch with #ifdefs.
1566 */
checkStackSum(Thread * self)1567 static void checkStackSum(Thread* self)
1568 {
1569 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
1570 u4 stackCrc, crc;
1571
1572 stackCrc = self->stackCrc;
1573 self->stackCrc = 0;
1574 crc = dvmInitCrc32();
1575 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
1576 if (crc != stackCrc) {
1577 const Method* meth = dvmGetCurrentJNIMethod();
1578 if (dvmComputeExactFrameDepth(self->curFrame) == 1) {
1579 LOGD("JNI: bad stack CRC (0x%08x) -- okay during init\n",
1580 stackCrc);
1581 } else if (strcmp(meth->name, "nativeLoad") == 0 &&
1582 (strcmp(meth->clazz->descriptor, "Ljava/lang/Runtime;") == 0))
1583 {
1584 LOGD("JNI: bad stack CRC (0x%08x) -- okay during JNI_OnLoad\n",
1585 stackCrc);
1586 } else {
1587 LOGW("JNI: bad stack CRC (%08x vs %08x)\n", crc, stackCrc);
1588 dvmAbort();
1589 }
1590 }
1591 self->stackCrc = (u4) -1; /* make logic errors more noticeable */
1592 }
1593 #endif
1594
1595
1596 /*
1597 * ===========================================================================
1598 * JNI call bridge
1599 * ===========================================================================
1600 */
1601
1602 /*
1603 * The functions here form a bridge between interpreted code and JNI native
1604 * functions. The basic task is to convert an array of primitives and
1605 * references into C-style function arguments. This is architecture-specific
1606 * and usually requires help from assembly code.
1607 *
1608 * The bridge takes four arguments: the array of parameters, a place to
1609 * store the function result (if any), the method to call, and a pointer
1610 * to the current thread.
1611 *
1612 * These functions aren't called directly from elsewhere in the VM.
1613 * A pointer in the Method struct points to one of these, and when a native
1614 * method is invoked the interpreter jumps to it.
1615 *
1616 * (The "internal native" methods are invoked the same way, but instead
1617 * of calling through a bridge, the target method is called directly.)
1618 *
1619 * The "args" array should not be modified, but we do so anyway for
1620 * performance reasons. We know that it points to the "outs" area on
1621 * the current method's interpreted stack. This area is ignored by the
1622 * precise GC, because there is no register map for a native method (for
1623 * an interpreted method the args would be listed in the argument set).
1624 * We know all of the values exist elsewhere on the interpreted stack,
1625 * because the method call setup copies them right before making the call,
1626 * so we don't have to worry about concealing stuff from the GC.
1627 *
1628 * If we don't want to modify "args", we either have to create a local
1629 * copy and modify it before calling dvmPlatformInvoke, or we have to do
1630 * the local reference replacement within dvmPlatformInvoke. The latter
1631 * has some performance advantages, though if we can inline the local
1632 * reference adds we may win when there's a lot of reference args (unless
1633 * we want to code up some local ref table manipulation in assembly.
1634 */
1635
1636 /*
1637 * If necessary, convert the value in pResult from a local/global reference
1638 * to an object pointer.
1639 */
convertReferenceResult(JNIEnv * env,JValue * pResult,const Method * method,Thread * self)1640 static inline void convertReferenceResult(JNIEnv* env, JValue* pResult,
1641 const Method* method, Thread* self)
1642 {
1643 #ifdef USE_INDIRECT_REF
1644 if (method->shorty[0] == 'L' && !dvmCheckException(self) &&
1645 pResult->l != NULL)
1646 {
1647 pResult->l = dvmDecodeIndirectRef(env, pResult->l);
1648 }
1649 #endif
1650 }
1651
1652 /*
1653 * General form, handles all cases.
1654 */
dvmCallJNIMethod_general(const u4 * args,JValue * pResult,const Method * method,Thread * self)1655 void dvmCallJNIMethod_general(const u4* args, JValue* pResult,
1656 const Method* method, Thread* self)
1657 {
1658 int oldStatus;
1659 u4* modArgs = (u4*) args;
1660 jclass staticMethodClass;
1661 JNIEnv* env = self->jniEnv;
1662
1663 //LOGI("JNI calling %p (%s.%s:%s):\n", method->insns,
1664 // method->clazz->descriptor, method->name, method->shorty);
1665
1666 #ifdef USE_INDIRECT_REF
1667 /*
1668 * Walk the argument list, creating local references for appropriate
1669 * arguments.
1670 */
1671 int idx = 0;
1672 if (dvmIsStaticMethod(method)) {
1673 /* add the class object we pass in */
1674 staticMethodClass = addLocalReference(env, (Object*) method->clazz);
1675 if (staticMethodClass == NULL) {
1676 assert(dvmCheckException(self));
1677 return;
1678 }
1679 } else {
1680 /* add "this" */
1681 staticMethodClass = NULL;
1682 jobject thisObj = addLocalReference(env, (Object*) modArgs[0]);
1683 if (thisObj == NULL) {
1684 assert(dvmCheckException(self));
1685 return;
1686 }
1687 modArgs[idx] = (u4) thisObj;
1688 idx = 1;
1689 }
1690
1691 const char* shorty = &method->shorty[1]; /* skip return type */
1692 while (*shorty != '\0') {
1693 switch (*shorty++) {
1694 case 'L':
1695 //LOGI(" local %d: 0x%08x\n", idx, modArgs[idx]);
1696 if (modArgs[idx] != 0) {
1697 //if (!dvmIsValidObject((Object*) modArgs[idx]))
1698 // dvmAbort();
1699 jobject argObj = addLocalReference(env, (Object*) modArgs[idx]);
1700 if (argObj == NULL) {
1701 assert(dvmCheckException(self));
1702 return;
1703 }
1704 modArgs[idx] = (u4) argObj;
1705 }
1706 break;
1707 case 'D':
1708 case 'J':
1709 idx++;
1710 break;
1711 default:
1712 /* Z B C S I -- do nothing */
1713 break;
1714 }
1715
1716 idx++;
1717 }
1718 #else
1719 staticMethodClass = dvmIsStaticMethod(method) ?
1720 (jclass) method->clazz : NULL;
1721 #endif
1722
1723 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
1724
1725 ANDROID_MEMBAR_FULL(); /* guarantee ordering on method->insns */
1726 assert(method->insns != NULL);
1727
1728 COMPUTE_STACK_SUM(self);
1729 dvmPlatformInvoke(env, staticMethodClass,
1730 method->jniArgInfo, method->insSize, modArgs, method->shorty,
1731 (void*)method->insns, pResult);
1732 CHECK_STACK_SUM(self);
1733
1734 dvmChangeStatus(self, oldStatus);
1735
1736 convertReferenceResult(env, pResult, method, self);
1737 }
1738
1739 /*
1740 * Handler for the unusual case of a synchronized native method.
1741 *
1742 * Lock the object, then call through the general function.
1743 */
dvmCallJNIMethod_synchronized(const u4 * args,JValue * pResult,const Method * method,Thread * self)1744 void dvmCallJNIMethod_synchronized(const u4* args, JValue* pResult,
1745 const Method* method, Thread* self)
1746 {
1747 Object* lockObj;
1748
1749 assert(dvmIsSynchronizedMethod(method));
1750
1751 if (dvmIsStaticMethod(method))
1752 lockObj = (Object*) method->clazz;
1753 else
1754 lockObj = (Object*) args[0];
1755
1756 LOGVV("Calling %s.%s: locking %p (%s)\n",
1757 method->clazz->descriptor, method->name,
1758 lockObj, lockObj->clazz->descriptor);
1759
1760 dvmLockObject(self, lockObj);
1761 dvmCallJNIMethod_general(args, pResult, method, self);
1762 dvmUnlockObject(self, lockObj);
1763 }
1764
1765 /*
1766 * Virtual method call, no reference arguments.
1767 *
1768 * We need to local-ref the "this" argument, found in args[0].
1769 */
dvmCallJNIMethod_virtualNoRef(const u4 * args,JValue * pResult,const Method * method,Thread * self)1770 void dvmCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult,
1771 const Method* method, Thread* self)
1772 {
1773 u4* modArgs = (u4*) args;
1774 int oldStatus;
1775
1776 #ifdef USE_INDIRECT_REF
1777 jobject thisObj = addLocalReference(self->jniEnv, (Object*) args[0]);
1778 if (thisObj == NULL) {
1779 assert(dvmCheckException(self));
1780 return;
1781 }
1782 modArgs[0] = (u4) thisObj;
1783 #endif
1784
1785 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
1786
1787 ANDROID_MEMBAR_FULL(); /* guarantee ordering on method->insns */
1788
1789 COMPUTE_STACK_SUM(self);
1790 dvmPlatformInvoke(self->jniEnv, NULL,
1791 method->jniArgInfo, method->insSize, modArgs, method->shorty,
1792 (void*)method->insns, pResult);
1793 CHECK_STACK_SUM(self);
1794
1795 dvmChangeStatus(self, oldStatus);
1796
1797 convertReferenceResult(self->jniEnv, pResult, method, self);
1798 }
1799
1800 /*
1801 * Static method call, no reference arguments.
1802 *
1803 * We need to local-ref the class reference.
1804 */
dvmCallJNIMethod_staticNoRef(const u4 * args,JValue * pResult,const Method * method,Thread * self)1805 void dvmCallJNIMethod_staticNoRef(const u4* args, JValue* pResult,
1806 const Method* method, Thread* self)
1807 {
1808 jclass staticMethodClass;
1809 int oldStatus;
1810
1811 #ifdef USE_INDIRECT_REF
1812 staticMethodClass = addLocalReference(self->jniEnv, (Object*)method->clazz);
1813 if (staticMethodClass == NULL) {
1814 assert(dvmCheckException(self));
1815 return;
1816 }
1817 #else
1818 staticMethodClass = (jobject) method->clazz;
1819 #endif
1820
1821 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
1822
1823 ANDROID_MEMBAR_FULL(); /* guarantee ordering on method->insns */
1824
1825 COMPUTE_STACK_SUM(self);
1826 dvmPlatformInvoke(self->jniEnv, staticMethodClass,
1827 method->jniArgInfo, method->insSize, args, method->shorty,
1828 (void*)method->insns, pResult);
1829 CHECK_STACK_SUM(self);
1830
1831 dvmChangeStatus(self, oldStatus);
1832
1833 convertReferenceResult(self->jniEnv, pResult, method, self);
1834 }
1835
1836 /*
1837 * Extract the return type enum from the "jniArgInfo" field.
1838 */
dvmGetArgInfoReturnType(int jniArgInfo)1839 DalvikJniReturnType dvmGetArgInfoReturnType(int jniArgInfo)
1840 {
1841 return (jniArgInfo & DALVIK_JNI_RETURN_MASK) >> DALVIK_JNI_RETURN_SHIFT;
1842 }
1843
1844
1845 /*
1846 * ===========================================================================
1847 * JNI implementation
1848 * ===========================================================================
1849 */
1850
1851 /*
1852 * Return the version of the native method interface.
1853 */
GetVersion(JNIEnv * env)1854 static jint GetVersion(JNIEnv* env)
1855 {
1856 JNI_ENTER();
1857 /*
1858 * There is absolutely no need to toggle the mode for correct behavior.
1859 * However, it does provide native code with a simple "suspend self
1860 * if necessary" call.
1861 */
1862 JNI_EXIT();
1863 return JNI_VERSION_1_6;
1864 }
1865
1866 /*
1867 * Create a new class from a bag of bytes.
1868 *
1869 * This is not currently supported within Dalvik.
1870 */
DefineClass(JNIEnv * env,const char * name,jobject loader,const jbyte * buf,jsize bufLen)1871 static jclass DefineClass(JNIEnv* env, const char *name, jobject loader,
1872 const jbyte* buf, jsize bufLen)
1873 {
1874 UNUSED_PARAMETER(name);
1875 UNUSED_PARAMETER(loader);
1876 UNUSED_PARAMETER(buf);
1877 UNUSED_PARAMETER(bufLen);
1878
1879 JNI_ENTER();
1880 LOGW("JNI DefineClass is not supported\n");
1881 JNI_EXIT();
1882 return NULL;
1883 }
1884
1885 /*
1886 * Find a class by name.
1887 *
1888 * We have to use the "no init" version of FindClass here, because we might
1889 * be getting the class prior to registering native methods that will be
1890 * used in <clinit>.
1891 *
1892 * We need to get the class loader associated with the current native
1893 * method. If there is no native method, e.g. we're calling this from native
1894 * code right after creating the VM, the spec says we need to use the class
1895 * loader returned by "ClassLoader.getBaseClassLoader". There is no such
1896 * method, but it's likely they meant ClassLoader.getSystemClassLoader.
1897 * We can't get that until after the VM has initialized though.
1898 */
FindClass(JNIEnv * env,const char * name)1899 static jclass FindClass(JNIEnv* env, const char* name)
1900 {
1901 JNI_ENTER();
1902
1903 const Method* thisMethod;
1904 ClassObject* clazz;
1905 jclass jclazz = NULL;
1906 Object* loader;
1907 char* descriptor = NULL;
1908
1909 thisMethod = dvmGetCurrentJNIMethod();
1910 assert(thisMethod != NULL);
1911
1912 descriptor = dvmNameToDescriptor(name);
1913 if (descriptor == NULL) {
1914 clazz = NULL;
1915 goto bail;
1916 }
1917
1918 //Thread* self = dvmThreadSelf();
1919 if (_self->classLoaderOverride != NULL) {
1920 /* hack for JNI_OnLoad */
1921 assert(strcmp(thisMethod->name, "nativeLoad") == 0);
1922 loader = _self->classLoaderOverride;
1923 } else if (thisMethod == gDvm.methFakeNativeEntry) {
1924 /* start point of invocation interface */
1925 if (!gDvm.initializing)
1926 loader = dvmGetSystemClassLoader();
1927 else
1928 loader = NULL;
1929 } else {
1930 loader = thisMethod->clazz->classLoader;
1931 }
1932
1933 clazz = dvmFindClassNoInit(descriptor, loader);
1934 jclazz = addLocalReference(env, (Object*) clazz);
1935
1936 bail:
1937 free(descriptor);
1938
1939 JNI_EXIT();
1940 return jclazz;
1941 }
1942
1943 /*
1944 * Return the superclass of a class.
1945 */
GetSuperclass(JNIEnv * env,jclass jclazz)1946 static jclass GetSuperclass(JNIEnv* env, jclass jclazz)
1947 {
1948 JNI_ENTER();
1949 jclass jsuper = NULL;
1950
1951 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1952 if (clazz != NULL)
1953 jsuper = addLocalReference(env, (Object*)clazz->super);
1954 JNI_EXIT();
1955 return jsuper;
1956 }
1957
1958 /*
1959 * Determine whether an object of clazz1 can be safely cast to clazz2.
1960 *
1961 * Like IsInstanceOf, but with a pair of class objects instead of obj+class.
1962 */
IsAssignableFrom(JNIEnv * env,jclass jclazz1,jclass jclazz2)1963 static jboolean IsAssignableFrom(JNIEnv* env, jclass jclazz1, jclass jclazz2)
1964 {
1965 JNI_ENTER();
1966
1967 ClassObject* clazz1 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz1);
1968 ClassObject* clazz2 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz2);
1969
1970 jboolean result = dvmInstanceof(clazz1, clazz2);
1971
1972 JNI_EXIT();
1973 return result;
1974 }
1975
1976 /*
1977 * Given a java.lang.reflect.Method or .Constructor, return a methodID.
1978 */
FromReflectedMethod(JNIEnv * env,jobject jmethod)1979 static jmethodID FromReflectedMethod(JNIEnv* env, jobject jmethod)
1980 {
1981 JNI_ENTER();
1982 jmethodID methodID;
1983 Object* method = dvmDecodeIndirectRef(env, jmethod);
1984 methodID = (jmethodID) dvmGetMethodFromReflectObj(method);
1985 JNI_EXIT();
1986 return methodID;
1987 }
1988
1989 /*
1990 * Given a java.lang.reflect.Field, return a fieldID.
1991 */
FromReflectedField(JNIEnv * env,jobject jfield)1992 static jfieldID FromReflectedField(JNIEnv* env, jobject jfield)
1993 {
1994 JNI_ENTER();
1995 jfieldID fieldID;
1996 Object* field = dvmDecodeIndirectRef(env, jfield);
1997 fieldID = (jfieldID) dvmGetFieldFromReflectObj(field);
1998 JNI_EXIT();
1999 return fieldID;
2000 }
2001
2002 /*
2003 * Convert a methodID to a java.lang.reflect.Method or .Constructor.
2004 *
2005 * (The "isStatic" field does not appear in the spec.)
2006 *
2007 * Throws OutOfMemory and returns NULL on failure.
2008 */
ToReflectedMethod(JNIEnv * env,jclass jcls,jmethodID methodID,jboolean isStatic)2009 static jobject ToReflectedMethod(JNIEnv* env, jclass jcls, jmethodID methodID,
2010 jboolean isStatic)
2011 {
2012 JNI_ENTER();
2013 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
2014 Object* obj = dvmCreateReflectObjForMethod(clazz, (Method*) methodID);
2015 dvmReleaseTrackedAlloc(obj, NULL);
2016 jobject jobj = addLocalReference(env, obj);
2017 JNI_EXIT();
2018 return jobj;
2019 }
2020
2021 /*
2022 * Convert a fieldID to a java.lang.reflect.Field.
2023 *
2024 * (The "isStatic" field does not appear in the spec.)
2025 *
2026 * Throws OutOfMemory and returns NULL on failure.
2027 */
ToReflectedField(JNIEnv * env,jclass jcls,jfieldID fieldID,jboolean isStatic)2028 static jobject ToReflectedField(JNIEnv* env, jclass jcls, jfieldID fieldID,
2029 jboolean isStatic)
2030 {
2031 JNI_ENTER();
2032 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
2033 Object* obj = dvmCreateReflectObjForField(clazz, (Field*) fieldID);
2034 dvmReleaseTrackedAlloc(obj, NULL);
2035 jobject jobj = addLocalReference(env, obj);
2036 JNI_EXIT();
2037 return jobj;
2038 }
2039
2040 /*
2041 * Take this exception and throw it.
2042 */
Throw(JNIEnv * env,jthrowable jobj)2043 static jint Throw(JNIEnv* env, jthrowable jobj)
2044 {
2045 JNI_ENTER();
2046
2047 jint retval;
2048
2049 if (jobj != NULL) {
2050 Object* obj = dvmDecodeIndirectRef(env, jobj);
2051 dvmSetException(_self, obj);
2052 retval = JNI_OK;
2053 } else {
2054 retval = JNI_ERR;
2055 }
2056
2057 JNI_EXIT();
2058 return retval;
2059 }
2060
2061 /*
2062 * Constructs an exception object from the specified class with the message
2063 * specified by "message", and throws it.
2064 */
ThrowNew(JNIEnv * env,jclass jclazz,const char * message)2065 static jint ThrowNew(JNIEnv* env, jclass jclazz, const char* message)
2066 {
2067 JNI_ENTER();
2068
2069 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2070 dvmThrowExceptionByClass(clazz, message);
2071 // TODO: should return failure if this didn't work (e.g. OOM)
2072
2073 JNI_EXIT();
2074 return JNI_OK;
2075 }
2076
2077 /*
2078 * If an exception is being thrown, return the exception object. Otherwise,
2079 * return NULL.
2080 *
2081 * TODO: if there is no pending exception, we should be able to skip the
2082 * enter/exit checks. If we find one, we need to enter and then re-fetch
2083 * the exception (in case it got moved by a compacting GC).
2084 */
ExceptionOccurred(JNIEnv * env)2085 static jthrowable ExceptionOccurred(JNIEnv* env)
2086 {
2087 JNI_ENTER();
2088
2089 Object* exception;
2090 jobject localException;
2091
2092 exception = dvmGetException(_self);
2093 localException = addLocalReference(env, exception);
2094 if (localException == NULL && exception != NULL) {
2095 /*
2096 * We were unable to add a new local reference, and threw a new
2097 * exception. We can't return "exception", because it's not a
2098 * local reference. So we have to return NULL, indicating that
2099 * there was no exception, even though it's pretty much raining
2100 * exceptions in here.
2101 */
2102 LOGW("JNI WARNING: addLocal/exception combo\n");
2103 }
2104
2105 JNI_EXIT();
2106 return localException;
2107 }
2108
2109 /*
2110 * Print an exception and stack trace to stderr.
2111 */
ExceptionDescribe(JNIEnv * env)2112 static void ExceptionDescribe(JNIEnv* env)
2113 {
2114 JNI_ENTER();
2115
2116 Object* exception = dvmGetException(_self);
2117 if (exception != NULL) {
2118 dvmPrintExceptionStackTrace();
2119 } else {
2120 LOGI("Odd: ExceptionDescribe called, but no exception pending\n");
2121 }
2122
2123 JNI_EXIT();
2124 }
2125
2126 /*
2127 * Clear the exception currently being thrown.
2128 *
2129 * TODO: we should be able to skip the enter/exit stuff.
2130 */
ExceptionClear(JNIEnv * env)2131 static void ExceptionClear(JNIEnv* env)
2132 {
2133 JNI_ENTER();
2134 dvmClearException(_self);
2135 JNI_EXIT();
2136 }
2137
2138 /*
2139 * Kill the VM. This function does not return.
2140 */
FatalError(JNIEnv * env,const char * msg)2141 static void FatalError(JNIEnv* env, const char* msg)
2142 {
2143 //dvmChangeStatus(NULL, THREAD_RUNNING);
2144 LOGE("JNI posting fatal error: %s\n", msg);
2145 dvmAbort();
2146 }
2147
2148 /*
2149 * Push a new JNI frame on the stack, with a new set of locals.
2150 *
2151 * The new frame must have the same method pointer. (If for no other
2152 * reason than FindClass needs it to get the appropriate class loader.)
2153 */
PushLocalFrame(JNIEnv * env,jint capacity)2154 static jint PushLocalFrame(JNIEnv* env, jint capacity)
2155 {
2156 JNI_ENTER();
2157 int result = JNI_OK;
2158 if (!ensureLocalCapacity(env, capacity) ||
2159 !dvmPushLocalFrame(_self /*dvmThreadSelf()*/, dvmGetCurrentJNIMethod()))
2160 {
2161 /* yes, OutOfMemoryError, not StackOverflowError */
2162 dvmClearException(_self);
2163 dvmThrowException("Ljava/lang/OutOfMemoryError;",
2164 "out of stack in JNI PushLocalFrame");
2165 result = JNI_ERR;
2166 }
2167 JNI_EXIT();
2168 return result;
2169 }
2170
2171 /*
2172 * Pop the local frame off. If "result" is not null, add it as a
2173 * local reference on the now-current frame.
2174 */
PopLocalFrame(JNIEnv * env,jobject jresult)2175 static jobject PopLocalFrame(JNIEnv* env, jobject jresult)
2176 {
2177 JNI_ENTER();
2178 Object* result = dvmDecodeIndirectRef(env, jresult);
2179 if (!dvmPopLocalFrame(_self /*dvmThreadSelf()*/)) {
2180 LOGW("JNI WARNING: too many PopLocalFrame calls\n");
2181 dvmClearException(_self);
2182 dvmThrowException("Ljava/lang/RuntimeException;",
2183 "too many PopLocalFrame calls");
2184 }
2185 jresult = addLocalReference(env, result);
2186 JNI_EXIT();
2187 return result;
2188 }
2189
2190 /*
2191 * Add a reference to the global list.
2192 */
NewGlobalRef(JNIEnv * env,jobject jobj)2193 static jobject NewGlobalRef(JNIEnv* env, jobject jobj)
2194 {
2195 Object* obj;
2196
2197 JNI_ENTER();
2198 if (dvmIsWeakGlobalRef(jobj))
2199 obj = getPhantomReferent(env, (jweak) jobj);
2200 else
2201 obj = dvmDecodeIndirectRef(env, jobj);
2202 jobject retval = addGlobalReference(obj);
2203 JNI_EXIT();
2204 return retval;
2205 }
2206
2207 /*
2208 * Delete a reference from the global list.
2209 */
DeleteGlobalRef(JNIEnv * env,jobject jglobalRef)2210 static void DeleteGlobalRef(JNIEnv* env, jobject jglobalRef)
2211 {
2212 JNI_ENTER();
2213 deleteGlobalReference(jglobalRef);
2214 JNI_EXIT();
2215 }
2216
2217
2218 /*
2219 * Add a reference to the local list.
2220 */
NewLocalRef(JNIEnv * env,jobject jobj)2221 static jobject NewLocalRef(JNIEnv* env, jobject jobj)
2222 {
2223 Object* obj;
2224
2225 JNI_ENTER();
2226 if (dvmIsWeakGlobalRef(jobj))
2227 obj = getPhantomReferent(env, (jweak) jobj);
2228 else
2229 obj = dvmDecodeIndirectRef(env, jobj);
2230 jobject retval = addLocalReference(env, obj);
2231 JNI_EXIT();
2232 return retval;
2233 }
2234
2235 /*
2236 * Delete a reference from the local list.
2237 */
DeleteLocalRef(JNIEnv * env,jobject jlocalRef)2238 static void DeleteLocalRef(JNIEnv* env, jobject jlocalRef)
2239 {
2240 JNI_ENTER();
2241 deleteLocalReference(env, jlocalRef);
2242 JNI_EXIT();
2243 }
2244
2245 /*
2246 * Ensure that the local references table can hold at least this many
2247 * references.
2248 */
EnsureLocalCapacity(JNIEnv * env,jint capacity)2249 static jint EnsureLocalCapacity(JNIEnv* env, jint capacity)
2250 {
2251 JNI_ENTER();
2252 bool okay = ensureLocalCapacity(env, capacity);
2253 if (!okay) {
2254 dvmThrowException("Ljava/lang/OutOfMemoryError;",
2255 "can't ensure local reference capacity");
2256 }
2257 JNI_EXIT();
2258 if (okay)
2259 return 0;
2260 else
2261 return -1;
2262 }
2263
2264
2265 /*
2266 * Determine whether two Object references refer to the same underlying object.
2267 */
IsSameObject(JNIEnv * env,jobject jref1,jobject jref2)2268 static jboolean IsSameObject(JNIEnv* env, jobject jref1, jobject jref2)
2269 {
2270 JNI_ENTER();
2271 Object* obj1 = dvmDecodeIndirectRef(env, jref1);
2272 Object* obj2 = dvmDecodeIndirectRef(env, jref2);
2273 jboolean result = (obj1 == obj2);
2274 JNI_EXIT();
2275 return result;
2276 }
2277
2278 /*
2279 * Allocate a new object without invoking any constructors.
2280 */
AllocObject(JNIEnv * env,jclass jclazz)2281 static jobject AllocObject(JNIEnv* env, jclass jclazz)
2282 {
2283 JNI_ENTER();
2284
2285 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2286 jobject result;
2287
2288 if (!canAllocClass(clazz) ||
2289 (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)))
2290 {
2291 assert(dvmCheckException(_self));
2292 result = NULL;
2293 } else {
2294 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2295 result = addLocalReference(env, newObj);
2296 }
2297
2298 JNI_EXIT();
2299 return result;
2300 }
2301
2302 /*
2303 * Allocate a new object and invoke the supplied constructor.
2304 */
NewObject(JNIEnv * env,jclass jclazz,jmethodID methodID,...)2305 static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...)
2306 {
2307 JNI_ENTER();
2308 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2309 jobject result;
2310
2311 if (!canAllocClass(clazz) ||
2312 (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)))
2313 {
2314 assert(dvmCheckException(_self));
2315 result = NULL;
2316 } else {
2317 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2318 result = addLocalReference(env, newObj);
2319 if (newObj != NULL) {
2320 JValue unused;
2321 va_list args;
2322 va_start(args, methodID);
2323 dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused,
2324 args);
2325 va_end(args);
2326 }
2327 }
2328
2329 JNI_EXIT();
2330 return result;
2331 }
NewObjectV(JNIEnv * env,jclass jclazz,jmethodID methodID,va_list args)2332 static jobject NewObjectV(JNIEnv* env, jclass jclazz, jmethodID methodID,
2333 va_list args)
2334 {
2335 JNI_ENTER();
2336 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2337 jobject result;
2338
2339 if (!canAllocClass(clazz) ||
2340 (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)))
2341 {
2342 assert(dvmCheckException(_self));
2343 result = NULL;
2344 } else {
2345 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2346 result = addLocalReference(env, newObj);
2347 if (newObj != NULL) {
2348 JValue unused;
2349 dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused,
2350 args);
2351 }
2352 }
2353
2354 JNI_EXIT();
2355 return result;
2356 }
NewObjectA(JNIEnv * env,jclass jclazz,jmethodID methodID,jvalue * args)2357 static jobject NewObjectA(JNIEnv* env, jclass jclazz, jmethodID methodID,
2358 jvalue* args)
2359 {
2360 JNI_ENTER();
2361 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2362 jobject result;
2363
2364 if (!canAllocClass(clazz) ||
2365 (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)))
2366 {
2367 assert(dvmCheckException(_self));
2368 result = NULL;
2369 } else {
2370 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2371 result = addLocalReference(env, newObj);
2372 if (newObj != NULL) {
2373 JValue unused;
2374 dvmCallMethodA(_self, (Method*) methodID, newObj, true, &unused,
2375 args);
2376 }
2377 }
2378
2379 JNI_EXIT();
2380 return result;
2381 }
2382
2383 /*
2384 * Returns the class of an object.
2385 *
2386 * JNI spec says: obj must not be NULL.
2387 */
GetObjectClass(JNIEnv * env,jobject jobj)2388 static jclass GetObjectClass(JNIEnv* env, jobject jobj)
2389 {
2390 JNI_ENTER();
2391
2392 assert(jobj != NULL);
2393
2394 Object* obj = dvmDecodeIndirectRef(env, jobj);
2395 jclass jclazz = addLocalReference(env, (Object*) obj->clazz);
2396
2397 JNI_EXIT();
2398 return jclazz;
2399 }
2400
2401 /*
2402 * Determine whether "obj" is an instance of "clazz".
2403 */
IsInstanceOf(JNIEnv * env,jobject jobj,jclass jclazz)2404 static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass jclazz)
2405 {
2406 JNI_ENTER();
2407
2408 assert(jclazz != NULL);
2409
2410 jboolean result;
2411
2412 if (jobj == NULL) {
2413 result = true;
2414 } else {
2415 Object* obj = dvmDecodeIndirectRef(env, jobj);
2416 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2417 result = dvmInstanceof(obj->clazz, clazz);
2418 }
2419
2420 JNI_EXIT();
2421 return result;
2422 }
2423
2424 /*
2425 * Get a method ID for an instance method.
2426 *
2427 * JNI defines <init> as an instance method, but Dalvik considers it a
2428 * "direct" method, so we have to special-case it here.
2429 *
2430 * Dalvik also puts all private methods into the "direct" list, so we
2431 * really need to just search both lists.
2432 */
GetMethodID(JNIEnv * env,jclass jclazz,const char * name,const char * sig)2433 static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name,
2434 const char* sig)
2435 {
2436 JNI_ENTER();
2437
2438 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2439 jmethodID id = NULL;
2440
2441 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2442 assert(dvmCheckException(_self));
2443 } else {
2444 Method* meth;
2445
2446 meth = dvmFindVirtualMethodHierByDescriptor(clazz, name, sig);
2447 if (meth == NULL) {
2448 /* search private methods and constructors; non-hierarchical */
2449 meth = dvmFindDirectMethodByDescriptor(clazz, name, sig);
2450 }
2451 if (meth != NULL && dvmIsStaticMethod(meth)) {
2452 IF_LOGD() {
2453 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2454 LOGD("GetMethodID: not returning static method %s.%s %s\n",
2455 clazz->descriptor, meth->name, desc);
2456 free(desc);
2457 }
2458 meth = NULL;
2459 }
2460 if (meth == NULL) {
2461 LOGD("GetMethodID: method not found: %s.%s:%s\n",
2462 clazz->descriptor, name, sig);
2463 dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
2464 }
2465
2466 /*
2467 * The method's class may not be the same as clazz, but if
2468 * it isn't this must be a virtual method and the class must
2469 * be a superclass (and, hence, already initialized).
2470 */
2471 if (meth != NULL) {
2472 assert(dvmIsClassInitialized(meth->clazz) ||
2473 dvmIsClassInitializing(meth->clazz));
2474 }
2475 id = (jmethodID) meth;
2476 }
2477 JNI_EXIT();
2478 return id;
2479 }
2480
2481 /*
2482 * Get a field ID (instance fields).
2483 */
GetFieldID(JNIEnv * env,jclass jclazz,const char * name,const char * sig)2484 static jfieldID GetFieldID(JNIEnv* env, jclass jclazz,
2485 const char* name, const char* sig)
2486 {
2487 JNI_ENTER();
2488
2489 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2490 jfieldID id;
2491
2492 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2493 assert(dvmCheckException(_self));
2494 id = NULL;
2495 } else {
2496 id = (jfieldID) dvmFindInstanceFieldHier(clazz, name, sig);
2497 if (id == NULL) {
2498 LOGD("GetFieldID: unable to find field %s.%s:%s\n",
2499 clazz->descriptor, name, sig);
2500 dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
2501 }
2502 }
2503 JNI_EXIT();
2504 return id;
2505 }
2506
2507 /*
2508 * Get the method ID for a static method in a class.
2509 */
GetStaticMethodID(JNIEnv * env,jclass jclazz,const char * name,const char * sig)2510 static jmethodID GetStaticMethodID(JNIEnv* env, jclass jclazz,
2511 const char* name, const char* sig)
2512 {
2513 JNI_ENTER();
2514
2515 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2516 jmethodID id;
2517
2518 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2519 assert(dvmCheckException(_self));
2520 id = NULL;
2521 } else {
2522 Method* meth;
2523
2524 meth = dvmFindDirectMethodHierByDescriptor(clazz, name, sig);
2525
2526 /* make sure it's static, not virtual+private */
2527 if (meth != NULL && !dvmIsStaticMethod(meth)) {
2528 IF_LOGD() {
2529 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2530 LOGD("GetStaticMethodID: "
2531 "not returning nonstatic method %s.%s %s\n",
2532 clazz->descriptor, meth->name, desc);
2533 free(desc);
2534 }
2535 meth = NULL;
2536 }
2537
2538 id = (jmethodID) meth;
2539 if (id == NULL)
2540 dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
2541 }
2542
2543 JNI_EXIT();
2544 return id;
2545 }
2546
2547 /*
2548 * Get a field ID (static fields).
2549 */
GetStaticFieldID(JNIEnv * env,jclass jclazz,const char * name,const char * sig)2550 static jfieldID GetStaticFieldID(JNIEnv* env, jclass jclazz,
2551 const char* name, const char* sig)
2552 {
2553 JNI_ENTER();
2554
2555 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2556 jfieldID id;
2557
2558 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2559 assert(dvmCheckException(_self));
2560 id = NULL;
2561 } else {
2562 id = (jfieldID) dvmFindStaticField(clazz, name, sig);
2563 if (id == NULL)
2564 dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
2565 }
2566 JNI_EXIT();
2567 return id;
2568 }
2569
2570 /*
2571 * Get a static field.
2572 *
2573 * If we get an object reference, add it to the local refs list.
2574 */
2575 #define GET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
2576 static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
2577 jfieldID fieldID) \
2578 { \
2579 UNUSED_PARAMETER(jclazz); \
2580 JNI_ENTER(); \
2581 StaticField* sfield = (StaticField*) fieldID; \
2582 _ctype value; \
2583 if (dvmIsVolatileField(&sfield->field)) { \
2584 if (_isref) { /* only when _ctype==jobject */ \
2585 Object* obj = dvmGetStaticFieldObjectVolatile(sfield); \
2586 value = (_ctype)(u4)addLocalReference(env, obj); \
2587 } else { \
2588 value = dvmGetStaticField##_jname##Volatile(sfield); \
2589 } \
2590 } else { \
2591 if (_isref) { \
2592 Object* obj = dvmGetStaticFieldObject(sfield); \
2593 value = (_ctype)(u4)addLocalReference(env, obj); \
2594 } else { \
2595 value = dvmGetStaticField##_jname(sfield); \
2596 } \
2597 } \
2598 JNI_EXIT(); \
2599 return value; \
2600 }
2601 GET_STATIC_TYPE_FIELD(jobject, Object, true);
2602 GET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
2603 GET_STATIC_TYPE_FIELD(jbyte, Byte, false);
2604 GET_STATIC_TYPE_FIELD(jchar, Char, false);
2605 GET_STATIC_TYPE_FIELD(jshort, Short, false);
2606 GET_STATIC_TYPE_FIELD(jint, Int, false);
2607 GET_STATIC_TYPE_FIELD(jlong, Long, false);
2608 GET_STATIC_TYPE_FIELD(jfloat, Float, false);
2609 GET_STATIC_TYPE_FIELD(jdouble, Double, false);
2610
2611 /*
2612 * Set a static field.
2613 */
2614 #define SET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
2615 static void SetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
2616 jfieldID fieldID, _ctype value) \
2617 { \
2618 UNUSED_PARAMETER(jclazz); \
2619 JNI_ENTER(); \
2620 StaticField* sfield = (StaticField*) fieldID; \
2621 if (dvmIsVolatileField(&sfield->field)) { \
2622 if (_isref) { /* only when _ctype==jobject */ \
2623 Object* valObj = \
2624 dvmDecodeIndirectRef(env, (jobject)(u4)value); \
2625 dvmSetStaticFieldObjectVolatile(sfield, valObj); \
2626 } else { \
2627 dvmSetStaticField##_jname##Volatile(sfield, value); \
2628 } \
2629 } else { \
2630 if (_isref) { \
2631 Object* valObj = \
2632 dvmDecodeIndirectRef(env, (jobject)(u4)value); \
2633 dvmSetStaticFieldObject(sfield, valObj); \
2634 } else { \
2635 dvmSetStaticField##_jname(sfield, value); \
2636 } \
2637 } \
2638 JNI_EXIT(); \
2639 }
2640 SET_STATIC_TYPE_FIELD(jobject, Object, true);
2641 SET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
2642 SET_STATIC_TYPE_FIELD(jbyte, Byte, false);
2643 SET_STATIC_TYPE_FIELD(jchar, Char, false);
2644 SET_STATIC_TYPE_FIELD(jshort, Short, false);
2645 SET_STATIC_TYPE_FIELD(jint, Int, false);
2646 SET_STATIC_TYPE_FIELD(jlong, Long, false);
2647 SET_STATIC_TYPE_FIELD(jfloat, Float, false);
2648 SET_STATIC_TYPE_FIELD(jdouble, Double, false);
2649
2650 /*
2651 * Get an instance field.
2652 *
2653 * If we get an object reference, add it to the local refs list.
2654 */
2655 #define GET_TYPE_FIELD(_ctype, _jname, _isref) \
2656 static _ctype Get##_jname##Field(JNIEnv* env, jobject jobj, \
2657 jfieldID fieldID) \
2658 { \
2659 JNI_ENTER(); \
2660 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2661 InstField* field = (InstField*) fieldID; \
2662 _ctype value; \
2663 if (dvmIsVolatileField(&field->field)) { \
2664 if (_isref) { /* only when _ctype==jobject */ \
2665 Object* valObj = \
2666 dvmGetFieldObjectVolatile(obj, field->byteOffset); \
2667 value = (_ctype)(u4)addLocalReference(env, valObj); \
2668 } else { \
2669 value = \
2670 dvmGetField##_jname##Volatile(obj, field->byteOffset); \
2671 } \
2672 } else { \
2673 if (_isref) { \
2674 Object* valObj = dvmGetFieldObject(obj, field->byteOffset); \
2675 value = (_ctype)(u4)addLocalReference(env, valObj); \
2676 } else { \
2677 value = dvmGetField##_jname(obj, field->byteOffset); \
2678 } \
2679 } \
2680 JNI_EXIT(); \
2681 return value; \
2682 }
2683 GET_TYPE_FIELD(jobject, Object, true);
2684 GET_TYPE_FIELD(jboolean, Boolean, false);
2685 GET_TYPE_FIELD(jbyte, Byte, false);
2686 GET_TYPE_FIELD(jchar, Char, false);
2687 GET_TYPE_FIELD(jshort, Short, false);
2688 GET_TYPE_FIELD(jint, Int, false);
2689 GET_TYPE_FIELD(jlong, Long, false);
2690 GET_TYPE_FIELD(jfloat, Float, false);
2691 GET_TYPE_FIELD(jdouble, Double, false);
2692
2693 /*
2694 * Set an instance field.
2695 */
2696 #define SET_TYPE_FIELD(_ctype, _jname, _isref) \
2697 static void Set##_jname##Field(JNIEnv* env, jobject jobj, \
2698 jfieldID fieldID, _ctype value) \
2699 { \
2700 JNI_ENTER(); \
2701 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2702 InstField* field = (InstField*) fieldID; \
2703 if (dvmIsVolatileField(&field->field)) { \
2704 if (_isref) { /* only when _ctype==jobject */ \
2705 Object* valObj = \
2706 dvmDecodeIndirectRef(env, (jobject)(u4)value); \
2707 dvmSetFieldObjectVolatile(obj, field->byteOffset, valObj); \
2708 } else { \
2709 dvmSetField##_jname##Volatile(obj, \
2710 field->byteOffset, value); \
2711 } \
2712 } else { \
2713 if (_isref) { \
2714 Object* valObj = \
2715 dvmDecodeIndirectRef(env, (jobject)(u4)value); \
2716 dvmSetFieldObject(obj, field->byteOffset, valObj); \
2717 } else { \
2718 dvmSetField##_jname(obj, field->byteOffset, value); \
2719 } \
2720 } \
2721 JNI_EXIT(); \
2722 }
2723 SET_TYPE_FIELD(jobject, Object, true);
2724 SET_TYPE_FIELD(jboolean, Boolean, false);
2725 SET_TYPE_FIELD(jbyte, Byte, false);
2726 SET_TYPE_FIELD(jchar, Char, false);
2727 SET_TYPE_FIELD(jshort, Short, false);
2728 SET_TYPE_FIELD(jint, Int, false);
2729 SET_TYPE_FIELD(jlong, Long, false);
2730 SET_TYPE_FIELD(jfloat, Float, false);
2731 SET_TYPE_FIELD(jdouble, Double, false);
2732
2733 /*
2734 * Make a virtual method call.
2735 *
2736 * Three versions (..., va_list, jvalue[]) for each return type. If we're
2737 * returning an Object, we have to add it to the local references table.
2738 */
2739 #define CALL_VIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
2740 static _ctype Call##_jname##Method(JNIEnv* env, jobject jobj, \
2741 jmethodID methodID, ...) \
2742 { \
2743 JNI_ENTER(); \
2744 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2745 const Method* meth; \
2746 va_list args; \
2747 JValue result; \
2748 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
2749 if (meth == NULL) { \
2750 JNI_EXIT(); \
2751 return _retfail; \
2752 } \
2753 va_start(args, methodID); \
2754 dvmCallMethodV(_self, meth, obj, true, &result, args); \
2755 va_end(args); \
2756 if (_isref && !dvmCheckException(_self)) \
2757 result.l = addLocalReference(env, result.l); \
2758 JNI_EXIT(); \
2759 return _retok; \
2760 } \
2761 static _ctype Call##_jname##MethodV(JNIEnv* env, jobject jobj, \
2762 jmethodID methodID, va_list args) \
2763 { \
2764 JNI_ENTER(); \
2765 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2766 const Method* meth; \
2767 JValue result; \
2768 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
2769 if (meth == NULL) { \
2770 JNI_EXIT(); \
2771 return _retfail; \
2772 } \
2773 dvmCallMethodV(_self, meth, obj, true, &result, args); \
2774 if (_isref && !dvmCheckException(_self)) \
2775 result.l = addLocalReference(env, result.l); \
2776 JNI_EXIT(); \
2777 return _retok; \
2778 } \
2779 static _ctype Call##_jname##MethodA(JNIEnv* env, jobject jobj, \
2780 jmethodID methodID, jvalue* args) \
2781 { \
2782 JNI_ENTER(); \
2783 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2784 const Method* meth; \
2785 JValue result; \
2786 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
2787 if (meth == NULL) { \
2788 JNI_EXIT(); \
2789 return _retfail; \
2790 } \
2791 dvmCallMethodA(_self, meth, obj, true, &result, args); \
2792 if (_isref && !dvmCheckException(_self)) \
2793 result.l = addLocalReference(env, result.l); \
2794 JNI_EXIT(); \
2795 return _retok; \
2796 }
2797 CALL_VIRTUAL(jobject, Object, NULL, result.l, true);
2798 CALL_VIRTUAL(jboolean, Boolean, 0, result.z, false);
2799 CALL_VIRTUAL(jbyte, Byte, 0, result.b, false);
2800 CALL_VIRTUAL(jchar, Char, 0, result.c, false);
2801 CALL_VIRTUAL(jshort, Short, 0, result.s, false);
2802 CALL_VIRTUAL(jint, Int, 0, result.i, false);
2803 CALL_VIRTUAL(jlong, Long, 0, result.j, false);
2804 CALL_VIRTUAL(jfloat, Float, 0.0f, result.f, false);
2805 CALL_VIRTUAL(jdouble, Double, 0.0, result.d, false);
2806 CALL_VIRTUAL(void, Void, , , false);
2807
2808 /*
2809 * Make a "non-virtual" method call. We're still calling a virtual method,
2810 * but this time we're not doing an indirection through the object's vtable.
2811 * The "clazz" parameter defines which implementation of a method we want.
2812 *
2813 * Three versions (..., va_list, jvalue[]) for each return type.
2814 */
2815 #define CALL_NONVIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
2816 static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, jobject jobj, \
2817 jclass jclazz, jmethodID methodID, ...) \
2818 { \
2819 JNI_ENTER(); \
2820 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2821 ClassObject* clazz = \
2822 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
2823 const Method* meth; \
2824 va_list args; \
2825 JValue result; \
2826 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
2827 if (meth == NULL) { \
2828 JNI_EXIT(); \
2829 return _retfail; \
2830 } \
2831 va_start(args, methodID); \
2832 dvmCallMethodV(_self, meth, obj, true, &result, args); \
2833 if (_isref && !dvmCheckException(_self)) \
2834 result.l = addLocalReference(env, result.l); \
2835 va_end(args); \
2836 JNI_EXIT(); \
2837 return _retok; \
2838 } \
2839 static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject jobj,\
2840 jclass jclazz, jmethodID methodID, va_list args) \
2841 { \
2842 JNI_ENTER(); \
2843 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2844 ClassObject* clazz = \
2845 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
2846 const Method* meth; \
2847 JValue result; \
2848 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
2849 if (meth == NULL) { \
2850 JNI_EXIT(); \
2851 return _retfail; \
2852 } \
2853 dvmCallMethodV(_self, meth, obj, true, &result, args); \
2854 if (_isref && !dvmCheckException(_self)) \
2855 result.l = addLocalReference(env, result.l); \
2856 JNI_EXIT(); \
2857 return _retok; \
2858 } \
2859 static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject jobj,\
2860 jclass jclazz, jmethodID methodID, jvalue* args) \
2861 { \
2862 JNI_ENTER(); \
2863 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2864 ClassObject* clazz = \
2865 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
2866 const Method* meth; \
2867 JValue result; \
2868 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
2869 if (meth == NULL) { \
2870 JNI_EXIT(); \
2871 return _retfail; \
2872 } \
2873 dvmCallMethodA(_self, meth, obj, true, &result, args); \
2874 if (_isref && !dvmCheckException(_self)) \
2875 result.l = addLocalReference(env, result.l); \
2876 JNI_EXIT(); \
2877 return _retok; \
2878 }
2879 CALL_NONVIRTUAL(jobject, Object, NULL, result.l, true);
2880 CALL_NONVIRTUAL(jboolean, Boolean, 0, result.z, false);
2881 CALL_NONVIRTUAL(jbyte, Byte, 0, result.b, false);
2882 CALL_NONVIRTUAL(jchar, Char, 0, result.c, false);
2883 CALL_NONVIRTUAL(jshort, Short, 0, result.s, false);
2884 CALL_NONVIRTUAL(jint, Int, 0, result.i, false);
2885 CALL_NONVIRTUAL(jlong, Long, 0, result.j, false);
2886 CALL_NONVIRTUAL(jfloat, Float, 0.0f, result.f, false);
2887 CALL_NONVIRTUAL(jdouble, Double, 0.0, result.d, false);
2888 CALL_NONVIRTUAL(void, Void, , , false);
2889
2890
2891 /*
2892 * Call a static method.
2893 */
2894 #define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref) \
2895 static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass jclazz, \
2896 jmethodID methodID, ...) \
2897 { \
2898 UNUSED_PARAMETER(jclazz); \
2899 JNI_ENTER(); \
2900 JValue result; \
2901 va_list args; \
2902 va_start(args, methodID); \
2903 dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
2904 va_end(args); \
2905 if (_isref && !dvmCheckException(_self)) \
2906 result.l = addLocalReference(env, result.l); \
2907 JNI_EXIT(); \
2908 return _retok; \
2909 } \
2910 static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz, \
2911 jmethodID methodID, va_list args) \
2912 { \
2913 UNUSED_PARAMETER(jclazz); \
2914 JNI_ENTER(); \
2915 JValue result; \
2916 dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
2917 if (_isref && !dvmCheckException(_self)) \
2918 result.l = addLocalReference(env, result.l); \
2919 JNI_EXIT(); \
2920 return _retok; \
2921 } \
2922 static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass jclazz, \
2923 jmethodID methodID, jvalue* args) \
2924 { \
2925 UNUSED_PARAMETER(jclazz); \
2926 JNI_ENTER(); \
2927 JValue result; \
2928 dvmCallMethodA(_self, (Method*)methodID, NULL, true, &result, args);\
2929 if (_isref && !dvmCheckException(_self)) \
2930 result.l = addLocalReference(env, result.l); \
2931 JNI_EXIT(); \
2932 return _retok; \
2933 }
2934 CALL_STATIC(jobject, Object, NULL, result.l, true);
2935 CALL_STATIC(jboolean, Boolean, 0, result.z, false);
2936 CALL_STATIC(jbyte, Byte, 0, result.b, false);
2937 CALL_STATIC(jchar, Char, 0, result.c, false);
2938 CALL_STATIC(jshort, Short, 0, result.s, false);
2939 CALL_STATIC(jint, Int, 0, result.i, false);
2940 CALL_STATIC(jlong, Long, 0, result.j, false);
2941 CALL_STATIC(jfloat, Float, 0.0f, result.f, false);
2942 CALL_STATIC(jdouble, Double, 0.0, result.d, false);
2943 CALL_STATIC(void, Void, , , false);
2944
2945 /*
2946 * Create a new String from Unicode data.
2947 *
2948 * If "len" is zero, we will return an empty string even if "unicodeChars"
2949 * is NULL. (The JNI spec is vague here.)
2950 */
NewString(JNIEnv * env,const jchar * unicodeChars,jsize len)2951 static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len)
2952 {
2953 JNI_ENTER();
2954 jobject retval;
2955
2956 StringObject* jstr = dvmCreateStringFromUnicode(unicodeChars, len);
2957 if (jstr == NULL) {
2958 retval = NULL;
2959 } else {
2960 dvmReleaseTrackedAlloc((Object*) jstr, NULL);
2961 retval = addLocalReference(env, (Object*) jstr);
2962 }
2963
2964 JNI_EXIT();
2965 return retval;
2966 }
2967
2968 /*
2969 * Return the length of a String in Unicode character units.
2970 */
GetStringLength(JNIEnv * env,jstring jstr)2971 static jsize GetStringLength(JNIEnv* env, jstring jstr)
2972 {
2973 JNI_ENTER();
2974
2975 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2976 jsize len = dvmStringLen(strObj);
2977
2978 JNI_EXIT();
2979 return len;
2980 }
2981
2982
2983 /*
2984 * Get a string's character data.
2985 *
2986 * The result is guaranteed to be valid until ReleaseStringChars is
2987 * called, which means we have to pin it or return a copy.
2988 */
GetStringChars(JNIEnv * env,jstring jstr,jboolean * isCopy)2989 static const jchar* GetStringChars(JNIEnv* env, jstring jstr, jboolean* isCopy)
2990 {
2991 JNI_ENTER();
2992
2993 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2994 ArrayObject* strChars = dvmStringCharArray(strObj);
2995
2996 pinPrimitiveArray(strChars);
2997
2998 const u2* data = dvmStringChars(strObj);
2999 if (isCopy != NULL)
3000 *isCopy = JNI_FALSE;
3001
3002 JNI_EXIT();
3003 return (jchar*)data;
3004 }
3005
3006 /*
3007 * Release our grip on some characters from a string.
3008 */
ReleaseStringChars(JNIEnv * env,jstring jstr,const jchar * chars)3009 static void ReleaseStringChars(JNIEnv* env, jstring jstr, const jchar* chars)
3010 {
3011 JNI_ENTER();
3012 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
3013 ArrayObject* strChars = dvmStringCharArray(strObj);
3014 unpinPrimitiveArray(strChars);
3015 JNI_EXIT();
3016 }
3017
3018 /*
3019 * Create a new java.lang.String object from chars in modified UTF-8 form.
3020 *
3021 * The spec doesn't say how to handle a NULL string. Popular desktop VMs
3022 * accept it and return a NULL pointer in response.
3023 */
NewStringUTF(JNIEnv * env,const char * bytes)3024 static jstring NewStringUTF(JNIEnv* env, const char* bytes)
3025 {
3026 JNI_ENTER();
3027
3028 jstring result;
3029
3030 if (bytes == NULL) {
3031 result = NULL;
3032 } else {
3033 /* note newStr could come back NULL on OOM */
3034 StringObject* newStr = dvmCreateStringFromCstr(bytes);
3035 result = addLocalReference(env, (Object*) newStr);
3036 dvmReleaseTrackedAlloc((Object*)newStr, NULL);
3037 }
3038
3039 JNI_EXIT();
3040 return result;
3041 }
3042
3043 /*
3044 * Return the length in bytes of the modified UTF-8 form of the string.
3045 */
GetStringUTFLength(JNIEnv * env,jstring jstr)3046 static jsize GetStringUTFLength(JNIEnv* env, jstring jstr)
3047 {
3048 JNI_ENTER();
3049
3050 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
3051 jsize len = dvmStringUtf8ByteLen(strObj);
3052
3053 JNI_EXIT();
3054 return len;
3055 }
3056
3057 /*
3058 * Convert "string" to modified UTF-8 and return a pointer. The returned
3059 * value must be released with ReleaseStringUTFChars.
3060 *
3061 * According to the JNI reference, "Returns a pointer to a UTF-8 string,
3062 * or NULL if the operation fails. Returns NULL if and only if an invocation
3063 * of this function has thrown an exception."
3064 *
3065 * The behavior here currently follows that of other open-source VMs, which
3066 * quietly return NULL if "string" is NULL. We should consider throwing an
3067 * NPE. (The CheckJNI code blows up if you try to pass in a NULL string,
3068 * which should catch this sort of thing during development.) Certain other
3069 * VMs will crash with a segmentation fault.
3070 */
GetStringUTFChars(JNIEnv * env,jstring jstr,jboolean * isCopy)3071 static const char* GetStringUTFChars(JNIEnv* env, jstring jstr,
3072 jboolean* isCopy)
3073 {
3074 JNI_ENTER();
3075 char* newStr;
3076
3077 if (jstr == NULL) {
3078 /* this shouldn't happen; throw NPE? */
3079 newStr = NULL;
3080 } else {
3081 if (isCopy != NULL)
3082 *isCopy = JNI_TRUE;
3083
3084 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
3085 newStr = dvmCreateCstrFromString(strObj);
3086 if (newStr == NULL) {
3087 /* assume memory failure */
3088 dvmThrowException("Ljava/lang/OutOfMemoryError;",
3089 "native heap string alloc failed");
3090 }
3091 }
3092
3093 JNI_EXIT();
3094 return newStr;
3095 }
3096
3097 /*
3098 * Release a string created by GetStringUTFChars().
3099 */
ReleaseStringUTFChars(JNIEnv * env,jstring jstr,const char * utf)3100 static void ReleaseStringUTFChars(JNIEnv* env, jstring jstr, const char* utf)
3101 {
3102 JNI_ENTER();
3103 free((char*)utf);
3104 JNI_EXIT();
3105 }
3106
3107 /*
3108 * Return the capacity of the array.
3109 */
GetArrayLength(JNIEnv * env,jarray jarr)3110 static jsize GetArrayLength(JNIEnv* env, jarray jarr)
3111 {
3112 JNI_ENTER();
3113
3114 ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3115 jsize length = arrObj->length;
3116
3117 JNI_EXIT();
3118 return length;
3119 }
3120
3121 /*
3122 * Construct a new array that holds objects from class "elementClass".
3123 */
NewObjectArray(JNIEnv * env,jsize length,jclass jelementClass,jobject jinitialElement)3124 static jobjectArray NewObjectArray(JNIEnv* env, jsize length,
3125 jclass jelementClass, jobject jinitialElement)
3126 {
3127 JNI_ENTER();
3128
3129 jobjectArray newArray = NULL;
3130 ClassObject* elemClassObj =
3131 (ClassObject*) dvmDecodeIndirectRef(env, jelementClass);
3132
3133 if (elemClassObj == NULL) {
3134 dvmThrowException("Ljava/lang/NullPointerException;",
3135 "JNI NewObjectArray");
3136 goto bail;
3137 }
3138
3139 ArrayObject* newObj =
3140 dvmAllocObjectArray(elemClassObj, length, ALLOC_DEFAULT);
3141 if (newObj == NULL) {
3142 assert(dvmCheckException(_self));
3143 goto bail;
3144 }
3145 newArray = addLocalReference(env, (Object*) newObj);
3146 dvmReleaseTrackedAlloc((Object*) newObj, NULL);
3147
3148 /*
3149 * Initialize the array. Trashes "length".
3150 */
3151 if (jinitialElement != NULL) {
3152 Object* initialElement = dvmDecodeIndirectRef(env, jinitialElement);
3153 Object** arrayData = (Object**) newObj->contents;
3154
3155 while (length--)
3156 *arrayData++ = initialElement;
3157 }
3158
3159
3160 bail:
3161 JNI_EXIT();
3162 return newArray;
3163 }
3164
3165 /*
3166 * Get one element of an Object array.
3167 *
3168 * Add the object to the local references table in case the array goes away.
3169 */
GetObjectArrayElement(JNIEnv * env,jobjectArray jarr,jsize index)3170 static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
3171 jsize index)
3172 {
3173 JNI_ENTER();
3174
3175 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3176 jobject retval = NULL;
3177
3178 assert(arrayObj != NULL);
3179
3180 /* check the array bounds */
3181 if (index < 0 || index >= (int) arrayObj->length) {
3182 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
3183 arrayObj->obj.clazz->descriptor);
3184 goto bail;
3185 }
3186
3187 Object* value = ((Object**) arrayObj->contents)[index];
3188 retval = addLocalReference(env, value);
3189
3190 bail:
3191 JNI_EXIT();
3192 return retval;
3193 }
3194
3195 /*
3196 * Set one element of an Object array.
3197 */
SetObjectArrayElement(JNIEnv * env,jobjectArray jarr,jsize index,jobject jobj)3198 static void SetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
3199 jsize index, jobject jobj)
3200 {
3201 JNI_ENTER();
3202
3203 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3204
3205 assert(arrayObj != NULL);
3206
3207 /* check the array bounds */
3208 if (index < 0 || index >= (int) arrayObj->length) {
3209 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
3210 arrayObj->obj.clazz->descriptor);
3211 goto bail;
3212 }
3213
3214 //LOGV("JNI: set element %d in array %p to %p\n", index, array, value);
3215
3216 Object* obj = dvmDecodeIndirectRef(env, jobj);
3217 dvmSetObjectArrayElement(arrayObj, index, obj);
3218
3219 bail:
3220 JNI_EXIT();
3221 }
3222
3223 /*
3224 * Create a new array of primitive elements.
3225 */
3226 #define NEW_PRIMITIVE_ARRAY(_artype, _jname, _typechar) \
3227 static _artype New##_jname##Array(JNIEnv* env, jsize length) \
3228 { \
3229 JNI_ENTER(); \
3230 ArrayObject* arrayObj; \
3231 arrayObj = dvmAllocPrimitiveArray(_typechar, length, \
3232 ALLOC_DEFAULT); \
3233 jarray jarr = NULL; \
3234 if (arrayObj != NULL) { \
3235 jarr = addLocalReference(env, (Object*) arrayObj); \
3236 dvmReleaseTrackedAlloc((Object*) arrayObj, NULL); \
3237 } \
3238 JNI_EXIT(); \
3239 return (_artype)jarr; \
3240 }
3241 NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean, 'Z');
3242 NEW_PRIMITIVE_ARRAY(jbyteArray, Byte, 'B');
3243 NEW_PRIMITIVE_ARRAY(jcharArray, Char, 'C');
3244 NEW_PRIMITIVE_ARRAY(jshortArray, Short, 'S');
3245 NEW_PRIMITIVE_ARRAY(jintArray, Int, 'I');
3246 NEW_PRIMITIVE_ARRAY(jlongArray, Long, 'J');
3247 NEW_PRIMITIVE_ARRAY(jfloatArray, Float, 'F');
3248 NEW_PRIMITIVE_ARRAY(jdoubleArray, Double, 'D');
3249
3250 /*
3251 * Get a pointer to a C array of primitive elements from an array object
3252 * of the matching type.
3253 *
3254 * In a compacting GC, we either need to return a copy of the elements or
3255 * "pin" the memory. Otherwise we run the risk of native code using the
3256 * buffer as the destination of e.g. a blocking read() call that wakes up
3257 * during a GC.
3258 */
3259 #define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
3260 static _ctype* Get##_jname##ArrayElements(JNIEnv* env, \
3261 _ctype##Array jarr, jboolean* isCopy) \
3262 { \
3263 JNI_ENTER(); \
3264 _ctype* data; \
3265 ArrayObject* arrayObj = \
3266 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
3267 pinPrimitiveArray(arrayObj); \
3268 data = (_ctype*) arrayObj->contents; \
3269 if (isCopy != NULL) \
3270 *isCopy = JNI_FALSE; \
3271 JNI_EXIT(); \
3272 return data; \
3273 }
3274
3275 /*
3276 * Release the storage locked down by the "get" function.
3277 *
3278 * The spec says, "'mode' has no effect if 'elems' is not a copy of the
3279 * elements in 'array'." They apparently did not anticipate the need to
3280 * un-pin memory.
3281 */
3282 #define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
3283 static void Release##_jname##ArrayElements(JNIEnv* env, \
3284 _ctype##Array jarr, _ctype* elems, jint mode) \
3285 { \
3286 UNUSED_PARAMETER(elems); \
3287 JNI_ENTER(); \
3288 if (mode != JNI_COMMIT) { \
3289 ArrayObject* arrayObj = \
3290 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
3291 unpinPrimitiveArray(arrayObj); \
3292 } \
3293 JNI_EXIT(); \
3294 }
3295
3296 /*
3297 * Copy a section of a primitive array to a buffer.
3298 */
3299 #define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
3300 static void Get##_jname##ArrayRegion(JNIEnv* env, \
3301 _ctype##Array jarr, jsize start, jsize len, _ctype* buf) \
3302 { \
3303 JNI_ENTER(); \
3304 ArrayObject* arrayObj = \
3305 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
3306 _ctype* data = (_ctype*) arrayObj->contents; \
3307 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
3308 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
3309 arrayObj->obj.clazz->descriptor); \
3310 } else { \
3311 memcpy(buf, data + start, len * sizeof(_ctype)); \
3312 } \
3313 JNI_EXIT(); \
3314 }
3315
3316 /*
3317 * Copy a section of a primitive array from a buffer.
3318 */
3319 #define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
3320 static void Set##_jname##ArrayRegion(JNIEnv* env, \
3321 _ctype##Array jarr, jsize start, jsize len, const _ctype* buf) \
3322 { \
3323 JNI_ENTER(); \
3324 ArrayObject* arrayObj = \
3325 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
3326 _ctype* data = (_ctype*) arrayObj->contents; \
3327 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
3328 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
3329 arrayObj->obj.clazz->descriptor); \
3330 } else { \
3331 memcpy(data + start, buf, len * sizeof(_ctype)); \
3332 } \
3333 JNI_EXIT(); \
3334 }
3335
3336 /*
3337 * 4-in-1:
3338 * Get<Type>ArrayElements
3339 * Release<Type>ArrayElements
3340 * Get<Type>ArrayRegion
3341 * Set<Type>ArrayRegion
3342 */
3343 #define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname) \
3344 GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
3345 RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
3346 GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); \
3347 SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);
3348
3349 PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean);
3350 PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte);
3351 PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char);
3352 PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short);
3353 PRIMITIVE_ARRAY_FUNCTIONS(jint, Int);
3354 PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long);
3355 PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float);
3356 PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double);
3357
3358 /*
3359 * Register one or more native functions in one class.
3360 *
3361 * This can be called multiple times on the same method, allowing the
3362 * caller to redefine the method implementation at will.
3363 */
RegisterNatives(JNIEnv * env,jclass jclazz,const JNINativeMethod * methods,jint nMethods)3364 static jint RegisterNatives(JNIEnv* env, jclass jclazz,
3365 const JNINativeMethod* methods, jint nMethods)
3366 {
3367 JNI_ENTER();
3368
3369 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
3370 jint retval = JNI_OK;
3371 int i;
3372
3373 if (gDvm.verboseJni) {
3374 LOGI("[Registering JNI native methods for class %s]\n",
3375 clazz->descriptor);
3376 }
3377
3378 for (i = 0; i < nMethods; i++) {
3379 if (!dvmRegisterJNIMethod(clazz, methods[i].name,
3380 methods[i].signature, methods[i].fnPtr))
3381 {
3382 retval = JNI_ERR;
3383 }
3384 }
3385
3386 JNI_EXIT();
3387 return retval;
3388 }
3389
3390 /*
3391 * Un-register all native methods associated with the class.
3392 *
3393 * The JNI docs refer to this as a way to reload/relink native libraries,
3394 * and say it "should not be used in normal native code". In particular,
3395 * there is no need to do this during shutdown, and you do not need to do
3396 * this before redefining a method implementation with RegisterNatives.
3397 *
3398 * It's chiefly useful for a native "plugin"-style library that wasn't
3399 * loaded with System.loadLibrary() (since there's no way to unload those).
3400 * For example, the library could upgrade itself by:
3401 *
3402 * 1. call UnregisterNatives to unbind the old methods
3403 * 2. ensure that no code is still executing inside it (somehow)
3404 * 3. dlclose() the library
3405 * 4. dlopen() the new library
3406 * 5. use RegisterNatives to bind the methods from the new library
3407 *
3408 * The above can work correctly without the UnregisterNatives call, but
3409 * creates a window of opportunity in which somebody might try to call a
3410 * method that is pointing at unmapped memory, crashing the VM. In theory
3411 * the same guards that prevent dlclose() from unmapping executing code could
3412 * prevent that anyway, but with this we can be more thorough and also deal
3413 * with methods that only exist in the old or new form of the library (maybe
3414 * the lib wants to try the call and catch the UnsatisfiedLinkError).
3415 */
UnregisterNatives(JNIEnv * env,jclass jclazz)3416 static jint UnregisterNatives(JNIEnv* env, jclass jclazz)
3417 {
3418 JNI_ENTER();
3419
3420 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
3421 if (gDvm.verboseJni) {
3422 LOGI("[Unregistering JNI native methods for class %s]\n",
3423 clazz->descriptor);
3424 }
3425 dvmUnregisterJNINativeMethods(clazz);
3426
3427 JNI_EXIT();
3428 return JNI_OK;
3429 }
3430
3431 /*
3432 * Lock the monitor.
3433 *
3434 * We have to track all monitor enters and exits, so that we can undo any
3435 * outstanding synchronization before the thread exits.
3436 */
MonitorEnter(JNIEnv * env,jobject jobj)3437 static jint MonitorEnter(JNIEnv* env, jobject jobj)
3438 {
3439 JNI_ENTER();
3440 Object* obj = dvmDecodeIndirectRef(env, jobj);
3441 dvmLockObject(_self, obj);
3442 trackMonitorEnter(_self, obj);
3443 JNI_EXIT();
3444 return JNI_OK;
3445 }
3446
3447 /*
3448 * Unlock the monitor.
3449 *
3450 * Throws an IllegalMonitorStateException if the current thread
3451 * doesn't own the monitor. (dvmUnlockObject() takes care of the throw.)
3452 *
3453 * According to the 1.6 spec, it's legal to call here with an exception
3454 * pending. If this fails, we'll stomp the original exception.
3455 */
MonitorExit(JNIEnv * env,jobject jobj)3456 static jint MonitorExit(JNIEnv* env, jobject jobj)
3457 {
3458 JNI_ENTER();
3459 Object* obj = dvmDecodeIndirectRef(env, jobj);
3460 bool success = dvmUnlockObject(_self, obj);
3461 if (success)
3462 trackMonitorExit(_self, obj);
3463 JNI_EXIT();
3464 return success ? JNI_OK : JNI_ERR;
3465 }
3466
3467 /*
3468 * Return the JavaVM interface associated with the current thread.
3469 */
GetJavaVM(JNIEnv * env,JavaVM ** vm)3470 static jint GetJavaVM(JNIEnv* env, JavaVM** vm)
3471 {
3472 JNI_ENTER();
3473 //*vm = gDvm.vmList;
3474 *vm = (JavaVM*) ((JNIEnvExt*)env)->vm;
3475 JNI_EXIT();
3476 if (*vm == NULL)
3477 return JNI_ERR;
3478 else
3479 return JNI_OK;
3480 }
3481
3482 /*
3483 * Copies "len" Unicode characters, from offset "start".
3484 */
GetStringRegion(JNIEnv * env,jstring jstr,jsize start,jsize len,jchar * buf)3485 static void GetStringRegion(JNIEnv* env, jstring jstr, jsize start, jsize len,
3486 jchar* buf)
3487 {
3488 JNI_ENTER();
3489 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
3490 if (start + len > dvmStringLen(strObj))
3491 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
3492 else
3493 memcpy(buf, dvmStringChars(strObj) + start, len * sizeof(u2));
3494 JNI_EXIT();
3495 }
3496
3497 /*
3498 * Translates "len" Unicode characters, from offset "start", into
3499 * modified UTF-8 encoding.
3500 */
GetStringUTFRegion(JNIEnv * env,jstring jstr,jsize start,jsize len,char * buf)3501 static void GetStringUTFRegion(JNIEnv* env, jstring jstr, jsize start,
3502 jsize len, char* buf)
3503 {
3504 JNI_ENTER();
3505 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
3506 if (start + len > dvmStringLen(strObj))
3507 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
3508 else
3509 dvmCreateCstrFromStringRegion(strObj, start, len, buf);
3510 JNI_EXIT();
3511 }
3512
3513 /*
3514 * Get a raw pointer to array data.
3515 *
3516 * The caller is expected to call "release" before doing any JNI calls
3517 * or blocking I/O operations.
3518 *
3519 * We need to pin the memory or block GC.
3520 */
GetPrimitiveArrayCritical(JNIEnv * env,jarray jarr,jboolean * isCopy)3521 static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray jarr,
3522 jboolean* isCopy)
3523 {
3524 JNI_ENTER();
3525 void* data;
3526 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3527 pinPrimitiveArray(arrayObj);
3528 data = arrayObj->contents;
3529 if (isCopy != NULL)
3530 *isCopy = JNI_FALSE;
3531 JNI_EXIT();
3532 return data;
3533 }
3534
3535 /*
3536 * Release an array obtained with GetPrimitiveArrayCritical.
3537 */
ReleasePrimitiveArrayCritical(JNIEnv * env,jarray jarr,void * carray,jint mode)3538 static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray jarr,
3539 void* carray, jint mode)
3540 {
3541 JNI_ENTER();
3542 if (mode != JNI_COMMIT) {
3543 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3544 unpinPrimitiveArray(arrayObj);
3545 }
3546 JNI_EXIT();
3547 }
3548
3549 /*
3550 * Like GetStringChars, but with restricted use.
3551 */
GetStringCritical(JNIEnv * env,jstring jstr,jboolean * isCopy)3552 static const jchar* GetStringCritical(JNIEnv* env, jstring jstr,
3553 jboolean* isCopy)
3554 {
3555 JNI_ENTER();
3556 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
3557 ArrayObject* strChars = dvmStringCharArray(strObj);
3558
3559 pinPrimitiveArray(strChars);
3560
3561 const u2* data = dvmStringChars(strObj);
3562 if (isCopy != NULL)
3563 *isCopy = JNI_FALSE;
3564
3565 JNI_EXIT();
3566 return (jchar*)data;
3567 }
3568
3569 /*
3570 * Like ReleaseStringChars, but with restricted use.
3571 */
ReleaseStringCritical(JNIEnv * env,jstring jstr,const jchar * carray)3572 static void ReleaseStringCritical(JNIEnv* env, jstring jstr,
3573 const jchar* carray)
3574 {
3575 JNI_ENTER();
3576 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
3577 ArrayObject* strChars = dvmStringCharArray(strObj);
3578 unpinPrimitiveArray(strChars);
3579 JNI_EXIT();
3580 }
3581
3582 /*
3583 * Create a new weak global reference.
3584 */
NewWeakGlobalRef(JNIEnv * env,jobject obj)3585 static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj)
3586 {
3587 JNI_ENTER();
3588 jweak wref = createWeakGlobalRef(env, obj);
3589 JNI_EXIT();
3590 return wref;
3591 }
3592
3593 /*
3594 * Delete the specified weak global reference.
3595 */
DeleteWeakGlobalRef(JNIEnv * env,jweak wref)3596 static void DeleteWeakGlobalRef(JNIEnv* env, jweak wref)
3597 {
3598 JNI_ENTER();
3599 deleteWeakGlobalRef(env, wref);
3600 JNI_EXIT();
3601 }
3602
3603 /*
3604 * Quick check for pending exceptions.
3605 *
3606 * TODO: we should be able to skip the enter/exit macros here.
3607 */
ExceptionCheck(JNIEnv * env)3608 static jboolean ExceptionCheck(JNIEnv* env)
3609 {
3610 JNI_ENTER();
3611 bool result = dvmCheckException(_self);
3612 JNI_EXIT();
3613 return result;
3614 }
3615
3616 /*
3617 * Returns the type of the object referred to by "obj". It can be local,
3618 * global, or weak global.
3619 *
3620 * In the current implementation, references can be global and local at
3621 * the same time, so while the return value is accurate it may not tell
3622 * the whole story.
3623 */
GetObjectRefType(JNIEnv * env,jobject jobj)3624 static jobjectRefType GetObjectRefType(JNIEnv* env, jobject jobj)
3625 {
3626 JNI_ENTER();
3627 jobjectRefType type = dvmGetJNIRefType(env, jobj);
3628 JNI_EXIT();
3629 return type;
3630 }
3631
3632 /*
3633 * Allocate and return a new java.nio.ByteBuffer for this block of memory.
3634 *
3635 * "address" may not be NULL, and "capacity" must be > 0. (These are only
3636 * verified when CheckJNI is enabled.)
3637 */
NewDirectByteBuffer(JNIEnv * env,void * address,jlong capacity)3638 static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity)
3639 {
3640 JNI_ENTER();
3641
3642 Thread* self = _self /*dvmThreadSelf()*/;
3643 Object* platformAddress = NULL;
3644 JValue callResult;
3645 jobject result = NULL;
3646 ClassObject* tmpClazz;
3647
3648 tmpClazz = gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on->clazz;
3649 if (!dvmIsClassInitialized(tmpClazz) && !dvmInitClass(tmpClazz))
3650 goto bail;
3651
3652 /* get an instance of PlatformAddress that wraps the provided address */
3653 dvmCallMethod(self,
3654 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on,
3655 NULL, &callResult, address);
3656 if (dvmGetException(self) != NULL || callResult.l == NULL)
3657 goto bail;
3658
3659 /* don't let the GC discard it */
3660 platformAddress = (Object*) callResult.l;
3661 dvmAddTrackedAlloc(platformAddress, self);
3662 LOGV("tracking %p for address=%p\n", platformAddress, address);
3663
3664 /* create an instance of java.nio.ReadWriteDirectByteBuffer */
3665 tmpClazz = gDvm.classJavaNioReadWriteDirectByteBuffer;
3666 if (!dvmIsClassInitialized(tmpClazz) && !dvmInitClass(tmpClazz))
3667 goto bail;
3668 Object* newObj = dvmAllocObject(tmpClazz, ALLOC_DONT_TRACK);
3669 if (newObj != NULL) {
3670 /* call the (PlatformAddress, int, int) constructor */
3671 result = addLocalReference(env, newObj);
3672 dvmCallMethod(self, gDvm.methJavaNioReadWriteDirectByteBuffer_init,
3673 newObj, &callResult, platformAddress, (jint) capacity, (jint) 0);
3674 if (dvmGetException(self) != NULL) {
3675 deleteLocalReference(env, result);
3676 result = NULL;
3677 goto bail;
3678 }
3679 }
3680
3681 bail:
3682 if (platformAddress != NULL)
3683 dvmReleaseTrackedAlloc(platformAddress, self);
3684 JNI_EXIT();
3685 return result;
3686 }
3687
3688 /*
3689 * Get the starting address of the buffer for the specified java.nio.Buffer.
3690 *
3691 * If this is not a "direct" buffer, we return NULL.
3692 */
GetDirectBufferAddress(JNIEnv * env,jobject jbuf)3693 static void* GetDirectBufferAddress(JNIEnv* env, jobject jbuf)
3694 {
3695 JNI_ENTER();
3696
3697 Object* bufObj = dvmDecodeIndirectRef(env, jbuf);
3698 Thread* self = _self /*dvmThreadSelf()*/;
3699 void* result;
3700
3701 /*
3702 * All Buffer objects have an effectiveDirectAddress field. If it's
3703 * nonzero, we can just return that value. If not, we have to call
3704 * through DirectBuffer.getEffectiveAddress(), which as a side-effect
3705 * will set the effectiveDirectAddress field for direct buffers (and
3706 * things that wrap direct buffers).
3707 */
3708 result = (void*) dvmGetFieldInt(bufObj,
3709 gDvm.offJavaNioBuffer_effectiveDirectAddress);
3710 if (result != NULL) {
3711 //LOGI("fast path for %p\n", buf);
3712 goto bail;
3713 }
3714
3715 /*
3716 * Start by determining if the object supports the DirectBuffer
3717 * interfaces. Note this does not guarantee that it's a direct buffer.
3718 */
3719 if (!dvmInstanceof(bufObj->clazz,
3720 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer))
3721 {
3722 goto bail;
3723 }
3724
3725 /*
3726 * Get a PlatformAddress object with the effective address.
3727 *
3728 * If this isn't a direct buffer, the result will be NULL and/or an
3729 * exception will have been thrown.
3730 */
3731 JValue callResult;
3732 const Method* meth = dvmGetVirtualizedMethod(bufObj->clazz,
3733 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress);
3734 dvmCallMethodA(self, meth, bufObj, false, &callResult, NULL);
3735 if (dvmGetException(self) != NULL) {
3736 dvmClearException(self);
3737 callResult.l = NULL;
3738 }
3739
3740 Object* platformAddr = callResult.l;
3741 if (platformAddr == NULL) {
3742 LOGV("Got request for address of non-direct buffer\n");
3743 goto bail;
3744 }
3745
3746 /*
3747 * Extract the address from the PlatformAddress object. Instead of
3748 * calling the toLong() method, just grab the field directly. This
3749 * is faster but more fragile.
3750 */
3751 result = (void*) dvmGetFieldInt(platformAddr,
3752 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr);
3753
3754 //LOGI("slow path for %p --> %p\n", buf, result);
3755
3756 bail:
3757 JNI_EXIT();
3758 return result;
3759 }
3760
3761 /*
3762 * Get the capacity of the buffer for the specified java.nio.Buffer.
3763 *
3764 * Returns -1 if the object is not a direct buffer. (We actually skip
3765 * this check, since it's expensive to determine, and just return the
3766 * capacity regardless.)
3767 */
GetDirectBufferCapacity(JNIEnv * env,jobject jbuf)3768 static jlong GetDirectBufferCapacity(JNIEnv* env, jobject jbuf)
3769 {
3770 JNI_ENTER();
3771
3772 /*
3773 * The capacity is always in the Buffer.capacity field.
3774 *
3775 * (The "check" version should verify that this is actually a Buffer,
3776 * but we're not required to do so here.)
3777 */
3778 Object* buf = dvmDecodeIndirectRef(env, jbuf);
3779 jlong result = dvmGetFieldInt(buf, gDvm.offJavaNioBuffer_capacity);
3780
3781 JNI_EXIT();
3782 return result;
3783 }
3784
3785
3786 /*
3787 * ===========================================================================
3788 * JNI invocation functions
3789 * ===========================================================================
3790 */
3791
3792 /*
3793 * Handle AttachCurrentThread{AsDaemon}.
3794 *
3795 * We need to make sure the VM is actually running. For example, if we start
3796 * up, issue an Attach, and the VM exits almost immediately, by the time the
3797 * attaching happens the VM could already be shutting down.
3798 *
3799 * It's hard to avoid a race condition here because we don't want to hold
3800 * a lock across the entire operation. What we can do is temporarily
3801 * increment the thread count to prevent a VM exit.
3802 *
3803 * This could potentially still have problems if a daemon thread calls here
3804 * while the VM is shutting down. dvmThreadSelf() will work, since it just
3805 * uses pthread TLS, but dereferencing "vm" could fail. Such is life when
3806 * you shut down a VM while threads are still running inside it.
3807 *
3808 * Remember that some code may call this as a way to find the per-thread
3809 * JNIEnv pointer. Don't do excess work for that case.
3810 */
attachThread(JavaVM * vm,JNIEnv ** p_env,void * thr_args,bool isDaemon)3811 static jint attachThread(JavaVM* vm, JNIEnv** p_env, void* thr_args,
3812 bool isDaemon)
3813 {
3814 JavaVMAttachArgs* args = (JavaVMAttachArgs*) thr_args;
3815 Thread* self;
3816 bool result = false;
3817
3818 /*
3819 * Return immediately if we're already one with the VM.
3820 */
3821 self = dvmThreadSelf();
3822 if (self != NULL) {
3823 *p_env = self->jniEnv;
3824 return JNI_OK;
3825 }
3826
3827 /*
3828 * No threads allowed in zygote mode.
3829 */
3830 if (gDvm.zygote) {
3831 return JNI_ERR;
3832 }
3833
3834 /* increment the count to keep the VM from bailing while we run */
3835 dvmLockThreadList(NULL);
3836 if (gDvm.nonDaemonThreadCount == 0) {
3837 // dead or dying
3838 LOGV("Refusing to attach thread '%s' -- VM is shutting down\n",
3839 (thr_args == NULL) ? "(unknown)" : args->name);
3840 dvmUnlockThreadList();
3841 return JNI_ERR;
3842 }
3843 gDvm.nonDaemonThreadCount++;
3844 dvmUnlockThreadList();
3845
3846 /* tweak the JavaVMAttachArgs as needed */
3847 JavaVMAttachArgs argsCopy;
3848 if (args == NULL) {
3849 /* allow the v1.1 calling convention */
3850 argsCopy.version = JNI_VERSION_1_2;
3851 argsCopy.name = NULL;
3852 argsCopy.group = dvmGetMainThreadGroup();
3853 } else {
3854 assert(args->version >= JNI_VERSION_1_2);
3855
3856 argsCopy.version = args->version;
3857 argsCopy.name = args->name;
3858 if (args->group != NULL)
3859 argsCopy.group = args->group;
3860 else
3861 argsCopy.group = dvmGetMainThreadGroup();
3862 }
3863
3864 result = dvmAttachCurrentThread(&argsCopy, isDaemon);
3865
3866 /* restore the count */
3867 dvmLockThreadList(NULL);
3868 gDvm.nonDaemonThreadCount--;
3869 dvmUnlockThreadList();
3870
3871 /*
3872 * Change the status to indicate that we're out in native code. This
3873 * call is not guarded with state-change macros, so we have to do it
3874 * by hand.
3875 */
3876 if (result) {
3877 self = dvmThreadSelf();
3878 assert(self != NULL);
3879 dvmChangeStatus(self, THREAD_NATIVE);
3880 *p_env = self->jniEnv;
3881 return JNI_OK;
3882 } else {
3883 return JNI_ERR;
3884 }
3885 }
3886
3887 /*
3888 * Attach the current thread to the VM. If the thread is already attached,
3889 * this is a no-op.
3890 */
AttachCurrentThread(JavaVM * vm,JNIEnv ** p_env,void * thr_args)3891 static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args)
3892 {
3893 return attachThread(vm, p_env, thr_args, false);
3894 }
3895
3896 /*
3897 * Like AttachCurrentThread, but set the "daemon" flag.
3898 */
AttachCurrentThreadAsDaemon(JavaVM * vm,JNIEnv ** p_env,void * thr_args)3899 static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env,
3900 void* thr_args)
3901 {
3902 return attachThread(vm, p_env, thr_args, true);
3903 }
3904
3905 /*
3906 * Dissociate the current thread from the VM.
3907 */
DetachCurrentThread(JavaVM * vm)3908 static jint DetachCurrentThread(JavaVM* vm)
3909 {
3910 Thread* self = dvmThreadSelf();
3911
3912 if (self == NULL) /* not attached, can't do anything */
3913 return JNI_ERR;
3914
3915 /* switch to "running" to check for suspension */
3916 dvmChangeStatus(self, THREAD_RUNNING);
3917
3918 /* detach the thread */
3919 dvmDetachCurrentThread();
3920
3921 /* (no need to change status back -- we have no status) */
3922 return JNI_OK;
3923 }
3924
3925 /*
3926 * If current thread is attached to VM, return the associated JNIEnv.
3927 * Otherwise, stuff NULL in and return JNI_EDETACHED.
3928 *
3929 * JVMTI overloads this by specifying a magic value for "version", so we
3930 * do want to check that here.
3931 */
GetEnv(JavaVM * vm,void ** env,jint version)3932 static jint GetEnv(JavaVM* vm, void** env, jint version)
3933 {
3934 Thread* self = dvmThreadSelf();
3935
3936 if (version < JNI_VERSION_1_1 || version > JNI_VERSION_1_6)
3937 return JNI_EVERSION;
3938
3939 if (self == NULL) {
3940 *env = NULL;
3941 } else {
3942 /* TODO: status change is probably unnecessary */
3943 dvmChangeStatus(self, THREAD_RUNNING);
3944 *env = (void*) dvmGetThreadJNIEnv(self);
3945 dvmChangeStatus(self, THREAD_NATIVE);
3946 }
3947 if (*env == NULL)
3948 return JNI_EDETACHED;
3949 else
3950 return JNI_OK;
3951 }
3952
3953 /*
3954 * Destroy the VM. This may be called from any thread.
3955 *
3956 * If the current thread is attached, wait until the current thread is
3957 * the only non-daemon user-level thread. If the current thread is not
3958 * attached, we attach it and do the processing as usual. (If the attach
3959 * fails, it's probably because all the non-daemon threads have already
3960 * exited and the VM doesn't want to let us back in.)
3961 *
3962 * TODO: we don't really deal with the situation where more than one thread
3963 * has called here. One thread wins, the other stays trapped waiting on
3964 * the condition variable forever. Not sure this situation is interesting
3965 * in real life.
3966 */
DestroyJavaVM(JavaVM * vm)3967 static jint DestroyJavaVM(JavaVM* vm)
3968 {
3969 JavaVMExt* ext = (JavaVMExt*) vm;
3970 Thread* self;
3971
3972 if (ext == NULL)
3973 return JNI_ERR;
3974
3975 if (gDvm.verboseShutdown)
3976 LOGD("DestroyJavaVM waiting for non-daemon threads to exit\n");
3977
3978 /*
3979 * Sleep on a condition variable until it's okay to exit.
3980 */
3981 self = dvmThreadSelf();
3982 if (self == NULL) {
3983 JNIEnv* tmpEnv;
3984 if (AttachCurrentThread(vm, &tmpEnv, NULL) != JNI_OK) {
3985 LOGV("Unable to reattach main for Destroy; assuming VM is "
3986 "shutting down (count=%d)\n",
3987 gDvm.nonDaemonThreadCount);
3988 goto shutdown;
3989 } else {
3990 LOGV("Attached to wait for shutdown in Destroy\n");
3991 }
3992 }
3993 dvmChangeStatus(self, THREAD_VMWAIT);
3994
3995 dvmLockThreadList(self);
3996 gDvm.nonDaemonThreadCount--; // remove current thread from count
3997
3998 while (gDvm.nonDaemonThreadCount > 0)
3999 pthread_cond_wait(&gDvm.vmExitCond, &gDvm.threadListLock);
4000
4001 dvmUnlockThreadList();
4002 self = NULL;
4003
4004 shutdown:
4005 // TODO: call System.exit() to run any registered shutdown hooks
4006 // (this may not return -- figure out how this should work)
4007
4008 if (gDvm.verboseShutdown)
4009 LOGD("DestroyJavaVM shutting VM down\n");
4010 dvmShutdown();
4011
4012 // TODO - free resources associated with JNI-attached daemon threads
4013 free(ext->envList);
4014 free(ext);
4015
4016 return JNI_OK;
4017 }
4018
4019
4020 /*
4021 * ===========================================================================
4022 * Function tables
4023 * ===========================================================================
4024 */
4025
4026 static const struct JNINativeInterface gNativeInterface = {
4027 NULL,
4028 NULL,
4029 NULL,
4030 NULL,
4031
4032 GetVersion,
4033
4034 DefineClass,
4035 FindClass,
4036
4037 FromReflectedMethod,
4038 FromReflectedField,
4039 ToReflectedMethod,
4040
4041 GetSuperclass,
4042 IsAssignableFrom,
4043
4044 ToReflectedField,
4045
4046 Throw,
4047 ThrowNew,
4048 ExceptionOccurred,
4049 ExceptionDescribe,
4050 ExceptionClear,
4051 FatalError,
4052
4053 PushLocalFrame,
4054 PopLocalFrame,
4055
4056 NewGlobalRef,
4057 DeleteGlobalRef,
4058 DeleteLocalRef,
4059 IsSameObject,
4060 NewLocalRef,
4061 EnsureLocalCapacity,
4062
4063 AllocObject,
4064 NewObject,
4065 NewObjectV,
4066 NewObjectA,
4067
4068 GetObjectClass,
4069 IsInstanceOf,
4070
4071 GetMethodID,
4072
4073 CallObjectMethod,
4074 CallObjectMethodV,
4075 CallObjectMethodA,
4076 CallBooleanMethod,
4077 CallBooleanMethodV,
4078 CallBooleanMethodA,
4079 CallByteMethod,
4080 CallByteMethodV,
4081 CallByteMethodA,
4082 CallCharMethod,
4083 CallCharMethodV,
4084 CallCharMethodA,
4085 CallShortMethod,
4086 CallShortMethodV,
4087 CallShortMethodA,
4088 CallIntMethod,
4089 CallIntMethodV,
4090 CallIntMethodA,
4091 CallLongMethod,
4092 CallLongMethodV,
4093 CallLongMethodA,
4094 CallFloatMethod,
4095 CallFloatMethodV,
4096 CallFloatMethodA,
4097 CallDoubleMethod,
4098 CallDoubleMethodV,
4099 CallDoubleMethodA,
4100 CallVoidMethod,
4101 CallVoidMethodV,
4102 CallVoidMethodA,
4103
4104 CallNonvirtualObjectMethod,
4105 CallNonvirtualObjectMethodV,
4106 CallNonvirtualObjectMethodA,
4107 CallNonvirtualBooleanMethod,
4108 CallNonvirtualBooleanMethodV,
4109 CallNonvirtualBooleanMethodA,
4110 CallNonvirtualByteMethod,
4111 CallNonvirtualByteMethodV,
4112 CallNonvirtualByteMethodA,
4113 CallNonvirtualCharMethod,
4114 CallNonvirtualCharMethodV,
4115 CallNonvirtualCharMethodA,
4116 CallNonvirtualShortMethod,
4117 CallNonvirtualShortMethodV,
4118 CallNonvirtualShortMethodA,
4119 CallNonvirtualIntMethod,
4120 CallNonvirtualIntMethodV,
4121 CallNonvirtualIntMethodA,
4122 CallNonvirtualLongMethod,
4123 CallNonvirtualLongMethodV,
4124 CallNonvirtualLongMethodA,
4125 CallNonvirtualFloatMethod,
4126 CallNonvirtualFloatMethodV,
4127 CallNonvirtualFloatMethodA,
4128 CallNonvirtualDoubleMethod,
4129 CallNonvirtualDoubleMethodV,
4130 CallNonvirtualDoubleMethodA,
4131 CallNonvirtualVoidMethod,
4132 CallNonvirtualVoidMethodV,
4133 CallNonvirtualVoidMethodA,
4134
4135 GetFieldID,
4136
4137 GetObjectField,
4138 GetBooleanField,
4139 GetByteField,
4140 GetCharField,
4141 GetShortField,
4142 GetIntField,
4143 GetLongField,
4144 GetFloatField,
4145 GetDoubleField,
4146 SetObjectField,
4147 SetBooleanField,
4148 SetByteField,
4149 SetCharField,
4150 SetShortField,
4151 SetIntField,
4152 SetLongField,
4153 SetFloatField,
4154 SetDoubleField,
4155
4156 GetStaticMethodID,
4157
4158 CallStaticObjectMethod,
4159 CallStaticObjectMethodV,
4160 CallStaticObjectMethodA,
4161 CallStaticBooleanMethod,
4162 CallStaticBooleanMethodV,
4163 CallStaticBooleanMethodA,
4164 CallStaticByteMethod,
4165 CallStaticByteMethodV,
4166 CallStaticByteMethodA,
4167 CallStaticCharMethod,
4168 CallStaticCharMethodV,
4169 CallStaticCharMethodA,
4170 CallStaticShortMethod,
4171 CallStaticShortMethodV,
4172 CallStaticShortMethodA,
4173 CallStaticIntMethod,
4174 CallStaticIntMethodV,
4175 CallStaticIntMethodA,
4176 CallStaticLongMethod,
4177 CallStaticLongMethodV,
4178 CallStaticLongMethodA,
4179 CallStaticFloatMethod,
4180 CallStaticFloatMethodV,
4181 CallStaticFloatMethodA,
4182 CallStaticDoubleMethod,
4183 CallStaticDoubleMethodV,
4184 CallStaticDoubleMethodA,
4185 CallStaticVoidMethod,
4186 CallStaticVoidMethodV,
4187 CallStaticVoidMethodA,
4188
4189 GetStaticFieldID,
4190
4191 GetStaticObjectField,
4192 GetStaticBooleanField,
4193 GetStaticByteField,
4194 GetStaticCharField,
4195 GetStaticShortField,
4196 GetStaticIntField,
4197 GetStaticLongField,
4198 GetStaticFloatField,
4199 GetStaticDoubleField,
4200
4201 SetStaticObjectField,
4202 SetStaticBooleanField,
4203 SetStaticByteField,
4204 SetStaticCharField,
4205 SetStaticShortField,
4206 SetStaticIntField,
4207 SetStaticLongField,
4208 SetStaticFloatField,
4209 SetStaticDoubleField,
4210
4211 NewString,
4212
4213 GetStringLength,
4214 GetStringChars,
4215 ReleaseStringChars,
4216
4217 NewStringUTF,
4218 GetStringUTFLength,
4219 GetStringUTFChars,
4220 ReleaseStringUTFChars,
4221
4222 GetArrayLength,
4223 NewObjectArray,
4224 GetObjectArrayElement,
4225 SetObjectArrayElement,
4226
4227 NewBooleanArray,
4228 NewByteArray,
4229 NewCharArray,
4230 NewShortArray,
4231 NewIntArray,
4232 NewLongArray,
4233 NewFloatArray,
4234 NewDoubleArray,
4235
4236 GetBooleanArrayElements,
4237 GetByteArrayElements,
4238 GetCharArrayElements,
4239 GetShortArrayElements,
4240 GetIntArrayElements,
4241 GetLongArrayElements,
4242 GetFloatArrayElements,
4243 GetDoubleArrayElements,
4244
4245 ReleaseBooleanArrayElements,
4246 ReleaseByteArrayElements,
4247 ReleaseCharArrayElements,
4248 ReleaseShortArrayElements,
4249 ReleaseIntArrayElements,
4250 ReleaseLongArrayElements,
4251 ReleaseFloatArrayElements,
4252 ReleaseDoubleArrayElements,
4253
4254 GetBooleanArrayRegion,
4255 GetByteArrayRegion,
4256 GetCharArrayRegion,
4257 GetShortArrayRegion,
4258 GetIntArrayRegion,
4259 GetLongArrayRegion,
4260 GetFloatArrayRegion,
4261 GetDoubleArrayRegion,
4262 SetBooleanArrayRegion,
4263 SetByteArrayRegion,
4264 SetCharArrayRegion,
4265 SetShortArrayRegion,
4266 SetIntArrayRegion,
4267 SetLongArrayRegion,
4268 SetFloatArrayRegion,
4269 SetDoubleArrayRegion,
4270
4271 RegisterNatives,
4272 UnregisterNatives,
4273
4274 MonitorEnter,
4275 MonitorExit,
4276
4277 GetJavaVM,
4278
4279 GetStringRegion,
4280 GetStringUTFRegion,
4281
4282 GetPrimitiveArrayCritical,
4283 ReleasePrimitiveArrayCritical,
4284
4285 GetStringCritical,
4286 ReleaseStringCritical,
4287
4288 NewWeakGlobalRef,
4289 DeleteWeakGlobalRef,
4290
4291 ExceptionCheck,
4292
4293 NewDirectByteBuffer,
4294 GetDirectBufferAddress,
4295 GetDirectBufferCapacity,
4296
4297 GetObjectRefType
4298 };
4299 static const struct JNIInvokeInterface gInvokeInterface = {
4300 NULL,
4301 NULL,
4302 NULL,
4303
4304 DestroyJavaVM,
4305 AttachCurrentThread,
4306 DetachCurrentThread,
4307
4308 GetEnv,
4309
4310 AttachCurrentThreadAsDaemon,
4311 };
4312
4313
4314 /*
4315 * ===========================================================================
4316 * VM/Env creation
4317 * ===========================================================================
4318 */
4319
4320 /*
4321 * Enable "checked JNI" after the VM has partially started. This must
4322 * only be called in "zygote" mode, when we have one thread running.
4323 *
4324 * This doesn't attempt to rewrite the JNI call bridge associated with
4325 * native methods, so we won't get those checks for any methods that have
4326 * already been resolved.
4327 */
dvmLateEnableCheckedJni(void)4328 void dvmLateEnableCheckedJni(void)
4329 {
4330 JNIEnvExt* extEnv;
4331 JavaVMExt* extVm;
4332
4333 extEnv = dvmGetJNIEnvForThread();
4334 if (extEnv == NULL) {
4335 LOGE("dvmLateEnableCheckedJni: thread has no JNIEnv\n");
4336 return;
4337 }
4338 extVm = extEnv->vm;
4339 assert(extVm != NULL);
4340
4341 if (!extVm->useChecked) {
4342 LOGD("Late-enabling CheckJNI\n");
4343 dvmUseCheckedJniVm(extVm);
4344 extVm->useChecked = true;
4345 dvmUseCheckedJniEnv(extEnv);
4346
4347 /* currently no way to pick up jniopts features */
4348 } else {
4349 LOGD("Not late-enabling CheckJNI (already on)\n");
4350 }
4351 }
4352
4353 /*
4354 * Not supported.
4355 */
JNI_GetDefaultJavaVMInitArgs(void * vm_args)4356 jint JNI_GetDefaultJavaVMInitArgs(void* vm_args)
4357 {
4358 return JNI_ERR;
4359 }
4360
4361 /*
4362 * Return a buffer full of created VMs.
4363 *
4364 * We always have zero or one.
4365 */
JNI_GetCreatedJavaVMs(JavaVM ** vmBuf,jsize bufLen,jsize * nVMs)4366 jint JNI_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs)
4367 {
4368 if (gDvm.vmList != NULL) {
4369 *nVMs = 1;
4370
4371 if (bufLen > 0)
4372 *vmBuf++ = gDvm.vmList;
4373 } else {
4374 *nVMs = 0;
4375 }
4376
4377 return JNI_OK;
4378 }
4379
4380
4381 /*
4382 * Create a new VM instance.
4383 *
4384 * The current thread becomes the main VM thread. We return immediately,
4385 * which effectively means the caller is executing in a native method.
4386 */
JNI_CreateJavaVM(JavaVM ** p_vm,JNIEnv ** p_env,void * vm_args)4387 jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args)
4388 {
4389 const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
4390 JNIEnvExt* pEnv = NULL;
4391 JavaVMExt* pVM = NULL;
4392 const char** argv;
4393 int argc = 0;
4394 int i, curOpt;
4395 int result = JNI_ERR;
4396 bool checkJni = false;
4397 bool warnError = true;
4398 bool forceDataCopy = false;
4399
4400 if (args->version < JNI_VERSION_1_2)
4401 return JNI_EVERSION;
4402
4403 // TODO: don't allow creation of multiple VMs -- one per customer for now
4404
4405 /* zero globals; not strictly necessary the first time a VM is started */
4406 memset(&gDvm, 0, sizeof(gDvm));
4407
4408 /*
4409 * Set up structures for JNIEnv and VM.
4410 */
4411 //pEnv = (JNIEnvExt*) malloc(sizeof(JNIEnvExt));
4412 pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt));
4413
4414 //memset(pEnv, 0, sizeof(JNIEnvExt));
4415 //pEnv->funcTable = &gNativeInterface;
4416 //pEnv->vm = pVM;
4417 memset(pVM, 0, sizeof(JavaVMExt));
4418 pVM->funcTable = &gInvokeInterface;
4419 pVM->envList = pEnv;
4420 dvmInitMutex(&pVM->envListLock);
4421
4422 argv = (const char**) malloc(sizeof(char*) * (args->nOptions));
4423 memset(argv, 0, sizeof(char*) * (args->nOptions));
4424
4425 curOpt = 0;
4426
4427 /*
4428 * Convert JNI args to argv.
4429 *
4430 * We have to pull out vfprintf/exit/abort, because they use the
4431 * "extraInfo" field to pass function pointer "hooks" in. We also
4432 * look for the -Xcheck:jni stuff here.
4433 */
4434 for (i = 0; i < args->nOptions; i++) {
4435 const char* optStr = args->options[i].optionString;
4436
4437 if (optStr == NULL) {
4438 fprintf(stderr, "ERROR: arg %d string was null\n", i);
4439 goto bail;
4440 } else if (strcmp(optStr, "vfprintf") == 0) {
4441 gDvm.vfprintfHook = args->options[i].extraInfo;
4442 } else if (strcmp(optStr, "exit") == 0) {
4443 gDvm.exitHook = args->options[i].extraInfo;
4444 } else if (strcmp(optStr, "abort") == 0) {
4445 gDvm.abortHook = args->options[i].extraInfo;
4446 } else if (strcmp(optStr, "-Xcheck:jni") == 0) {
4447 checkJni = true;
4448 } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {
4449 const char* jniOpts = optStr + 9;
4450 while (jniOpts != NULL) {
4451 jniOpts++; /* skip past ':' or ',' */
4452 if (strncmp(jniOpts, "warnonly", 8) == 0) {
4453 warnError = false;
4454 } else if (strncmp(jniOpts, "forcecopy", 9) == 0) {
4455 forceDataCopy = true;
4456 } else {
4457 LOGW("unknown jni opt starting at '%s'\n", jniOpts);
4458 }
4459 jniOpts = strchr(jniOpts, ',');
4460 }
4461 } else {
4462 /* regular option */
4463 argv[curOpt++] = optStr;
4464 }
4465 }
4466 argc = curOpt;
4467
4468 if (checkJni) {
4469 dvmUseCheckedJniVm(pVM);
4470 pVM->useChecked = true;
4471 }
4472 pVM->warnError = warnError;
4473 pVM->forceDataCopy = forceDataCopy;
4474
4475 /* set this up before initializing VM, so it can create some JNIEnvs */
4476 gDvm.vmList = (JavaVM*) pVM;
4477
4478 /*
4479 * Create an env for main thread. We need to have something set up
4480 * here because some of the class initialization we do when starting
4481 * up the VM will call into native code.
4482 */
4483 pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
4484
4485 /* initialize VM */
4486 gDvm.initializing = true;
4487 if (dvmStartup(argc, argv, args->ignoreUnrecognized, (JNIEnv*)pEnv) != 0) {
4488 free(pEnv);
4489 free(pVM);
4490 goto bail;
4491 }
4492
4493 /*
4494 * Success! Return stuff to caller.
4495 */
4496 dvmChangeStatus(NULL, THREAD_NATIVE);
4497 *p_env = (JNIEnv*) pEnv;
4498 *p_vm = (JavaVM*) pVM;
4499 result = JNI_OK;
4500
4501 bail:
4502 gDvm.initializing = false;
4503 if (result == JNI_OK)
4504 LOGV("JNI_CreateJavaVM succeeded\n");
4505 else
4506 LOGW("JNI_CreateJavaVM failed\n");
4507 free(argv);
4508 return result;
4509 }
4510