Jni.cpp revision 00feda9a6a12b3a894fbe10876636973d2a03ac2
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               thisMethod == gDvm.methDalvikSystemNativeStart_run) {
1231        /* start point of invocation interface */
1232        if (!gDvm.initializing) {
1233            loader = trackedLoader = dvmGetSystemClassLoader();
1234        } else {
1235            loader = NULL;
1236        }
1237    } else {
1238        loader = thisMethod->clazz->classLoader;
1239    }
1240
1241    char* descriptor = dvmNameToDescriptor(name);
1242    if (descriptor == NULL) {
1243        return NULL;
1244    }
1245    ClassObject* clazz = dvmFindClassNoInit(descriptor, loader);
1246    free(descriptor);
1247
1248    jclass jclazz = (jclass) addLocalReference(env, (Object*) clazz);
1249    dvmReleaseTrackedAlloc(trackedLoader, ts.self());
1250    return jclazz;
1251}
1252
1253/*
1254 * Return the superclass of a class.
1255 */
1256static jclass GetSuperclass(JNIEnv* env, jclass jclazz) {
1257    ScopedJniThreadState ts(env);
1258    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1259    return (jclass) addLocalReference(env, (Object*)clazz->super);
1260}
1261
1262/*
1263 * Determine whether an object of clazz1 can be safely cast to clazz2.
1264 *
1265 * Like IsInstanceOf, but with a pair of class objects instead of obj+class.
1266 */
1267static jboolean IsAssignableFrom(JNIEnv* env, jclass jclazz1, jclass jclazz2) {
1268    ScopedJniThreadState ts(env);
1269    ClassObject* clazz1 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz1);
1270    ClassObject* clazz2 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz2);
1271    return dvmInstanceof(clazz1, clazz2);
1272}
1273
1274/*
1275 * Given a java.lang.reflect.Method or .Constructor, return a methodID.
1276 */
1277static jmethodID FromReflectedMethod(JNIEnv* env, jobject jmethod) {
1278    ScopedJniThreadState ts(env);
1279    Object* method = dvmDecodeIndirectRef(env, jmethod);
1280    return (jmethodID) dvmGetMethodFromReflectObj(method);
1281}
1282
1283/*
1284 * Given a java.lang.reflect.Field, return a fieldID.
1285 */
1286static jfieldID FromReflectedField(JNIEnv* env, jobject jfield) {
1287    ScopedJniThreadState ts(env);
1288    Object* field = dvmDecodeIndirectRef(env, jfield);
1289    return (jfieldID) dvmGetFieldFromReflectObj(field);
1290}
1291
1292/*
1293 * Convert a methodID to a java.lang.reflect.Method or .Constructor.
1294 *
1295 * (The "isStatic" field does not appear in the spec.)
1296 *
1297 * Throws OutOfMemory and returns NULL on failure.
1298 */
1299static jobject ToReflectedMethod(JNIEnv* env, jclass jcls, jmethodID methodID, jboolean isStatic) {
1300    ScopedJniThreadState ts(env);
1301    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
1302    Object* obj = dvmCreateReflectObjForMethod(clazz, (Method*) methodID);
1303    dvmReleaseTrackedAlloc(obj, NULL);
1304    return addLocalReference(env, obj);
1305}
1306
1307/*
1308 * Convert a fieldID to a java.lang.reflect.Field.
1309 *
1310 * (The "isStatic" field does not appear in the spec.)
1311 *
1312 * Throws OutOfMemory and returns NULL on failure.
1313 */
1314static jobject ToReflectedField(JNIEnv* env, jclass jcls, jfieldID fieldID, jboolean isStatic) {
1315    ScopedJniThreadState ts(env);
1316    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
1317    Object* obj = dvmCreateReflectObjForField(clazz, (Field*) fieldID);
1318    dvmReleaseTrackedAlloc(obj, NULL);
1319    return addLocalReference(env, obj);
1320}
1321
1322/*
1323 * Take this exception and throw it.
1324 */
1325static jint Throw(JNIEnv* env, jthrowable jobj) {
1326    ScopedJniThreadState ts(env);
1327    if (jobj != NULL) {
1328        Object* obj = dvmDecodeIndirectRef(env, jobj);
1329        dvmSetException(ts.self(), obj);
1330        return JNI_OK;
1331    }
1332    return JNI_ERR;
1333}
1334
1335/*
1336 * Constructs an exception object from the specified class with the message
1337 * specified by "message", and throws it.
1338 */
1339static jint ThrowNew(JNIEnv* env, jclass jclazz, const char* message) {
1340    ScopedJniThreadState ts(env);
1341    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1342    dvmThrowException(clazz, message);
1343    // TODO: should return failure if this didn't work (e.g. OOM)
1344    return JNI_OK;
1345}
1346
1347/*
1348 * If an exception is being thrown, return the exception object.  Otherwise,
1349 * return NULL.
1350 *
1351 * TODO: if there is no pending exception, we should be able to skip the
1352 * enter/exit checks.  If we find one, we need to enter and then re-fetch
1353 * the exception (in case it got moved by a compacting GC).
1354 */
1355static jthrowable ExceptionOccurred(JNIEnv* env) {
1356    ScopedJniThreadState ts(env);
1357    Object* exception = dvmGetException(ts.self());
1358    jthrowable localException = (jthrowable) addLocalReference(env, exception);
1359    if (localException == NULL && exception != NULL) {
1360        /*
1361         * We were unable to add a new local reference, and threw a new
1362         * exception.  We can't return "exception", because it's not a
1363         * local reference.  So we have to return NULL, indicating that
1364         * there was no exception, even though it's pretty much raining
1365         * exceptions in here.
1366         */
1367        LOGW("JNI WARNING: addLocal/exception combo");
1368    }
1369    return localException;
1370}
1371
1372/*
1373 * Print an exception and stack trace to stderr.
1374 */
1375static void ExceptionDescribe(JNIEnv* env) {
1376    ScopedJniThreadState ts(env);
1377    Object* exception = dvmGetException(ts.self());
1378    if (exception != NULL) {
1379        dvmPrintExceptionStackTrace();
1380    } else {
1381        LOGI("Odd: ExceptionDescribe called, but no exception pending");
1382    }
1383}
1384
1385/*
1386 * Clear the exception currently being thrown.
1387 *
1388 * TODO: we should be able to skip the enter/exit stuff.
1389 */
1390static void ExceptionClear(JNIEnv* env) {
1391    ScopedJniThreadState ts(env);
1392    dvmClearException(ts.self());
1393}
1394
1395/*
1396 * Kill the VM.  This function does not return.
1397 */
1398static void FatalError(JNIEnv* env, const char* msg) {
1399    //dvmChangeStatus(NULL, THREAD_RUNNING);
1400    LOGE("JNI posting fatal error: %s", msg);
1401    dvmAbort();
1402}
1403
1404/*
1405 * Push a new JNI frame on the stack, with a new set of locals.
1406 *
1407 * The new frame must have the same method pointer.  (If for no other
1408 * reason than FindClass needs it to get the appropriate class loader.)
1409 */
1410static jint PushLocalFrame(JNIEnv* env, jint capacity) {
1411    ScopedJniThreadState ts(env);
1412    if (!ensureLocalCapacity(env, capacity) ||
1413            !dvmPushLocalFrame(ts.self(), dvmGetCurrentJNIMethod()))
1414    {
1415        /* yes, OutOfMemoryError, not StackOverflowError */
1416        dvmClearException(ts.self());
1417        dvmThrowOutOfMemoryError("out of stack in JNI PushLocalFrame");
1418        return JNI_ERR;
1419    }
1420    return JNI_OK;
1421}
1422
1423/*
1424 * Pop the local frame off.  If "jresult" is not null, add it as a
1425 * local reference on the now-current frame.
1426 */
1427static jobject PopLocalFrame(JNIEnv* env, jobject jresult) {
1428    ScopedJniThreadState ts(env);
1429    Object* result = dvmDecodeIndirectRef(env, jresult);
1430    if (!dvmPopLocalFrame(ts.self())) {
1431        LOGW("JNI WARNING: too many PopLocalFrame calls");
1432        dvmClearException(ts.self());
1433        dvmThrowRuntimeException("too many PopLocalFrame calls");
1434    }
1435    return addLocalReference(env, result);
1436}
1437
1438/*
1439 * Add a reference to the global list.
1440 */
1441static jobject NewGlobalRef(JNIEnv* env, jobject jobj) {
1442    ScopedJniThreadState ts(env);
1443    Object* obj = dvmDecodeIndirectRef(env, jobj);
1444    return addGlobalReference(obj);
1445}
1446
1447/*
1448 * Delete a reference from the global list.
1449 */
1450static void DeleteGlobalRef(JNIEnv* env, jobject jglobalRef) {
1451    ScopedJniThreadState ts(env);
1452    deleteGlobalReference(jglobalRef);
1453}
1454
1455
1456/*
1457 * Add a reference to the local list.
1458 */
1459static jobject NewLocalRef(JNIEnv* env, jobject jobj) {
1460    ScopedJniThreadState ts(env);
1461    Object* obj = dvmDecodeIndirectRef(env, jobj);
1462    return addLocalReference(env, obj);
1463}
1464
1465/*
1466 * Delete a reference from the local list.
1467 */
1468static void DeleteLocalRef(JNIEnv* env, jobject jlocalRef) {
1469    ScopedJniThreadState ts(env);
1470    deleteLocalReference(env, jlocalRef);
1471}
1472
1473/*
1474 * Ensure that the local references table can hold at least this many
1475 * references.
1476 */
1477static jint EnsureLocalCapacity(JNIEnv* env, jint capacity) {
1478    ScopedJniThreadState ts(env);
1479    bool okay = ensureLocalCapacity(env, capacity);
1480    if (!okay) {
1481        dvmThrowOutOfMemoryError("can't ensure local reference capacity");
1482    }
1483    return okay ? 0 : -1;
1484}
1485
1486
1487/*
1488 * Determine whether two Object references refer to the same underlying object.
1489 */
1490static jboolean IsSameObject(JNIEnv* env, jobject jref1, jobject jref2) {
1491    ScopedJniThreadState ts(env);
1492    Object* obj1 = dvmDecodeIndirectRef(env, jref1);
1493    Object* obj2 = dvmDecodeIndirectRef(env, jref2);
1494    return (obj1 == obj2);
1495}
1496
1497/*
1498 * Allocate a new object without invoking any constructors.
1499 */
1500static jobject AllocObject(JNIEnv* env, jclass jclazz) {
1501    ScopedJniThreadState ts(env);
1502
1503    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1504    if (!canAllocClass(clazz) ||
1505        (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)))
1506    {
1507        assert(dvmCheckException(ts.self()));
1508        return NULL;
1509    }
1510
1511    Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
1512    return addLocalReference(env, newObj);
1513}
1514
1515/*
1516 * Allocate a new object and invoke the supplied constructor.
1517 */
1518static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...) {
1519    ScopedJniThreadState ts(env);
1520    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1521
1522    if (!canAllocClass(clazz) || (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))) {
1523        assert(dvmCheckException(ts.self()));
1524        return NULL;
1525    }
1526
1527    Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
1528    jobject result = addLocalReference(env, newObj);
1529    if (newObj != NULL) {
1530        JValue unused;
1531        va_list args;
1532        va_start(args, methodID);
1533        dvmCallMethodV(ts.self(), (Method*) methodID, newObj, true, &unused, args);
1534        va_end(args);
1535    }
1536    return result;
1537}
1538
1539static jobject NewObjectV(JNIEnv* env, jclass jclazz, jmethodID methodID, va_list args) {
1540    ScopedJniThreadState ts(env);
1541    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1542
1543    if (!canAllocClass(clazz) || (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))) {
1544        assert(dvmCheckException(ts.self()));
1545        return NULL;
1546    }
1547
1548    Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
1549    jobject result = addLocalReference(env, newObj);
1550    if (newObj != NULL) {
1551        JValue unused;
1552        dvmCallMethodV(ts.self(), (Method*) methodID, newObj, true, &unused, args);
1553    }
1554    return result;
1555}
1556
1557static jobject NewObjectA(JNIEnv* env, jclass jclazz, jmethodID methodID, jvalue* args) {
1558    ScopedJniThreadState ts(env);
1559    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1560
1561    if (!canAllocClass(clazz) || (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))) {
1562        assert(dvmCheckException(ts.self()));
1563        return NULL;
1564    }
1565
1566    Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
1567    jobject result = addLocalReference(env, newObj);
1568    if (newObj != NULL) {
1569        JValue unused;
1570        dvmCallMethodA(ts.self(), (Method*) methodID, newObj, true, &unused, args);
1571    }
1572    return result;
1573}
1574
1575/*
1576 * Returns the class of an object.
1577 *
1578 * JNI spec says: obj must not be NULL.
1579 */
1580static jclass GetObjectClass(JNIEnv* env, jobject jobj) {
1581    ScopedJniThreadState ts(env);
1582
1583    assert(jobj != NULL);
1584
1585    Object* obj = dvmDecodeIndirectRef(env, jobj);
1586    return (jclass) addLocalReference(env, (Object*) obj->clazz);
1587}
1588
1589/*
1590 * Determine whether "obj" is an instance of "clazz".
1591 */
1592static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass jclazz) {
1593    ScopedJniThreadState ts(env);
1594
1595    assert(jclazz != NULL);
1596    if (jobj == NULL) {
1597        return true;
1598    }
1599
1600    Object* obj = dvmDecodeIndirectRef(env, jobj);
1601    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1602    return dvmInstanceof(obj->clazz, clazz);
1603}
1604
1605/*
1606 * Get a method ID for an instance method.
1607 *
1608 * While Dalvik bytecode has distinct instructions for virtual, super,
1609 * static, direct, and interface method invocation, JNI only provides
1610 * two functions for acquiring a method ID.  This call handles everything
1611 * but static methods.
1612 *
1613 * JNI defines <init> as an instance method, but Dalvik considers it a
1614 * "direct" method, so we have to special-case it here.
1615 *
1616 * Dalvik also puts all private methods into the "direct" list, so we
1617 * really need to just search both lists.
1618 */
1619static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name, const char* sig) {
1620    ScopedJniThreadState ts(env);
1621
1622    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1623    if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1624        assert(dvmCheckException(ts.self()));
1625    } else if (dvmIsInterfaceClass(clazz)) {
1626        Method* meth = dvmFindInterfaceMethodHierByDescriptor(clazz, name, sig);
1627        if (meth == NULL) {
1628            dvmThrowExceptionFmt(gDvm.exNoSuchMethodError,
1629                "no method with name='%s' signature='%s' in interface %s",
1630                name, sig, clazz->descriptor);
1631        }
1632        return (jmethodID) meth;
1633    }
1634    Method* meth = dvmFindVirtualMethodHierByDescriptor(clazz, name, sig);
1635    if (meth == NULL) {
1636        /* search private methods and constructors; non-hierarchical */
1637        meth = dvmFindDirectMethodByDescriptor(clazz, name, sig);
1638    }
1639    if (meth != NULL && dvmIsStaticMethod(meth)) {
1640        IF_LOGD() {
1641            char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
1642            LOGD("GetMethodID: not returning static method %s.%s %s",
1643                    clazz->descriptor, meth->name, desc);
1644            free(desc);
1645        }
1646        meth = NULL;
1647    }
1648    if (meth == NULL) {
1649        dvmThrowExceptionFmt(gDvm.exNoSuchMethodError,
1650                "no method with name='%s' signature='%s' in class %s",
1651                name, sig, clazz->descriptor);
1652    } else {
1653        /*
1654         * The method's class may not be the same as clazz, but if
1655         * it isn't this must be a virtual method and the class must
1656         * be a superclass (and, hence, already initialized).
1657         */
1658        assert(dvmIsClassInitialized(meth->clazz) || dvmIsClassInitializing(meth->clazz));
1659    }
1660    return (jmethodID) meth;
1661}
1662
1663/*
1664 * Get a field ID (instance fields).
1665 */
1666static jfieldID GetFieldID(JNIEnv* env, jclass jclazz, const char* name, const char* sig) {
1667    ScopedJniThreadState ts(env);
1668
1669    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1670
1671    if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1672        assert(dvmCheckException(ts.self()));
1673        return NULL;
1674    }
1675
1676    jfieldID id = (jfieldID) dvmFindInstanceFieldHier(clazz, name, sig);
1677    if (id == NULL) {
1678        dvmThrowExceptionFmt(gDvm.exNoSuchFieldError,
1679                "no field with name='%s' signature='%s' in class %s",
1680                name, sig, clazz->descriptor);
1681    }
1682    return id;
1683}
1684
1685/*
1686 * Get the method ID for a static method in a class.
1687 */
1688static jmethodID GetStaticMethodID(JNIEnv* env, jclass jclazz, const char* name, const char* sig) {
1689    ScopedJniThreadState ts(env);
1690
1691    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1692    if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1693        assert(dvmCheckException(ts.self()));
1694        return NULL;
1695    }
1696
1697    Method* meth = dvmFindDirectMethodHierByDescriptor(clazz, name, sig);
1698
1699    /* make sure it's static, not virtual+private */
1700    if (meth != NULL && !dvmIsStaticMethod(meth)) {
1701        IF_LOGD() {
1702            char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
1703            LOGD("GetStaticMethodID: not returning nonstatic method %s.%s %s",
1704                    clazz->descriptor, meth->name, desc);
1705            free(desc);
1706        }
1707        meth = NULL;
1708    }
1709
1710    jmethodID id = (jmethodID) meth;
1711    if (id == NULL) {
1712        dvmThrowExceptionFmt(gDvm.exNoSuchMethodError,
1713                "no static method with name='%s' signature='%s' in class %s",
1714                name, sig, clazz->descriptor);
1715    }
1716    return id;
1717}
1718
1719/*
1720 * Get a field ID (static fields).
1721 */
1722static jfieldID GetStaticFieldID(JNIEnv* env, jclass jclazz, const char* name, const char* sig) {
1723    ScopedJniThreadState ts(env);
1724
1725    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1726    if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1727        assert(dvmCheckException(ts.self()));
1728        return NULL;
1729    }
1730
1731    jfieldID id = (jfieldID) dvmFindStaticFieldHier(clazz, name, sig);
1732    if (id == NULL) {
1733        dvmThrowExceptionFmt(gDvm.exNoSuchFieldError,
1734                "no static field with name='%s' signature='%s' in class %s",
1735                name, sig, clazz->descriptor);
1736    }
1737    return id;
1738}
1739
1740/*
1741 * Get a static field.
1742 *
1743 * If we get an object reference, add it to the local refs list.
1744 */
1745#define GET_STATIC_TYPE_FIELD(_ctype, _jname, _isref)                       \
1746    static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass jclazz,      \
1747        jfieldID fieldID)                                                   \
1748    {                                                                       \
1749        UNUSED_PARAMETER(jclazz);                                           \
1750        ScopedJniThreadState ts(env);                                       \
1751        StaticField* sfield = (StaticField*) fieldID;                       \
1752        _ctype value;                                                       \
1753        if (dvmIsVolatileField(sfield)) {                                   \
1754            if (_isref) {   /* only when _ctype==jobject */                 \
1755                Object* obj = dvmGetStaticFieldObjectVolatile(sfield);      \
1756                value = (_ctype)(u4)addLocalReference(env, obj);            \
1757            } else {                                                        \
1758                value = (_ctype) dvmGetStaticField##_jname##Volatile(sfield);\
1759            }                                                               \
1760        } else {                                                            \
1761            if (_isref) {                                                   \
1762                Object* obj = dvmGetStaticFieldObject(sfield);              \
1763                value = (_ctype)(u4)addLocalReference(env, obj);            \
1764            } else {                                                        \
1765                value = (_ctype) dvmGetStaticField##_jname(sfield);         \
1766            }                                                               \
1767        }                                                                   \
1768        return value;                                                       \
1769    }
1770GET_STATIC_TYPE_FIELD(jobject, Object, true);
1771GET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
1772GET_STATIC_TYPE_FIELD(jbyte, Byte, false);
1773GET_STATIC_TYPE_FIELD(jchar, Char, false);
1774GET_STATIC_TYPE_FIELD(jshort, Short, false);
1775GET_STATIC_TYPE_FIELD(jint, Int, false);
1776GET_STATIC_TYPE_FIELD(jlong, Long, false);
1777GET_STATIC_TYPE_FIELD(jfloat, Float, false);
1778GET_STATIC_TYPE_FIELD(jdouble, Double, false);
1779
1780/*
1781 * Set a static field.
1782 */
1783#define SET_STATIC_TYPE_FIELD(_ctype, _ctype2, _jname, _isref)              \
1784    static void SetStatic##_jname##Field(JNIEnv* env, jclass jclazz,        \
1785        jfieldID fieldID, _ctype value)                                     \
1786    {                                                                       \
1787        UNUSED_PARAMETER(jclazz);                                           \
1788        ScopedJniThreadState ts(env);                                       \
1789        StaticField* sfield = (StaticField*) fieldID;                       \
1790        if (dvmIsVolatileField(sfield)) {                                   \
1791            if (_isref) {   /* only when _ctype==jobject */                 \
1792                Object* valObj =                                            \
1793                    dvmDecodeIndirectRef(env, (jobject)(u4)value);          \
1794                dvmSetStaticFieldObjectVolatile(sfield, valObj);            \
1795            } else {                                                        \
1796                dvmSetStaticField##_jname##Volatile(sfield, (_ctype2)value);\
1797            }                                                               \
1798        } else {                                                            \
1799            if (_isref) {                                                   \
1800                Object* valObj =                                            \
1801                    dvmDecodeIndirectRef(env, (jobject)(u4)value);          \
1802                dvmSetStaticFieldObject(sfield, valObj);                    \
1803            } else {                                                        \
1804                dvmSetStaticField##_jname(sfield, (_ctype2)value);          \
1805            }                                                               \
1806        }                                                                   \
1807    }
1808SET_STATIC_TYPE_FIELD(jobject, Object*, Object, true);
1809SET_STATIC_TYPE_FIELD(jboolean, bool, Boolean, false);
1810SET_STATIC_TYPE_FIELD(jbyte, s1, Byte, false);
1811SET_STATIC_TYPE_FIELD(jchar, u2, Char, false);
1812SET_STATIC_TYPE_FIELD(jshort, s2, Short, false);
1813SET_STATIC_TYPE_FIELD(jint, s4, Int, false);
1814SET_STATIC_TYPE_FIELD(jlong, s8, Long, false);
1815SET_STATIC_TYPE_FIELD(jfloat, float, Float, false);
1816SET_STATIC_TYPE_FIELD(jdouble, double, Double, false);
1817
1818/*
1819 * Get an instance field.
1820 *
1821 * If we get an object reference, add it to the local refs list.
1822 */
1823#define GET_TYPE_FIELD(_ctype, _jname, _isref)                              \
1824    static _ctype Get##_jname##Field(JNIEnv* env, jobject jobj,             \
1825        jfieldID fieldID)                                                   \
1826    {                                                                       \
1827        ScopedJniThreadState ts(env);                                       \
1828        Object* obj = dvmDecodeIndirectRef(env, jobj);                      \
1829        InstField* field = (InstField*) fieldID;                            \
1830        _ctype value;                                                       \
1831        if (dvmIsVolatileField(field)) {                            \
1832            if (_isref) {   /* only when _ctype==jobject */                 \
1833                Object* valObj =                                            \
1834                    dvmGetFieldObjectVolatile(obj, field->byteOffset);      \
1835                value = (_ctype)(u4)addLocalReference(env, valObj);         \
1836            } else {                                                        \
1837                value = (_ctype)                                            \
1838                    dvmGetField##_jname##Volatile(obj, field->byteOffset);  \
1839            }                                                               \
1840        } else {                                                            \
1841            if (_isref) {                                                   \
1842                Object* valObj = dvmGetFieldObject(obj, field->byteOffset); \
1843                value = (_ctype)(u4)addLocalReference(env, valObj);         \
1844            } else {                                                        \
1845                value = (_ctype) dvmGetField##_jname(obj, field->byteOffset);\
1846            }                                                               \
1847        }                                                                   \
1848        return value;                                                       \
1849    }
1850GET_TYPE_FIELD(jobject, Object, true);
1851GET_TYPE_FIELD(jboolean, Boolean, false);
1852GET_TYPE_FIELD(jbyte, Byte, false);
1853GET_TYPE_FIELD(jchar, Char, false);
1854GET_TYPE_FIELD(jshort, Short, false);
1855GET_TYPE_FIELD(jint, Int, false);
1856GET_TYPE_FIELD(jlong, Long, false);
1857GET_TYPE_FIELD(jfloat, Float, false);
1858GET_TYPE_FIELD(jdouble, Double, false);
1859
1860/*
1861 * Set an instance field.
1862 */
1863#define SET_TYPE_FIELD(_ctype, _ctype2, _jname, _isref)                     \
1864    static void Set##_jname##Field(JNIEnv* env, jobject jobj,               \
1865        jfieldID fieldID, _ctype value)                                     \
1866    {                                                                       \
1867        ScopedJniThreadState ts(env);                                       \
1868        Object* obj = dvmDecodeIndirectRef(env, jobj);                      \
1869        InstField* field = (InstField*) fieldID;                            \
1870        if (dvmIsVolatileField(field)) {                                    \
1871            if (_isref) {   /* only when _ctype==jobject */                 \
1872                Object* valObj =                                            \
1873                    dvmDecodeIndirectRef(env, (jobject)(u4)value);          \
1874                dvmSetFieldObjectVolatile(obj, field->byteOffset, valObj);  \
1875            } else {                                                        \
1876                dvmSetField##_jname##Volatile(obj,                          \
1877                    field->byteOffset, (_ctype2)value);                     \
1878            }                                                               \
1879        } else {                                                            \
1880            if (_isref) {                                                   \
1881                Object* valObj =                                            \
1882                    dvmDecodeIndirectRef(env, (jobject)(u4)value);          \
1883                dvmSetFieldObject(obj, field->byteOffset, valObj);          \
1884            } else {                                                        \
1885                dvmSetField##_jname(obj,                                    \
1886                    field->byteOffset, (_ctype2)value);                     \
1887            }                                                               \
1888        }                                                                   \
1889    }
1890SET_TYPE_FIELD(jobject, Object*, Object, true);
1891SET_TYPE_FIELD(jboolean, bool, Boolean, false);
1892SET_TYPE_FIELD(jbyte, s1, Byte, false);
1893SET_TYPE_FIELD(jchar, u2, Char, false);
1894SET_TYPE_FIELD(jshort, s2, Short, false);
1895SET_TYPE_FIELD(jint, s4, Int, false);
1896SET_TYPE_FIELD(jlong, s8, Long, false);
1897SET_TYPE_FIELD(jfloat, float, Float, false);
1898SET_TYPE_FIELD(jdouble, double, Double, false);
1899
1900/*
1901 * Make a virtual method call.
1902 *
1903 * Three versions (..., va_list, jvalue[]) for each return type.  If we're
1904 * returning an Object, we have to add it to the local references table.
1905 */
1906#define CALL_VIRTUAL(_ctype, _jname, _retfail, _retok, _isref)              \
1907    static _ctype Call##_jname##Method(JNIEnv* env, jobject jobj,           \
1908        jmethodID methodID, ...)                                            \
1909    {                                                                       \
1910        ScopedJniThreadState ts(env);                                       \
1911        Object* obj = dvmDecodeIndirectRef(env, jobj);                      \
1912        const Method* meth;                                                 \
1913        va_list args;                                                       \
1914        JValue result;                                                      \
1915        meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID);      \
1916        if (meth == NULL) {                                                 \
1917            return _retfail;                                                \
1918        }                                                                   \
1919        va_start(args, methodID);                                           \
1920        dvmCallMethodV(ts.self(), meth, obj, true, &result, args);          \
1921        va_end(args);                                                       \
1922        if (_isref && !dvmCheckException(ts.self()))                        \
1923            result.l = (Object*)addLocalReference(env, result.l);           \
1924        return _retok;                                                      \
1925    }                                                                       \
1926    static _ctype Call##_jname##MethodV(JNIEnv* env, jobject jobj,          \
1927        jmethodID methodID, va_list args)                                   \
1928    {                                                                       \
1929        ScopedJniThreadState ts(env);                                       \
1930        Object* obj = dvmDecodeIndirectRef(env, jobj);                      \
1931        const Method* meth;                                                 \
1932        JValue result;                                                      \
1933        meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID);      \
1934        if (meth == NULL) {                                                 \
1935            return _retfail;                                                \
1936        }                                                                   \
1937        dvmCallMethodV(ts.self(), meth, obj, true, &result, args);          \
1938        if (_isref && !dvmCheckException(ts.self()))                        \
1939            result.l = (Object*)addLocalReference(env, result.l);           \
1940        return _retok;                                                      \
1941    }                                                                       \
1942    static _ctype Call##_jname##MethodA(JNIEnv* env, jobject jobj,          \
1943        jmethodID methodID, jvalue* args)                                   \
1944    {                                                                       \
1945        ScopedJniThreadState ts(env);                                       \
1946        Object* obj = dvmDecodeIndirectRef(env, jobj);                      \
1947        const Method* meth;                                                 \
1948        JValue result;                                                      \
1949        meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID);      \
1950        if (meth == NULL) {                                                 \
1951            return _retfail;                                                \
1952        }                                                                   \
1953        dvmCallMethodA(ts.self(), meth, obj, true, &result, args);          \
1954        if (_isref && !dvmCheckException(ts.self()))                        \
1955            result.l = (Object*)addLocalReference(env, result.l);           \
1956        return _retok;                                                      \
1957    }
1958CALL_VIRTUAL(jobject, Object, NULL, (jobject) result.l, true);
1959CALL_VIRTUAL(jboolean, Boolean, 0, result.z, false);
1960CALL_VIRTUAL(jbyte, Byte, 0, result.b, false);
1961CALL_VIRTUAL(jchar, Char, 0, result.c, false);
1962CALL_VIRTUAL(jshort, Short, 0, result.s, false);
1963CALL_VIRTUAL(jint, Int, 0, result.i, false);
1964CALL_VIRTUAL(jlong, Long, 0, result.j, false);
1965CALL_VIRTUAL(jfloat, Float, 0.0f, result.f, false);
1966CALL_VIRTUAL(jdouble, Double, 0.0, result.d, false);
1967CALL_VIRTUAL(void, Void, , , false);
1968
1969/*
1970 * Make a "non-virtual" method call.  We're still calling a virtual method,
1971 * but this time we're not doing an indirection through the object's vtable.
1972 * The "clazz" parameter defines which implementation of a method we want.
1973 *
1974 * Three versions (..., va_list, jvalue[]) for each return type.
1975 */
1976#define CALL_NONVIRTUAL(_ctype, _jname, _retfail, _retok, _isref)           \
1977    static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, jobject jobj, \
1978        jclass jclazz, jmethodID methodID, ...)                             \
1979    {                                                                       \
1980        ScopedJniThreadState ts(env);                                       \
1981        Object* obj = dvmDecodeIndirectRef(env, jobj);                      \
1982        ClassObject* clazz =                                                \
1983            (ClassObject*) dvmDecodeIndirectRef(env, jclazz);               \
1984        const Method* meth;                                                 \
1985        va_list args;                                                       \
1986        JValue result;                                                      \
1987        meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID);           \
1988        if (meth == NULL) {                                                 \
1989            return _retfail;                                                \
1990        }                                                                   \
1991        va_start(args, methodID);                                           \
1992        dvmCallMethodV(ts.self(), meth, obj, true, &result, args);          \
1993        if (_isref && !dvmCheckException(ts.self()))                        \
1994            result.l = (Object*)addLocalReference(env, result.l);           \
1995        va_end(args);                                                       \
1996        return _retok;                                                      \
1997    }                                                                       \
1998    static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject jobj,\
1999        jclass jclazz, jmethodID methodID, va_list args)                    \
2000    {                                                                       \
2001        ScopedJniThreadState ts(env);                                       \
2002        Object* obj = dvmDecodeIndirectRef(env, jobj);                      \
2003        ClassObject* clazz =                                                \
2004            (ClassObject*) dvmDecodeIndirectRef(env, jclazz);               \
2005        const Method* meth;                                                 \
2006        JValue result;                                                      \
2007        meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID);           \
2008        if (meth == NULL) {                                                 \
2009            return _retfail;                                                \
2010        }                                                                   \
2011        dvmCallMethodV(ts.self(), meth, obj, true, &result, args);          \
2012        if (_isref && !dvmCheckException(ts.self()))                        \
2013            result.l = (Object*)addLocalReference(env, result.l);           \
2014        return _retok;                                                      \
2015    }                                                                       \
2016    static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject jobj,\
2017        jclass jclazz, jmethodID methodID, jvalue* args)                    \
2018    {                                                                       \
2019        ScopedJniThreadState ts(env);                                       \
2020        Object* obj = dvmDecodeIndirectRef(env, jobj);                      \
2021        ClassObject* clazz =                                                \
2022            (ClassObject*) dvmDecodeIndirectRef(env, jclazz);               \
2023        const Method* meth;                                                 \
2024        JValue result;                                                      \
2025        meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID);           \
2026        if (meth == NULL) {                                                 \
2027            return _retfail;                                                \
2028        }                                                                   \
2029        dvmCallMethodA(ts.self(), meth, obj, true, &result, args);          \
2030        if (_isref && !dvmCheckException(ts.self()))                        \
2031            result.l = (Object*)addLocalReference(env, result.l);           \
2032        return _retok;                                                      \
2033    }
2034CALL_NONVIRTUAL(jobject, Object, NULL, (jobject) result.l, true);
2035CALL_NONVIRTUAL(jboolean, Boolean, 0, result.z, false);
2036CALL_NONVIRTUAL(jbyte, Byte, 0, result.b, false);
2037CALL_NONVIRTUAL(jchar, Char, 0, result.c, false);
2038CALL_NONVIRTUAL(jshort, Short, 0, result.s, false);
2039CALL_NONVIRTUAL(jint, Int, 0, result.i, false);
2040CALL_NONVIRTUAL(jlong, Long, 0, result.j, false);
2041CALL_NONVIRTUAL(jfloat, Float, 0.0f, result.f, false);
2042CALL_NONVIRTUAL(jdouble, Double, 0.0, result.d, false);
2043CALL_NONVIRTUAL(void, Void, , , false);
2044
2045
2046/*
2047 * Call a static method.
2048 */
2049#define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref)               \
2050    static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass jclazz,    \
2051        jmethodID methodID, ...)                                            \
2052    {                                                                       \
2053        UNUSED_PARAMETER(jclazz);                                           \
2054        ScopedJniThreadState ts(env);                                       \
2055        JValue result;                                                      \
2056        va_list args;                                                       \
2057        va_start(args, methodID);                                           \
2058        dvmCallMethodV(ts.self(), (Method*)methodID, NULL, true, &result, args);\
2059        va_end(args);                                                       \
2060        if (_isref && !dvmCheckException(ts.self()))                        \
2061            result.l = (Object*)addLocalReference(env, result.l);           \
2062        return _retok;                                                      \
2063    }                                                                       \
2064    static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz,   \
2065        jmethodID methodID, va_list args)                                   \
2066    {                                                                       \
2067        UNUSED_PARAMETER(jclazz);                                           \
2068        ScopedJniThreadState ts(env);                                       \
2069        JValue result;                                                      \
2070        dvmCallMethodV(ts.self(), (Method*)methodID, NULL, true, &result, args);\
2071        if (_isref && !dvmCheckException(ts.self()))                        \
2072            result.l = (Object*)addLocalReference(env, result.l);           \
2073        return _retok;                                                      \
2074    }                                                                       \
2075    static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass jclazz,   \
2076        jmethodID methodID, jvalue* args)                                   \
2077    {                                                                       \
2078        UNUSED_PARAMETER(jclazz);                                           \
2079        ScopedJniThreadState ts(env);                                       \
2080        JValue result;                                                      \
2081        dvmCallMethodA(ts.self(), (Method*)methodID, NULL, true, &result, args);\
2082        if (_isref && !dvmCheckException(ts.self()))                        \
2083            result.l = (Object*)addLocalReference(env, result.l);           \
2084        return _retok;                                                      \
2085    }
2086CALL_STATIC(jobject, Object, NULL, (jobject) result.l, true);
2087CALL_STATIC(jboolean, Boolean, 0, result.z, false);
2088CALL_STATIC(jbyte, Byte, 0, result.b, false);
2089CALL_STATIC(jchar, Char, 0, result.c, false);
2090CALL_STATIC(jshort, Short, 0, result.s, false);
2091CALL_STATIC(jint, Int, 0, result.i, false);
2092CALL_STATIC(jlong, Long, 0, result.j, false);
2093CALL_STATIC(jfloat, Float, 0.0f, result.f, false);
2094CALL_STATIC(jdouble, Double, 0.0, result.d, false);
2095CALL_STATIC(void, Void, , , false);
2096
2097/*
2098 * Create a new String from Unicode data.
2099 *
2100 * If "len" is zero, we will return an empty string even if "unicodeChars"
2101 * is NULL.  (The JNI spec is vague here.)
2102 */
2103static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len) {
2104    ScopedJniThreadState ts(env);
2105    StringObject* jstr = dvmCreateStringFromUnicode(unicodeChars, len);
2106    if (jstr == NULL) {
2107        return NULL;
2108    }
2109    dvmReleaseTrackedAlloc((Object*) jstr, NULL);
2110    return (jstring) addLocalReference(env, (Object*) jstr);
2111}
2112
2113/*
2114 * Return the length of a String in Unicode character units.
2115 */
2116static jsize GetStringLength(JNIEnv* env, jstring jstr) {
2117    ScopedJniThreadState ts(env);
2118    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2119    return dvmStringLen(strObj);
2120}
2121
2122
2123/*
2124 * Get a string's character data.
2125 *
2126 * The result is guaranteed to be valid until ReleaseStringChars is
2127 * called, which means we have to pin it or return a copy.
2128 */
2129static const jchar* GetStringChars(JNIEnv* env, jstring jstr, jboolean* isCopy) {
2130    ScopedJniThreadState ts(env);
2131
2132    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2133    ArrayObject* strChars = dvmStringCharArray(strObj);
2134
2135    pinPrimitiveArray(strChars);
2136
2137    const u2* data = dvmStringChars(strObj);
2138    if (isCopy != NULL) {
2139        *isCopy = JNI_FALSE;
2140    }
2141    return (jchar*) data;
2142}
2143
2144/*
2145 * Release our grip on some characters from a string.
2146 */
2147static void ReleaseStringChars(JNIEnv* env, jstring jstr, const jchar* chars) {
2148    ScopedJniThreadState ts(env);
2149    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2150    ArrayObject* strChars = dvmStringCharArray(strObj);
2151    unpinPrimitiveArray(strChars);
2152}
2153
2154/*
2155 * Create a new java.lang.String object from chars in modified UTF-8 form.
2156 *
2157 * The spec doesn't say how to handle a NULL string.  Popular desktop VMs
2158 * accept it and return a NULL pointer in response.
2159 */
2160static jstring NewStringUTF(JNIEnv* env, const char* bytes) {
2161    ScopedJniThreadState ts(env);
2162    if (bytes == NULL) {
2163        return NULL;
2164    }
2165    /* note newStr could come back NULL on OOM */
2166    StringObject* newStr = dvmCreateStringFromCstr(bytes);
2167    jstring result = (jstring) addLocalReference(env, (Object*) newStr);
2168    dvmReleaseTrackedAlloc((Object*)newStr, NULL);
2169    return result;
2170}
2171
2172/*
2173 * Return the length in bytes of the modified UTF-8 form of the string.
2174 */
2175static jsize GetStringUTFLength(JNIEnv* env, jstring jstr) {
2176    ScopedJniThreadState ts(env);
2177    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2178    return dvmStringUtf8ByteLen(strObj);
2179}
2180
2181/*
2182 * Convert "string" to modified UTF-8 and return a pointer.  The returned
2183 * value must be released with ReleaseStringUTFChars.
2184 *
2185 * According to the JNI reference, "Returns a pointer to a UTF-8 string,
2186 * or NULL if the operation fails. Returns NULL if and only if an invocation
2187 * of this function has thrown an exception."
2188 *
2189 * The behavior here currently follows that of other open-source VMs, which
2190 * quietly return NULL if "string" is NULL.  We should consider throwing an
2191 * NPE.  (The CheckJNI code blows up if you try to pass in a NULL string,
2192 * which should catch this sort of thing during development.)  Certain other
2193 * VMs will crash with a segmentation fault.
2194 */
2195static const char* GetStringUTFChars(JNIEnv* env, jstring jstr, jboolean* isCopy) {
2196    ScopedJniThreadState ts(env);
2197    if (jstr == NULL) {
2198        /* this shouldn't happen; throw NPE? */
2199        return NULL;
2200    }
2201    if (isCopy != NULL) {
2202        *isCopy = JNI_TRUE;
2203    }
2204    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2205    char* newStr = dvmCreateCstrFromString(strObj);
2206    if (newStr == NULL) {
2207        /* assume memory failure */
2208        dvmThrowOutOfMemoryError("native heap string alloc failed");
2209    }
2210    return newStr;
2211}
2212
2213/*
2214 * Release a string created by GetStringUTFChars().
2215 */
2216static void ReleaseStringUTFChars(JNIEnv* env, jstring jstr, const char* utf) {
2217    ScopedJniThreadState ts(env);
2218    free((char*) utf);
2219}
2220
2221/*
2222 * Return the capacity of the array.
2223 */
2224static jsize GetArrayLength(JNIEnv* env, jarray jarr) {
2225    ScopedJniThreadState ts(env);
2226    ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
2227    return arrObj->length;
2228}
2229
2230/*
2231 * Construct a new array that holds objects from class "elementClass".
2232 */
2233static jobjectArray NewObjectArray(JNIEnv* env, jsize length,
2234    jclass jelementClass, jobject jinitialElement)
2235{
2236    ScopedJniThreadState ts(env);
2237
2238    if (jelementClass == NULL) {
2239        dvmThrowNullPointerException("JNI NewObjectArray element class");
2240        return NULL;
2241    }
2242
2243    ClassObject* elemClassObj = (ClassObject*) dvmDecodeIndirectRef(env, jelementClass);
2244    ClassObject* arrayClass = dvmFindArrayClassForElement(elemClassObj);
2245    ArrayObject* newObj = dvmAllocArrayByClass(arrayClass, length, ALLOC_DEFAULT);
2246    if (newObj == NULL) {
2247        assert(dvmCheckException(ts.self()));
2248        return NULL;
2249    }
2250    jobjectArray newArray = (jobjectArray) addLocalReference(env, (Object*) newObj);
2251    dvmReleaseTrackedAlloc((Object*) newObj, NULL);
2252
2253    /*
2254     * Initialize the array.
2255     */
2256    if (jinitialElement != NULL) {
2257        Object* initialElement = dvmDecodeIndirectRef(env, jinitialElement);
2258        Object** arrayData = (Object**) (void*) newObj->contents;
2259        for (jsize i = 0; i < length; ++i) {
2260            arrayData[i] = initialElement;
2261        }
2262    }
2263
2264    return newArray;
2265}
2266
2267static bool checkArrayElementBounds(ArrayObject* arrayObj, jsize index) {
2268    assert(arrayObj != NULL);
2269    if (index < 0 || index >= (int) arrayObj->length) {
2270        dvmThrowArrayIndexOutOfBoundsException(arrayObj->length, index);
2271        return false;
2272    }
2273    return true;
2274}
2275
2276/*
2277 * Get one element of an Object array.
2278 *
2279 * Add the object to the local references table in case the array goes away.
2280 */
2281static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray jarr, jsize index) {
2282    ScopedJniThreadState ts(env);
2283
2284    ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
2285    if (!checkArrayElementBounds(arrayObj, index)) {
2286        return NULL;
2287    }
2288
2289    Object* value = ((Object**) (void*) arrayObj->contents)[index];
2290    return addLocalReference(env, value);
2291}
2292
2293/*
2294 * Set one element of an Object array.
2295 */
2296static void SetObjectArrayElement(JNIEnv* env, jobjectArray jarr, jsize index, jobject jobj) {
2297    ScopedJniThreadState ts(env);
2298
2299    ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
2300    if (!checkArrayElementBounds(arrayObj, index)) {
2301        return;
2302    }
2303
2304    //LOGV("JNI: set element %d in array %p to %p", index, array, value);
2305
2306    Object* obj = dvmDecodeIndirectRef(env, jobj);
2307    dvmSetObjectArrayElement(arrayObj, index, obj);
2308}
2309
2310/*
2311 * Create a new array of primitive elements.
2312 */
2313#define NEW_PRIMITIVE_ARRAY(_artype, _jname, _typechar) \
2314    static _artype New##_jname##Array(JNIEnv* env, jsize length) { \
2315        ScopedJniThreadState ts(env); \
2316        ArrayObject* arrayObj = dvmAllocPrimitiveArray(_typechar, length, ALLOC_DEFAULT); \
2317        if (arrayObj == NULL) { \
2318            return NULL; \
2319        } \
2320        _artype result = (_artype) addLocalReference(env, (Object*) arrayObj); \
2321        dvmReleaseTrackedAlloc((Object*) arrayObj, NULL); \
2322        return result; \
2323    }
2324NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean, 'Z');
2325NEW_PRIMITIVE_ARRAY(jbyteArray, Byte, 'B');
2326NEW_PRIMITIVE_ARRAY(jcharArray, Char, 'C');
2327NEW_PRIMITIVE_ARRAY(jshortArray, Short, 'S');
2328NEW_PRIMITIVE_ARRAY(jintArray, Int, 'I');
2329NEW_PRIMITIVE_ARRAY(jlongArray, Long, 'J');
2330NEW_PRIMITIVE_ARRAY(jfloatArray, Float, 'F');
2331NEW_PRIMITIVE_ARRAY(jdoubleArray, Double, 'D');
2332
2333/*
2334 * Get a pointer to a C array of primitive elements from an array object
2335 * of the matching type.
2336 *
2337 * In a compacting GC, we either need to return a copy of the elements or
2338 * "pin" the memory.  Otherwise we run the risk of native code using the
2339 * buffer as the destination of e.g. a blocking read() call that wakes up
2340 * during a GC.
2341 */
2342#define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
2343    static _ctype* Get##_jname##ArrayElements(JNIEnv* env, \
2344        _ctype##Array jarr, jboolean* isCopy) \
2345    { \
2346        ScopedJniThreadState ts(env); \
2347        ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
2348        pinPrimitiveArray(arrayObj); \
2349        _ctype* data = (_ctype*) (void*) arrayObj->contents; \
2350        if (isCopy != NULL) { \
2351            *isCopy = JNI_FALSE; \
2352        } \
2353        return data; \
2354    }
2355
2356/*
2357 * Release the storage locked down by the "get" function.
2358 *
2359 * The spec says, "'mode' has no effect if 'elems' is not a copy of the
2360 * elements in 'array'."  They apparently did not anticipate the need to
2361 * un-pin memory.
2362 */
2363#define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname)                    \
2364    static void Release##_jname##ArrayElements(JNIEnv* env,                 \
2365        _ctype##Array jarr, _ctype* elems, jint mode)                       \
2366    {                                                                       \
2367        UNUSED_PARAMETER(elems);                                            \
2368        if (mode != JNI_COMMIT) {                                           \
2369            ScopedJniThreadState ts(env);                                   \
2370            ArrayObject* arrayObj =                                         \
2371                (ArrayObject*) dvmDecodeIndirectRef(env, jarr);             \
2372            unpinPrimitiveArray(arrayObj);                                  \
2373        }                                                                   \
2374    }
2375
2376static void throwArrayRegionOutOfBounds(ArrayObject* arrayObj, jsize start,
2377    jsize len, const char* arrayIdentifier)
2378{
2379    dvmThrowExceptionFmt(gDvm.exArrayIndexOutOfBoundsException,
2380        "%s offset=%d length=%d %s.length=%d",
2381        arrayObj->clazz->descriptor, start, len, arrayIdentifier,
2382        arrayObj->length);
2383}
2384
2385/*
2386 * Copy a section of a primitive array to a buffer.
2387 */
2388#define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
2389    static void Get##_jname##ArrayRegion(JNIEnv* env, \
2390        _ctype##Array jarr, jsize start, jsize len, _ctype* buf) \
2391    { \
2392        ScopedJniThreadState ts(env); \
2393        ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
2394        _ctype* data = (_ctype*) (void*) arrayObj->contents; \
2395        if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
2396            throwArrayRegionOutOfBounds(arrayObj, start, len, "src"); \
2397        } else { \
2398            memcpy(buf, data + start, len * sizeof(_ctype)); \
2399        } \
2400    }
2401
2402/*
2403 * Copy a section of a primitive array from a buffer.
2404 */
2405#define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
2406    static void Set##_jname##ArrayRegion(JNIEnv* env, \
2407        _ctype##Array jarr, jsize start, jsize len, const _ctype* buf) \
2408    { \
2409        ScopedJniThreadState ts(env); \
2410        ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
2411        _ctype* data = (_ctype*) (void*) arrayObj->contents; \
2412        if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
2413            throwArrayRegionOutOfBounds(arrayObj, start, len, "dst"); \
2414        } else { \
2415            memcpy(data + start, buf, len * sizeof(_ctype)); \
2416        } \
2417    }
2418
2419/*
2420 * 4-in-1:
2421 *  Get<Type>ArrayElements
2422 *  Release<Type>ArrayElements
2423 *  Get<Type>ArrayRegion
2424 *  Set<Type>ArrayRegion
2425 */
2426#define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname)                           \
2427    GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname);                           \
2428    RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname);                       \
2429    GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);                             \
2430    SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);
2431
2432PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean);
2433PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte);
2434PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char);
2435PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short);
2436PRIMITIVE_ARRAY_FUNCTIONS(jint, Int);
2437PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long);
2438PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float);
2439PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double);
2440
2441/*
2442 * Register one or more native functions in one class.
2443 *
2444 * This can be called multiple times on the same method, allowing the
2445 * caller to redefine the method implementation at will.
2446 */
2447static jint RegisterNatives(JNIEnv* env, jclass jclazz,
2448    const JNINativeMethod* methods, jint nMethods)
2449{
2450    ScopedJniThreadState ts(env);
2451
2452    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2453
2454    if (gDvm.verboseJni) {
2455        LOGI("[Registering JNI native methods for class %s]",
2456            clazz->descriptor);
2457    }
2458
2459    for (int i = 0; i < nMethods; i++) {
2460        if (!dvmRegisterJNIMethod(clazz, methods[i].name,
2461                methods[i].signature, methods[i].fnPtr))
2462        {
2463            return JNI_ERR;
2464        }
2465    }
2466    return JNI_OK;
2467}
2468
2469/*
2470 * Un-register all native methods associated with the class.
2471 *
2472 * The JNI docs refer to this as a way to reload/relink native libraries,
2473 * and say it "should not be used in normal native code".  In particular,
2474 * there is no need to do this during shutdown, and you do not need to do
2475 * this before redefining a method implementation with RegisterNatives.
2476 *
2477 * It's chiefly useful for a native "plugin"-style library that wasn't
2478 * loaded with System.loadLibrary() (since there's no way to unload those).
2479 * For example, the library could upgrade itself by:
2480 *
2481 *  1. call UnregisterNatives to unbind the old methods
2482 *  2. ensure that no code is still executing inside it (somehow)
2483 *  3. dlclose() the library
2484 *  4. dlopen() the new library
2485 *  5. use RegisterNatives to bind the methods from the new library
2486 *
2487 * The above can work correctly without the UnregisterNatives call, but
2488 * creates a window of opportunity in which somebody might try to call a
2489 * method that is pointing at unmapped memory, crashing the VM.  In theory
2490 * the same guards that prevent dlclose() from unmapping executing code could
2491 * prevent that anyway, but with this we can be more thorough and also deal
2492 * with methods that only exist in the old or new form of the library (maybe
2493 * the lib wants to try the call and catch the UnsatisfiedLinkError).
2494 */
2495static jint UnregisterNatives(JNIEnv* env, jclass jclazz) {
2496    ScopedJniThreadState ts(env);
2497
2498    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2499    if (gDvm.verboseJni) {
2500        LOGI("[Unregistering JNI native methods for class %s]",
2501            clazz->descriptor);
2502    }
2503    dvmUnregisterJNINativeMethods(clazz);
2504    return JNI_OK;
2505}
2506
2507/*
2508 * Lock the monitor.
2509 *
2510 * We have to track all monitor enters and exits, so that we can undo any
2511 * outstanding synchronization before the thread exits.
2512 */
2513static jint MonitorEnter(JNIEnv* env, jobject jobj) {
2514    ScopedJniThreadState ts(env);
2515    Object* obj = dvmDecodeIndirectRef(env, jobj);
2516    dvmLockObject(ts.self(), obj);
2517    trackMonitorEnter(ts.self(), obj);
2518    return JNI_OK;
2519}
2520
2521/*
2522 * Unlock the monitor.
2523 *
2524 * Throws an IllegalMonitorStateException if the current thread
2525 * doesn't own the monitor.  (dvmUnlockObject() takes care of the throw.)
2526 *
2527 * According to the 1.6 spec, it's legal to call here with an exception
2528 * pending.  If this fails, we'll stomp the original exception.
2529 */
2530static jint MonitorExit(JNIEnv* env, jobject jobj) {
2531    ScopedJniThreadState ts(env);
2532    Object* obj = dvmDecodeIndirectRef(env, jobj);
2533    bool success = dvmUnlockObject(ts.self(), obj);
2534    if (success) {
2535        trackMonitorExit(ts.self(), obj);
2536    }
2537    return success ? JNI_OK : JNI_ERR;
2538}
2539
2540/*
2541 * Return the JavaVM interface associated with the current thread.
2542 */
2543static jint GetJavaVM(JNIEnv* env, JavaVM** vm) {
2544    ScopedJniThreadState ts(env);
2545    *vm = gDvmJni.jniVm;
2546    return (*vm == NULL) ? JNI_ERR : JNI_OK;
2547}
2548
2549/*
2550 * Copies "len" Unicode characters, from offset "start".
2551 */
2552static void GetStringRegion(JNIEnv* env, jstring jstr, jsize start, jsize len, jchar* buf) {
2553    ScopedJniThreadState ts(env);
2554    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2555    int strLen = dvmStringLen(strObj);
2556    if (((start|len) < 0) || (start + len > dvmStringLen(strObj))) {
2557        dvmThrowStringIndexOutOfBoundsExceptionWithRegion(strLen, start, len);
2558        return;
2559    }
2560    memcpy(buf, dvmStringChars(strObj) + start, len * sizeof(u2));
2561}
2562
2563/*
2564 * Translates "len" Unicode characters, from offset "start", into
2565 * modified UTF-8 encoding.
2566 */
2567static void GetStringUTFRegion(JNIEnv* env, jstring jstr, jsize start, jsize len, char* buf) {
2568    ScopedJniThreadState ts(env);
2569    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2570    int strLen = dvmStringLen(strObj);
2571    if (((start|len) < 0) || (start + len > dvmStringLen(strObj))) {
2572        dvmThrowStringIndexOutOfBoundsExceptionWithRegion(strLen, start, len);
2573        return;
2574    }
2575    dvmCreateCstrFromStringRegion(strObj, start, len, buf);
2576}
2577
2578/*
2579 * Get a raw pointer to array data.
2580 *
2581 * The caller is expected to call "release" before doing any JNI calls
2582 * or blocking I/O operations.
2583 *
2584 * We need to pin the memory or block GC.
2585 */
2586static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray jarr, jboolean* isCopy) {
2587    ScopedJniThreadState ts(env);
2588    ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
2589    pinPrimitiveArray(arrayObj);
2590    void* data = arrayObj->contents;
2591    if (isCopy != NULL) {
2592        *isCopy = JNI_FALSE;
2593    }
2594    return data;
2595}
2596
2597/*
2598 * Release an array obtained with GetPrimitiveArrayCritical.
2599 */
2600static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray jarr, void* carray, jint mode) {
2601    if (mode != JNI_COMMIT) {
2602        ScopedJniThreadState ts(env);
2603        ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
2604        unpinPrimitiveArray(arrayObj);
2605    }
2606}
2607
2608/*
2609 * Like GetStringChars, but with restricted use.
2610 */
2611static const jchar* GetStringCritical(JNIEnv* env, jstring jstr, jboolean* isCopy) {
2612    ScopedJniThreadState ts(env);
2613
2614    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2615    ArrayObject* strChars = dvmStringCharArray(strObj);
2616
2617    pinPrimitiveArray(strChars);
2618
2619    const u2* data = dvmStringChars(strObj);
2620    if (isCopy != NULL) {
2621        *isCopy = JNI_FALSE;
2622    }
2623    return (jchar*) data;
2624}
2625
2626/*
2627 * Like ReleaseStringChars, but with restricted use.
2628 */
2629static void ReleaseStringCritical(JNIEnv* env, jstring jstr, const jchar* carray) {
2630    ScopedJniThreadState ts(env);
2631    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2632    ArrayObject* strChars = dvmStringCharArray(strObj);
2633    unpinPrimitiveArray(strChars);
2634}
2635
2636/*
2637 * Create a new weak global reference.
2638 */
2639static jweak NewWeakGlobalRef(JNIEnv* env, jobject jobj) {
2640    ScopedJniThreadState ts(env);
2641    Object *obj = dvmDecodeIndirectRef(env, jobj);
2642    return (jweak) addWeakGlobalReference(obj);
2643}
2644
2645/*
2646 * Delete the specified weak global reference.
2647 */
2648static void DeleteWeakGlobalRef(JNIEnv* env, jweak wref) {
2649    ScopedJniThreadState ts(env);
2650    deleteWeakGlobalReference(wref);
2651}
2652
2653/*
2654 * Quick check for pending exceptions.
2655 *
2656 * TODO: we should be able to skip the enter/exit macros here.
2657 */
2658static jboolean ExceptionCheck(JNIEnv* env) {
2659    ScopedJniThreadState ts(env);
2660    return dvmCheckException(ts.self());
2661}
2662
2663/*
2664 * Returns the type of the object referred to by "obj".  It can be local,
2665 * global, or weak global.
2666 *
2667 * In the current implementation, references can be global and local at
2668 * the same time, so while the return value is accurate it may not tell
2669 * the whole story.
2670 */
2671static jobjectRefType GetObjectRefType(JNIEnv* env, jobject jobj) {
2672    ScopedJniThreadState ts(env);
2673    return dvmGetJNIRefType(env, jobj);
2674}
2675
2676/*
2677 * Allocate and return a new java.nio.ByteBuffer for this block of memory.
2678 *
2679 * "address" may not be NULL, and "capacity" must be > 0.  (These are only
2680 * verified when CheckJNI is enabled.)
2681 */
2682static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) {
2683    ScopedJniThreadState ts(env);
2684
2685    /* create an instance of java.nio.ReadWriteDirectByteBuffer */
2686    ClassObject* bufferClazz = gDvm.classJavaNioReadWriteDirectByteBuffer;
2687    if (!dvmIsClassInitialized(bufferClazz) && !dvmInitClass(bufferClazz)) {
2688        return NULL;
2689    }
2690    Object* newObj = dvmAllocObject(bufferClazz, ALLOC_DONT_TRACK);
2691    if (newObj == NULL) {
2692        return NULL;
2693    }
2694    /* call the constructor */
2695    jobject result = addLocalReference(env, newObj);
2696    JValue unused;
2697    dvmCallMethod(ts.self(), gDvm.methJavaNioReadWriteDirectByteBuffer_init,
2698            newObj, &unused, (jint) address, (jint) capacity);
2699    if (dvmGetException(ts.self()) != NULL) {
2700        deleteLocalReference(env, result);
2701        return NULL;
2702    }
2703    return result;
2704}
2705
2706/*
2707 * Get the starting address of the buffer for the specified java.nio.Buffer.
2708 *
2709 * If this is not a "direct" buffer, we return NULL.
2710 */
2711static void* GetDirectBufferAddress(JNIEnv* env, jobject jbuf) {
2712    ScopedJniThreadState ts(env);
2713
2714    // All Buffer objects have an effectiveDirectAddress field.
2715    Object* bufObj = dvmDecodeIndirectRef(env, jbuf);
2716    return (void*) dvmGetFieldInt(bufObj, gDvm.offJavaNioBuffer_effectiveDirectAddress);
2717}
2718
2719/*
2720 * Get the capacity of the buffer for the specified java.nio.Buffer.
2721 *
2722 * Returns -1 if the object is not a direct buffer.  (We actually skip
2723 * this check, since it's expensive to determine, and just return the
2724 * capacity regardless.)
2725 */
2726static jlong GetDirectBufferCapacity(JNIEnv* env, jobject jbuf) {
2727    ScopedJniThreadState ts(env);
2728
2729    /*
2730     * The capacity is always in the Buffer.capacity field.
2731     *
2732     * (The "check" version should verify that this is actually a Buffer,
2733     * but we're not required to do so here.)
2734     */
2735    Object* buf = dvmDecodeIndirectRef(env, jbuf);
2736    return dvmGetFieldInt(buf, gDvm.offJavaNioBuffer_capacity);
2737}
2738
2739
2740/*
2741 * ===========================================================================
2742 *      JNI invocation functions
2743 * ===========================================================================
2744 */
2745
2746/*
2747 * Handle AttachCurrentThread{AsDaemon}.
2748 *
2749 * We need to make sure the VM is actually running.  For example, if we start
2750 * up, issue an Attach, and the VM exits almost immediately, by the time the
2751 * attaching happens the VM could already be shutting down.
2752 *
2753 * It's hard to avoid a race condition here because we don't want to hold
2754 * a lock across the entire operation.  What we can do is temporarily
2755 * increment the thread count to prevent a VM exit.
2756 *
2757 * This could potentially still have problems if a daemon thread calls here
2758 * while the VM is shutting down.  dvmThreadSelf() will work, since it just
2759 * uses pthread TLS, but dereferencing "vm" could fail.  Such is life when
2760 * you shut down a VM while threads are still running inside it.
2761 *
2762 * Remember that some code may call this as a way to find the per-thread
2763 * JNIEnv pointer.  Don't do excess work for that case.
2764 */
2765static jint attachThread(JavaVM* vm, JNIEnv** p_env, void* thr_args, bool isDaemon) {
2766    JavaVMAttachArgs* args = (JavaVMAttachArgs*) thr_args;
2767
2768    /*
2769     * Return immediately if we're already one with the VM.
2770     */
2771    Thread* self = dvmThreadSelf();
2772    if (self != NULL) {
2773        *p_env = self->jniEnv;
2774        return JNI_OK;
2775    }
2776
2777    /*
2778     * No threads allowed in zygote mode.
2779     */
2780    if (gDvm.zygote) {
2781        return JNI_ERR;
2782    }
2783
2784    /* increment the count to keep the VM from bailing while we run */
2785    dvmLockThreadList(NULL);
2786    if (gDvm.nonDaemonThreadCount == 0) {
2787        // dead or dying
2788        LOGV("Refusing to attach thread '%s' -- VM is shutting down",
2789            (thr_args == NULL) ? "(unknown)" : args->name);
2790        dvmUnlockThreadList();
2791        return JNI_ERR;
2792    }
2793    gDvm.nonDaemonThreadCount++;
2794    dvmUnlockThreadList();
2795
2796    /* tweak the JavaVMAttachArgs as needed */
2797    JavaVMAttachArgs argsCopy;
2798    if (args == NULL) {
2799        /* allow the v1.1 calling convention */
2800        argsCopy.version = JNI_VERSION_1_2;
2801        argsCopy.name = NULL;
2802        argsCopy.group = (jobject) dvmGetMainThreadGroup();
2803    } else {
2804        assert(args->version >= JNI_VERSION_1_2);
2805
2806        argsCopy.version = args->version;
2807        argsCopy.name = args->name;
2808        if (args->group != NULL) {
2809            argsCopy.group = args->group;
2810        } else {
2811            argsCopy.group = (jobject) dvmGetMainThreadGroup();
2812        }
2813    }
2814
2815    bool result = dvmAttachCurrentThread(&argsCopy, isDaemon);
2816
2817    /* restore the count */
2818    dvmLockThreadList(NULL);
2819    gDvm.nonDaemonThreadCount--;
2820    dvmUnlockThreadList();
2821
2822    /*
2823     * Change the status to indicate that we're out in native code.  This
2824     * call is not guarded with state-change macros, so we have to do it
2825     * by hand.
2826     */
2827    if (result) {
2828        self = dvmThreadSelf();
2829        assert(self != NULL);
2830        dvmChangeStatus(self, THREAD_NATIVE);
2831        *p_env = self->jniEnv;
2832        return JNI_OK;
2833    } else {
2834        return JNI_ERR;
2835    }
2836}
2837
2838/*
2839 * Attach the current thread to the VM.  If the thread is already attached,
2840 * this is a no-op.
2841 */
2842static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
2843    return attachThread(vm, p_env, thr_args, false);
2844}
2845
2846/*
2847 * Like AttachCurrentThread, but set the "daemon" flag.
2848 */
2849static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env, void* thr_args)
2850{
2851    return attachThread(vm, p_env, thr_args, true);
2852}
2853
2854/*
2855 * Dissociate the current thread from the VM.
2856 */
2857static jint DetachCurrentThread(JavaVM* vm) {
2858    Thread* self = dvmThreadSelf();
2859    if (self == NULL) {
2860        /* not attached, can't do anything */
2861        return JNI_ERR;
2862    }
2863
2864    /* switch to "running" to check for suspension */
2865    dvmChangeStatus(self, THREAD_RUNNING);
2866
2867    /* detach the thread */
2868    dvmDetachCurrentThread();
2869
2870    /* (no need to change status back -- we have no status) */
2871    return JNI_OK;
2872}
2873
2874/*
2875 * If current thread is attached to VM, return the associated JNIEnv.
2876 * Otherwise, stuff NULL in and return JNI_EDETACHED.
2877 *
2878 * JVMTI overloads this by specifying a magic value for "version", so we
2879 * do want to check that here.
2880 */
2881static jint GetEnv(JavaVM* vm, void** env, jint version) {
2882    Thread* self = dvmThreadSelf();
2883
2884    if (version < JNI_VERSION_1_1 || version > JNI_VERSION_1_6) {
2885        return JNI_EVERSION;
2886    }
2887
2888    if (self == NULL) {
2889        *env = NULL;
2890    } else {
2891        /* TODO: status change is probably unnecessary */
2892        dvmChangeStatus(self, THREAD_RUNNING);
2893        *env = (void*) dvmGetThreadJNIEnv(self);
2894        dvmChangeStatus(self, THREAD_NATIVE);
2895    }
2896    return (*env != NULL) ? JNI_OK : JNI_EDETACHED;
2897}
2898
2899/*
2900 * Destroy the VM.  This may be called from any thread.
2901 *
2902 * If the current thread is attached, wait until the current thread is
2903 * the only non-daemon user-level thread.  If the current thread is not
2904 * attached, we attach it and do the processing as usual.  (If the attach
2905 * fails, it's probably because all the non-daemon threads have already
2906 * exited and the VM doesn't want to let us back in.)
2907 *
2908 * TODO: we don't really deal with the situation where more than one thread
2909 * has called here.  One thread wins, the other stays trapped waiting on
2910 * the condition variable forever.  Not sure this situation is interesting
2911 * in real life.
2912 */
2913static jint DestroyJavaVM(JavaVM* vm) {
2914    JavaVMExt* ext = (JavaVMExt*) vm;
2915    if (ext == NULL) {
2916        return JNI_ERR;
2917    }
2918
2919    if (gDvm.verboseShutdown) {
2920        LOGD("DestroyJavaVM waiting for non-daemon threads to exit");
2921    }
2922
2923    /*
2924     * Sleep on a condition variable until it's okay to exit.
2925     */
2926    Thread* self = dvmThreadSelf();
2927    if (self == NULL) {
2928        JNIEnv* tmpEnv;
2929        if (AttachCurrentThread(vm, &tmpEnv, NULL) != JNI_OK) {
2930            LOGV("Unable to reattach main for Destroy; assuming VM is shutting down (count=%d)",
2931                gDvm.nonDaemonThreadCount);
2932            goto shutdown;
2933        } else {
2934            LOGV("Attached to wait for shutdown in Destroy");
2935        }
2936    }
2937    dvmChangeStatus(self, THREAD_VMWAIT);
2938
2939    dvmLockThreadList(self);
2940    gDvm.nonDaemonThreadCount--;    // remove current thread from count
2941
2942    while (gDvm.nonDaemonThreadCount > 0) {
2943        pthread_cond_wait(&gDvm.vmExitCond, &gDvm.threadListLock);
2944    }
2945
2946    dvmUnlockThreadList();
2947    self = NULL;
2948
2949shutdown:
2950    // TODO: call System.exit() to run any registered shutdown hooks
2951    // (this may not return -- figure out how this should work)
2952
2953    if (gDvm.verboseShutdown) {
2954        LOGD("DestroyJavaVM shutting VM down");
2955    }
2956    dvmShutdown();
2957
2958    // TODO - free resources associated with JNI-attached daemon threads
2959    free(ext->envList);
2960    free(ext);
2961
2962    return JNI_OK;
2963}
2964
2965
2966/*
2967 * ===========================================================================
2968 *      Function tables
2969 * ===========================================================================
2970 */
2971
2972static const struct JNINativeInterface gNativeInterface = {
2973    NULL,
2974    NULL,
2975    NULL,
2976    NULL,
2977
2978    GetVersion,
2979
2980    DefineClass,
2981    FindClass,
2982
2983    FromReflectedMethod,
2984    FromReflectedField,
2985    ToReflectedMethod,
2986
2987    GetSuperclass,
2988    IsAssignableFrom,
2989
2990    ToReflectedField,
2991
2992    Throw,
2993    ThrowNew,
2994    ExceptionOccurred,
2995    ExceptionDescribe,
2996    ExceptionClear,
2997    FatalError,
2998
2999    PushLocalFrame,
3000    PopLocalFrame,
3001
3002    NewGlobalRef,
3003    DeleteGlobalRef,
3004    DeleteLocalRef,
3005    IsSameObject,
3006    NewLocalRef,
3007    EnsureLocalCapacity,
3008
3009    AllocObject,
3010    NewObject,
3011    NewObjectV,
3012    NewObjectA,
3013
3014    GetObjectClass,
3015    IsInstanceOf,
3016
3017    GetMethodID,
3018
3019    CallObjectMethod,
3020    CallObjectMethodV,
3021    CallObjectMethodA,
3022    CallBooleanMethod,
3023    CallBooleanMethodV,
3024    CallBooleanMethodA,
3025    CallByteMethod,
3026    CallByteMethodV,
3027    CallByteMethodA,
3028    CallCharMethod,
3029    CallCharMethodV,
3030    CallCharMethodA,
3031    CallShortMethod,
3032    CallShortMethodV,
3033    CallShortMethodA,
3034    CallIntMethod,
3035    CallIntMethodV,
3036    CallIntMethodA,
3037    CallLongMethod,
3038    CallLongMethodV,
3039    CallLongMethodA,
3040    CallFloatMethod,
3041    CallFloatMethodV,
3042    CallFloatMethodA,
3043    CallDoubleMethod,
3044    CallDoubleMethodV,
3045    CallDoubleMethodA,
3046    CallVoidMethod,
3047    CallVoidMethodV,
3048    CallVoidMethodA,
3049
3050    CallNonvirtualObjectMethod,
3051    CallNonvirtualObjectMethodV,
3052    CallNonvirtualObjectMethodA,
3053    CallNonvirtualBooleanMethod,
3054    CallNonvirtualBooleanMethodV,
3055    CallNonvirtualBooleanMethodA,
3056    CallNonvirtualByteMethod,
3057    CallNonvirtualByteMethodV,
3058    CallNonvirtualByteMethodA,
3059    CallNonvirtualCharMethod,
3060    CallNonvirtualCharMethodV,
3061    CallNonvirtualCharMethodA,
3062    CallNonvirtualShortMethod,
3063    CallNonvirtualShortMethodV,
3064    CallNonvirtualShortMethodA,
3065    CallNonvirtualIntMethod,
3066    CallNonvirtualIntMethodV,
3067    CallNonvirtualIntMethodA,
3068    CallNonvirtualLongMethod,
3069    CallNonvirtualLongMethodV,
3070    CallNonvirtualLongMethodA,
3071    CallNonvirtualFloatMethod,
3072    CallNonvirtualFloatMethodV,
3073    CallNonvirtualFloatMethodA,
3074    CallNonvirtualDoubleMethod,
3075    CallNonvirtualDoubleMethodV,
3076    CallNonvirtualDoubleMethodA,
3077    CallNonvirtualVoidMethod,
3078    CallNonvirtualVoidMethodV,
3079    CallNonvirtualVoidMethodA,
3080
3081    GetFieldID,
3082
3083    GetObjectField,
3084    GetBooleanField,
3085    GetByteField,
3086    GetCharField,
3087    GetShortField,
3088    GetIntField,
3089    GetLongField,
3090    GetFloatField,
3091    GetDoubleField,
3092    SetObjectField,
3093    SetBooleanField,
3094    SetByteField,
3095    SetCharField,
3096    SetShortField,
3097    SetIntField,
3098    SetLongField,
3099    SetFloatField,
3100    SetDoubleField,
3101
3102    GetStaticMethodID,
3103
3104    CallStaticObjectMethod,
3105    CallStaticObjectMethodV,
3106    CallStaticObjectMethodA,
3107    CallStaticBooleanMethod,
3108    CallStaticBooleanMethodV,
3109    CallStaticBooleanMethodA,
3110    CallStaticByteMethod,
3111    CallStaticByteMethodV,
3112    CallStaticByteMethodA,
3113    CallStaticCharMethod,
3114    CallStaticCharMethodV,
3115    CallStaticCharMethodA,
3116    CallStaticShortMethod,
3117    CallStaticShortMethodV,
3118    CallStaticShortMethodA,
3119    CallStaticIntMethod,
3120    CallStaticIntMethodV,
3121    CallStaticIntMethodA,
3122    CallStaticLongMethod,
3123    CallStaticLongMethodV,
3124    CallStaticLongMethodA,
3125    CallStaticFloatMethod,
3126    CallStaticFloatMethodV,
3127    CallStaticFloatMethodA,
3128    CallStaticDoubleMethod,
3129    CallStaticDoubleMethodV,
3130    CallStaticDoubleMethodA,
3131    CallStaticVoidMethod,
3132    CallStaticVoidMethodV,
3133    CallStaticVoidMethodA,
3134
3135    GetStaticFieldID,
3136
3137    GetStaticObjectField,
3138    GetStaticBooleanField,
3139    GetStaticByteField,
3140    GetStaticCharField,
3141    GetStaticShortField,
3142    GetStaticIntField,
3143    GetStaticLongField,
3144    GetStaticFloatField,
3145    GetStaticDoubleField,
3146
3147    SetStaticObjectField,
3148    SetStaticBooleanField,
3149    SetStaticByteField,
3150    SetStaticCharField,
3151    SetStaticShortField,
3152    SetStaticIntField,
3153    SetStaticLongField,
3154    SetStaticFloatField,
3155    SetStaticDoubleField,
3156
3157    NewString,
3158
3159    GetStringLength,
3160    GetStringChars,
3161    ReleaseStringChars,
3162
3163    NewStringUTF,
3164    GetStringUTFLength,
3165    GetStringUTFChars,
3166    ReleaseStringUTFChars,
3167
3168    GetArrayLength,
3169    NewObjectArray,
3170    GetObjectArrayElement,
3171    SetObjectArrayElement,
3172
3173    NewBooleanArray,
3174    NewByteArray,
3175    NewCharArray,
3176    NewShortArray,
3177    NewIntArray,
3178    NewLongArray,
3179    NewFloatArray,
3180    NewDoubleArray,
3181
3182    GetBooleanArrayElements,
3183    GetByteArrayElements,
3184    GetCharArrayElements,
3185    GetShortArrayElements,
3186    GetIntArrayElements,
3187    GetLongArrayElements,
3188    GetFloatArrayElements,
3189    GetDoubleArrayElements,
3190
3191    ReleaseBooleanArrayElements,
3192    ReleaseByteArrayElements,
3193    ReleaseCharArrayElements,
3194    ReleaseShortArrayElements,
3195    ReleaseIntArrayElements,
3196    ReleaseLongArrayElements,
3197    ReleaseFloatArrayElements,
3198    ReleaseDoubleArrayElements,
3199
3200    GetBooleanArrayRegion,
3201    GetByteArrayRegion,
3202    GetCharArrayRegion,
3203    GetShortArrayRegion,
3204    GetIntArrayRegion,
3205    GetLongArrayRegion,
3206    GetFloatArrayRegion,
3207    GetDoubleArrayRegion,
3208    SetBooleanArrayRegion,
3209    SetByteArrayRegion,
3210    SetCharArrayRegion,
3211    SetShortArrayRegion,
3212    SetIntArrayRegion,
3213    SetLongArrayRegion,
3214    SetFloatArrayRegion,
3215    SetDoubleArrayRegion,
3216
3217    RegisterNatives,
3218    UnregisterNatives,
3219
3220    MonitorEnter,
3221    MonitorExit,
3222
3223    GetJavaVM,
3224
3225    GetStringRegion,
3226    GetStringUTFRegion,
3227
3228    GetPrimitiveArrayCritical,
3229    ReleasePrimitiveArrayCritical,
3230
3231    GetStringCritical,
3232    ReleaseStringCritical,
3233
3234    NewWeakGlobalRef,
3235    DeleteWeakGlobalRef,
3236
3237    ExceptionCheck,
3238
3239    NewDirectByteBuffer,
3240    GetDirectBufferAddress,
3241    GetDirectBufferCapacity,
3242
3243    GetObjectRefType
3244};
3245
3246static const struct JNIInvokeInterface gInvokeInterface = {
3247    NULL,
3248    NULL,
3249    NULL,
3250
3251    DestroyJavaVM,
3252    AttachCurrentThread,
3253    DetachCurrentThread,
3254
3255    GetEnv,
3256
3257    AttachCurrentThreadAsDaemon,
3258};
3259
3260/*
3261 * ===========================================================================
3262 *      VM/Env creation
3263 * ===========================================================================
3264 */
3265
3266/*
3267 * Create a new JNIEnv struct and add it to the VM's list.
3268 *
3269 * "self" will be NULL for the main thread, since the VM hasn't started
3270 * yet; the value will be filled in later.
3271 */
3272JNIEnv* dvmCreateJNIEnv(Thread* self) {
3273    JavaVMExt* vm = (JavaVMExt*) gDvmJni.jniVm;
3274
3275    //if (self != NULL)
3276    //    LOGI("Ent CreateJNIEnv: threadid=%d %p", self->threadId, self);
3277
3278    assert(vm != NULL);
3279
3280    JNIEnvExt* newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt));
3281    newEnv->funcTable = &gNativeInterface;
3282    if (self != NULL) {
3283        dvmSetJniEnvThreadId((JNIEnv*) newEnv, self);
3284        assert(newEnv->envThreadId != 0);
3285    } else {
3286        /* make it obvious if we fail to initialize these later */
3287        newEnv->envThreadId = 0x77777775;
3288        newEnv->self = (Thread*) 0x77777779;
3289    }
3290    if (gDvmJni.useCheckJni) {
3291        dvmUseCheckedJniEnv(newEnv);
3292    }
3293
3294    ScopedPthreadMutexLock lock(&vm->envListLock);
3295
3296    /* insert at head of list */
3297    newEnv->next = vm->envList;
3298    assert(newEnv->prev == NULL);
3299    if (vm->envList == NULL) {
3300        // rare, but possible
3301        vm->envList = newEnv;
3302    } else {
3303        vm->envList->prev = newEnv;
3304    }
3305    vm->envList = newEnv;
3306
3307    //if (self != NULL)
3308    //    LOGI("Xit CreateJNIEnv: threadid=%d %p", self->threadId, self);
3309    return (JNIEnv*) newEnv;
3310}
3311
3312/*
3313 * Remove a JNIEnv struct from the list and free it.
3314 */
3315void dvmDestroyJNIEnv(JNIEnv* env) {
3316    if (env == NULL) {
3317        return;
3318    }
3319
3320    //LOGI("Ent DestroyJNIEnv: threadid=%d %p", self->threadId, self);
3321
3322    JNIEnvExt* extEnv = (JNIEnvExt*) env;
3323    JavaVMExt* vm = (JavaVMExt*) gDvmJni.jniVm;
3324
3325    ScopedPthreadMutexLock lock(&vm->envListLock);
3326
3327    if (extEnv == vm->envList) {
3328        assert(extEnv->prev == NULL);
3329        vm->envList = extEnv->next;
3330    } else {
3331        assert(extEnv->prev != NULL);
3332        extEnv->prev->next = extEnv->next;
3333    }
3334    if (extEnv->next != NULL) {
3335        extEnv->next->prev = extEnv->prev;
3336    }
3337
3338    free(env);
3339    //LOGI("Xit DestroyJNIEnv: threadid=%d %p", self->threadId, self);
3340}
3341
3342/*
3343 * Enable "checked JNI" after the VM has partially started.  This must
3344 * only be called in "zygote" mode, when we have one thread running.
3345 *
3346 * This doesn't attempt to rewrite the JNI call bridge associated with
3347 * native methods, so we won't get those checks for any methods that have
3348 * already been resolved.
3349 */
3350void dvmLateEnableCheckedJni() {
3351    JNIEnvExt* extEnv = dvmGetJNIEnvForThread();
3352    if (extEnv == NULL) {
3353        LOGE("dvmLateEnableCheckedJni: thread has no JNIEnv");
3354        return;
3355    }
3356    JavaVMExt* extVm = (JavaVMExt*) gDvmJni.jniVm;
3357    assert(extVm != NULL);
3358
3359    if (!gDvmJni.useCheckJni) {
3360        LOGD("Late-enabling CheckJNI");
3361        dvmUseCheckedJniVm(extVm);
3362        dvmUseCheckedJniEnv(extEnv);
3363    } else {
3364        LOGD("Not late-enabling CheckJNI (already on)");
3365    }
3366}
3367
3368/*
3369 * Not supported.
3370 */
3371jint JNI_GetDefaultJavaVMInitArgs(void* vm_args) {
3372    return JNI_ERR;
3373}
3374
3375/*
3376 * Return a buffer full of created VMs.
3377 *
3378 * We always have zero or one.
3379 */
3380jint JNI_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs) {
3381    if (gDvmJni.jniVm != NULL) {
3382        *nVMs = 1;
3383        if (bufLen > 0) {
3384            *vmBuf++ = gDvmJni.jniVm;
3385        }
3386    } else {
3387        *nVMs = 0;
3388    }
3389    return JNI_OK;
3390}
3391
3392/*
3393 * Create a new VM instance.
3394 *
3395 * The current thread becomes the main VM thread.  We return immediately,
3396 * which effectively means the caller is executing in a native method.
3397 */
3398jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
3399    const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
3400    if (args->version < JNI_VERSION_1_2) {
3401        return JNI_EVERSION;
3402    }
3403
3404    // TODO: don't allow creation of multiple VMs -- one per customer for now
3405
3406    /* zero globals; not strictly necessary the first time a VM is started */
3407    memset(&gDvm, 0, sizeof(gDvm));
3408
3409    /*
3410     * Set up structures for JNIEnv and VM.
3411     */
3412    JavaVMExt* pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt));
3413    memset(pVM, 0, sizeof(JavaVMExt));
3414    pVM->funcTable = &gInvokeInterface;
3415    pVM->envList = NULL;
3416    dvmInitMutex(&pVM->envListLock);
3417
3418    UniquePtr<const char*[]> argv(new const char*[args->nOptions]);
3419    memset(argv.get(), 0, sizeof(char*) * (args->nOptions));
3420
3421    /*
3422     * Convert JNI args to argv.
3423     *
3424     * We have to pull out vfprintf/exit/abort, because they use the
3425     * "extraInfo" field to pass function pointer "hooks" in.  We also
3426     * look for the -Xcheck:jni stuff here.
3427     */
3428    int argc = 0;
3429    bool sawJniOpts = false;
3430    for (int i = 0; i < args->nOptions; i++) {
3431        const char* optStr = args->options[i].optionString;
3432        if (optStr == NULL) {
3433            dvmFprintf(stderr, "ERROR: CreateJavaVM failed: argument %d was NULL\n", i);
3434            return JNI_ERR;
3435        } else if (strcmp(optStr, "vfprintf") == 0) {
3436            gDvm.vfprintfHook = (int (*)(FILE *, const char*, va_list))args->options[i].extraInfo;
3437        } else if (strcmp(optStr, "exit") == 0) {
3438            gDvm.exitHook = (void (*)(int)) args->options[i].extraInfo;
3439        } else if (strcmp(optStr, "abort") == 0) {
3440            gDvm.abortHook = (void (*)(void))args->options[i].extraInfo;
3441        } else if (strcmp(optStr, "sensitiveThread") == 0) {
3442            gDvm.isSensitiveThreadHook = (bool (*)(void))args->options[i].extraInfo;
3443        } else if (strcmp(optStr, "-Xcheck:jni") == 0) {
3444            gDvmJni.useCheckJni = true;
3445        } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {
3446            sawJniOpts = true;
3447            char* jniOpts = strdup(optStr + 10);
3448            size_t jniOptCount = 1;
3449            for (char* p = jniOpts; *p != 0; ++p) {
3450                if (*p == ',') {
3451                    ++jniOptCount;
3452                    *p = 0;
3453                }
3454            }
3455            char* jniOpt = jniOpts;
3456            for (size_t i = 0; i < jniOptCount; ++i) {
3457                if (strcmp(jniOpt, "warnonly") == 0) {
3458                    gDvmJni.warnOnly = true;
3459                } else if (strcmp(jniOpt, "forcecopy") == 0) {
3460                    gDvmJni.forceCopy = true;
3461                } else {
3462                    dvmFprintf(stderr, "ERROR: CreateJavaVM failed: unknown -Xjniopts option '%s'\n",
3463                            jniOpt);
3464                    return JNI_ERR;
3465                }
3466                jniOpt += strlen(jniOpt) + 1;
3467            }
3468            free(jniOpts);
3469        } else {
3470            /* regular option */
3471            argv[argc++] = optStr;
3472        }
3473    }
3474
3475    if (sawJniOpts && !gDvmJni.useCheckJni) {
3476        dvmFprintf(stderr, "ERROR: -Xjniopts only makes sense with -Xcheck:jni\n");
3477        return JNI_ERR;
3478    }
3479
3480    if (gDvmJni.useCheckJni) {
3481        dvmUseCheckedJniVm(pVM);
3482    }
3483
3484    if (gDvmJni.jniVm != NULL) {
3485        dvmFprintf(stderr, "ERROR: Dalvik only supports one VM per process\n");
3486        return JNI_ERR;
3487    }
3488    gDvmJni.jniVm = (JavaVM*) pVM;
3489
3490    /*
3491     * Create a JNIEnv for the main thread.  We need to have something set up
3492     * here because some of the class initialization we do when starting
3493     * up the VM will call into native code.
3494     */
3495    JNIEnvExt* pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
3496
3497    /* Initialize VM. */
3498    gDvm.initializing = true;
3499    int rc = dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);
3500    gDvm.initializing = false;
3501
3502    if (rc != 0) {
3503        free(pEnv);
3504        free(pVM);
3505        LOGW("CreateJavaVM failed");
3506        return JNI_ERR;
3507    }
3508
3509    /*
3510     * Success!  Return stuff to caller.
3511     */
3512    dvmChangeStatus(NULL, THREAD_NATIVE);
3513    *p_env = (JNIEnv*) pEnv;
3514    *p_vm = (JavaVM*) pVM;
3515    LOGV("CreateJavaVM succeeded");
3516    return JNI_OK;
3517}
3518