OSDN Git Service

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