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