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