OSDN Git Service

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