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