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