Progress toward indirect JNI references.

Switch from simple typecasts to conversion functions for the objects
passed in and out of JNI calls.  No real change here; object references
are still just pointers.

Use explicit pin/unpin calls for primitive arrays.  For GetStringChars
we now pin the char[] rather than the String object.  (Which doesn't
make much difference in the grand scheme of things, since you need to
keep the String to pass into the release function anyway.)
diff --git a/vm/CheckJni.c b/vm/CheckJni.c
index f8e393c..390f4c8 100644
--- a/vm/CheckJni.c
+++ b/vm/CheckJni.c
@@ -62,7 +62,8 @@
     const Method* method, Thread* self)
 {
     assert(pResult->l != NULL);
-    ClassObject* objClazz = ((Object*)pResult->l)->clazz;
+    Object* resultObj = dvmDecodeIndirectRef(self->jniEnv, pResult->l);
+    ClassObject* objClazz = resultObj->clazz;
 
     /*
      * Make sure that pResult->l is an instance of the type this
@@ -194,8 +195,8 @@
 #define CHECK_VMEXIT(_vm, _hasmeth)                                         \
     do { JNI_TRACE(false, _hasmeth); } while(false)
 
-#define CHECK_FIELD_TYPE(_obj, _fieldid, _prim, _isstatic)                  \
-    checkFieldType(_obj, _fieldid, _prim, _isstatic, __FUNCTION__)
+#define CHECK_FIELD_TYPE(_env, _obj, _fieldid, _prim, _isstatic)            \
+    checkFieldType(_env, _obj, _fieldid, _prim, _isstatic, __FUNCTION__)
 #define CHECK_INST_FIELD_ID(_env, _obj, _fieldid)                           \
     checkInstanceFieldID(_env, _obj, _fieldid, __FUNCTION__)
 #define CHECK_CLASS(_env, _clazz)                                           \
@@ -296,7 +297,8 @@
      */
     if (threadEnv == NULL) {
         LOGE("JNI ERROR: non-VM thread making JNI calls\n");
-        // don't set printWarn
+        // don't set printWarn -- it'll try to call showLocation()
+        dvmAbort();
     } else if ((JNIEnvExt*) env != threadEnv) {
         if (dvmThreadSelf()->threadId != threadEnv->envThreadId) {
             LOGE("JNI: threadEnv != thread->env?\n");
@@ -312,7 +314,8 @@
         //    "invalid use of JNI env ptr");
     } else if (((JNIEnvExt*) env)->self != dvmThreadSelf()) {
         /* correct JNIEnv*; make sure the "self" pointer is correct */
-        LOGE("JNI: env->self != thread-self\n");
+        LOGE("JNI ERROR: env->self != thread-self (%p vs. %p)\n",
+            ((JNIEnvExt*) env)->self, dvmThreadSelf());
         dvmAbort();
     }
 
@@ -354,12 +357,6 @@
         printException = true;
     }
 
-    if (false) {
-        Thread* self = dvmThreadSelf();
-        LOGW("NOW: %d\n",
-            (int) dvmReferenceTableEntries(&self->internalLocalRefTable));
-    }
-
     if (printWarn)
         showLocation(dvmGetCurrentJNIMethod(), func);
     if (printException) {
@@ -376,8 +373,8 @@
  *
  * Works for both static and instance fields.
  */
-static void checkFieldType(jobject obj, jfieldID fieldID, PrimitiveType prim,
-    bool isStatic, const char* func)
+static void checkFieldType(JNIEnv* env, jobject jobj, jfieldID fieldID,
+    PrimitiveType prim, bool isStatic, const char* func)
 {
     static const char* primNameList[] = {
         "Object/Array", "boolean", "char", "float", "double",
@@ -387,6 +384,8 @@
     Field* field = (Field*) fieldID;
     bool printWarn = false;
 
+    Object* obj = dvmDecodeIndirectRef(env, jobj);
+
     if (fieldID == NULL) {
         LOGE("JNI ERROR: null field ID\n");
         abortMaybe();
@@ -396,7 +395,7 @@
         if (obj != NULL) {
             ClassObject* fieldClass =
                 dvmFindLoadedClass(field->signature);
-            ClassObject* objClass = ((Object*)obj)->clazz;
+            ClassObject* objClass = obj->clazz;
 
             assert(fieldClass != NULL);
             assert(objClass != NULL);
@@ -428,26 +427,28 @@
 }
 
 /*
- * Verify that "obj" is a valid object, and that it's an object that JNI
+ * Verify that "jobj" is a valid object, and that it's an object that JNI
  * is allowed to know about.  We allow NULL references.
  *
  * Switches to "running" mode before performing checks.
  */
-static void checkObject(JNIEnv* env, jobject obj, const char* func)
+static void checkObject(JNIEnv* env, jobject jobj, const char* func)
 {
     UNUSED_PARAMETER(env);
     bool printWarn = false;
 
-    if (obj == NULL)
+    if (jobj == NULL)
         return;
 
+    Object* obj = dvmDecodeIndirectRef(env, jobj);
+
     JNI_ENTER();
-    if (!dvmIsValidObject(obj)) {
-        LOGW("JNI WARNING: native code passing in bad object %p (%s)\n",
-            obj, func);
+    if (obj == NULL || !dvmIsValidObject(obj)) {
+        LOGW("JNI WARNING: native code passing in bad object %p %p (%s)\n",
+            jobj, obj, func);
         printWarn = true;
-    } else if (dvmGetJNIRefType(obj) == JNIInvalidRefType) {
-        LOGW("JNI WARNING: ref %p should not be visible to native code\n", obj);
+    } else if (dvmGetJNIRefType(env, obj) == JNIInvalidRefType) {
+        LOGW("JNI WARNING: %p is not a valid JNI reference\n", jobj);
         printWarn = true;
     }
 
@@ -475,7 +476,7 @@
     JNI_ENTER();
     bool printWarn = false;
 
-    ClassObject* clazz = (ClassObject*) jclazz;
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
 
     if (clazz == NULL) {
         LOGW("JNI WARNING: received null jclass\n");
@@ -501,22 +502,28 @@
  *
  * Since we're dealing with objects, switch to "running" mode.
  */
-static void checkString(JNIEnv* env, jstring str, const char* func)
+static void checkString(JNIEnv* env, jstring jstr, const char* func)
 {
     JNI_ENTER();
     bool printWarn = false;
 
-    Object* obj = (Object*) str;
+    Object* obj = dvmDecodeIndirectRef(env, jstr);
 
     if (obj == NULL) {
         LOGW("JNI WARNING: received null jstring (%s)\n", func);
         printWarn = true;
     } else if (obj->clazz != gDvm.classJavaLangString) {
+        /*
+         * TODO: we probably should test dvmIsValidObject first, because
+         * this will crash if "obj" is non-null but pointing to an invalid
+         * memory region.  However, the "is valid" test is a little slow,
+         * we're doing it again over in checkObject().
+         */
         if (dvmIsValidObject(obj))
-            LOGW("JNI WARNING: jstring points to non-string object (%s)\n",
-                func);
+            LOGW("JNI WARNING: jstring %p points to non-string object (%s)\n",
+                jstr, func);
         else
-            LOGW("JNI WARNING: jstring %p is bogus (%s)\n", str, func);
+            LOGW("JNI WARNING: jstring %p is bogus (%s)\n", jstr, func);
         printWarn = true;
     }
     JNI_EXIT();
@@ -524,7 +531,7 @@
     if (printWarn)
         abortMaybe();
     else
-        checkObject(env, str, func);
+        checkObject(env, jstr, func);
 }
 
 /*
@@ -641,21 +648,22 @@
  *
  * Since we're dealing with objects, switch to "running" mode.
  */
-static void checkArray(JNIEnv* env, jarray array, const char* func)
+static void checkArray(JNIEnv* env, jarray jarr, const char* func)
 {
     JNI_ENTER();
     bool printWarn = false;
 
-    Object* obj = (Object*) array;
+    Object* obj = dvmDecodeIndirectRef(env, jarr);
 
     if (obj == NULL) {
         LOGW("JNI WARNING: received null array (%s)\n", func);
         printWarn = true;
     } else if (obj->clazz->descriptor[0] != '[') {
         if (dvmIsValidObject(obj))
-            LOGW("JNI WARNING: jarray points to non-array object\n");
+            LOGW("JNI WARNING: jarray %p points to non-array object (%s)\n",
+                jarr, obj->clazz->descriptor);
         else
-            LOGW("JNI WARNING: jarray is bogus (%p)\n", array);
+            LOGW("JNI WARNING: jarray %p is bogus\n", jarr);
         printWarn = true;
     }
 
@@ -664,7 +672,7 @@
     if (printWarn)
         abortMaybe();
     else
-        checkObject(env, array, func);
+        checkObject(env, jarr, func);
 }
 
 /*
@@ -736,16 +744,17 @@
 /*
  * Verify that this static field ID is valid for this class.
  */
-static void checkStaticFieldID(JNIEnv* env, jclass clazz, jfieldID fieldID)
+static void checkStaticFieldID(JNIEnv* env, jclass jclazz, jfieldID fieldID)
 {
-    StaticField* base = ((ClassObject*) clazz)->sfields;
-    int fieldCount = ((ClassObject*) clazz)->sfieldCount;
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+    StaticField* base = clazz->sfields;
+    int fieldCount = clazz->sfieldCount;
 
     if ((StaticField*) fieldID < base ||
         (StaticField*) fieldID >= base + fieldCount)
     {
         LOGW("JNI WARNING: static fieldID %p not valid for class %s\n",
-            fieldID, ((ClassObject*) clazz)->descriptor);
+            fieldID, clazz->descriptor);
         LOGW("             base=%p count=%d\n", base, fieldCount);
         abortMaybe();
     }
@@ -754,18 +763,19 @@
 /*
  * Verify that this instance field ID is valid for this object.
  */
-static void checkInstanceFieldID(JNIEnv* env, jobject obj, jfieldID fieldID,
+static void checkInstanceFieldID(JNIEnv* env, jobject jobj, jfieldID fieldID,
     const char* func)
 {
     JNI_ENTER();
 
-    if (obj == NULL) {
+    if (jobj == NULL) {
         LOGW("JNI WARNING: invalid null object (%s)\n", func);
         abortMaybe();
         goto bail;
     }
 
-    ClassObject* clazz = ((Object*)obj)->clazz;
+    Object* obj = dvmDecodeIndirectRef(env, jobj);
+    ClassObject* clazz = obj->clazz;
 
     /*
      * Check this class and all of its superclasses for a matching field.
@@ -782,7 +792,7 @@
     }
 
     LOGW("JNI WARNING: inst fieldID %p not valid for class %s\n",
-        fieldID, ((Object*)obj)->clazz->descriptor);
+        fieldID, obj->clazz->descriptor);
     abortMaybe();
 
 bail:
@@ -1002,13 +1012,15 @@
  * Create a guarded copy of a primitive array.  Modifications to the copied
  * data are allowed.  Returns a pointer to the copied data.
  */
-static void* createGuardedPACopy(const ArrayObject* array, jboolean* isCopy)
+static void* createGuardedPACopy(JNIEnv* env, const jarray jarr,
+    jboolean* isCopy)
 {
-    PrimitiveType primType = array->obj.clazz->elementClass->primitiveType;
-    int len = array->length * dvmPrimitiveTypeWidth(primType);
+    ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
+    PrimitiveType primType = arrObj->obj.clazz->elementClass->primitiveType;
+    int len = arrObj->length * dvmPrimitiveTypeWidth(primType);
     void* result;
 
-    result = createGuardedCopy(array->contents, len, true);
+    result = createGuardedCopy(arrObj->contents, len, true);
 
     if (isCopy != NULL)
         *isCopy = JNI_TRUE;
@@ -1020,9 +1032,11 @@
  * Perform the array "release" operation, which may or may not copy data
  * back into the VM, and may or may not release the underlying storage.
  */
-static void* releaseGuardedPACopy(ArrayObject* array, void* dataBuf, int mode)
+static void* releaseGuardedPACopy(JNIEnv* env, jarray jarr, void* dataBuf,
+    int mode)
 {
-    PrimitiveType primType = array->obj.clazz->elementClass->primitiveType;
+    ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
+    PrimitiveType primType = arrObj->obj.clazz->elementClass->primitiveType;
     //int len = array->length * dvmPrimitiveTypeWidth(primType);
     bool release, copyBack;
     u1* result;
@@ -1053,7 +1067,7 @@
 
     if (copyBack) {
         size_t len = getGuardedCopyOriginalLen(dataBuf);
-        memcpy(array->contents, dataBuf, len);
+        memcpy(arrObj->contents, dataBuf, len);
     }
 
     if (release) {
@@ -1448,7 +1462,7 @@
         CHECK_ENTER(env, kFlag_Default);                                    \
         CHECK_CLASS(env, clazz);                                            \
         checkStaticFieldID(env, clazz, fieldID);                            \
-        CHECK_FIELD_TYPE((jobject)(u4)value, fieldID, _ftype, true);        \
+        CHECK_FIELD_TYPE(env, (jobject)(u4)value, fieldID, _ftype, true);   \
         BASE_ENV(env)->SetStatic##_jname##Field(env, clazz, fieldID,        \
             value);                                                         \
         CHECK_EXIT(env);                                                    \
@@ -1492,7 +1506,7 @@
         CHECK_ENTER(env, kFlag_Default);                                    \
         CHECK_OBJECT(env, obj);                                             \
         CHECK_INST_FIELD_ID(env, obj, fieldID);                             \
-        CHECK_FIELD_TYPE((jobject)(u4) value, fieldID, _ftype, false);      \
+        CHECK_FIELD_TYPE(env, (jobject)(u4) value, fieldID, _ftype, false); \
         BASE_ENV(env)->Set##_jname##Field(env, obj, fieldID, value);        \
         CHECK_EXIT(env);                                                    \
     }
@@ -1849,8 +1863,7 @@
         result = BASE_ENV(env)->Get##_jname##ArrayElements(env,             \
             array, isCopy);                                                 \
         if (((JNIEnvExt*)env)->forceDataCopy && result != NULL) {           \
-            result = (_ctype*)                                              \
-                createGuardedPACopy((ArrayObject*) array, isCopy);          \
+            result = (_ctype*) createGuardedPACopy(env, array, isCopy);     \
         }                                                                   \
         CHECK_EXIT(env);                                                    \
         return result;                                                      \
@@ -1865,8 +1878,7 @@
         CHECK_NON_NULL(env, elems);                                         \
         CHECK_RELEASE_MODE(env, mode);                                      \
         if (((JNIEnvExt*)env)->forceDataCopy) {                             \
-            elems = (_ctype*) releaseGuardedPACopy((ArrayObject*) array,    \
-                elems, mode);                                               \
+            elems = (_ctype*) releaseGuardedPACopy(env, array, elems, mode);\
         }                                                                   \
         BASE_ENV(env)->Release##_jname##ArrayElements(env,                  \
             array, elems, mode);                                            \
@@ -1987,7 +1999,7 @@
     void* result;
     result = BASE_ENV(env)->GetPrimitiveArrayCritical(env, array, isCopy);
     if (((JNIEnvExt*)env)->forceDataCopy && result != NULL) {
-        result = createGuardedPACopy((ArrayObject*) array, isCopy);
+        result = createGuardedPACopy(env, array, isCopy);
     }
     CHECK_EXIT(env);
     return result;
@@ -2001,7 +2013,7 @@
     CHECK_NON_NULL(env, carray);
     CHECK_RELEASE_MODE(env, mode);
     if (((JNIEnvExt*)env)->forceDataCopy) {
-        carray = releaseGuardedPACopy((ArrayObject*) array, carray, mode);
+        carray = releaseGuardedPACopy(env, array, carray, mode);
     }
     BASE_ENV(env)->ReleasePrimitiveArrayCritical(env, array, carray, mode);
     CHECK_EXIT(env);
diff --git a/vm/IndirectRefTable.c b/vm/IndirectRefTable.c
index 0106fe4..6e3ee25 100644
--- a/vm/IndirectRefTable.c
+++ b/vm/IndirectRefTable.c
@@ -35,7 +35,7 @@
 #ifndef NDEBUG
     memset(pRef->table, 0xd1, initialCount * sizeof(Object*));
 #endif
-    pRef->segmentState.all = IRT_SEGMENT_INIT;
+    pRef->segmentState.all = IRT_FIRST_SEGMENT;
     pRef->allocEntries = initialCount;
     pRef->maxEntries = maxCount;
     pRef->kind = kind;
@@ -181,6 +181,11 @@
  */
 bool dvmGetFromIndirectRefTableCheck(IndirectRefTable* pRef, IndirectRef iref)
 {
+    if (dvmGetIndirectRefType(iref) == kIndirectKindInvalid) {
+        LOGW("Invalid indirect reference 0x%08x\n", (u4) iref);
+        return false;
+    }
+
     int topIndex = pRef->segmentState.parts.topIndex;
     int idx = dvmIndirectRefToIndex(iref);
 
diff --git a/vm/IndirectRefTable.h b/vm/IndirectRefTable.h
index 4c842f2..c03353b 100644
--- a/vm/IndirectRefTable.h
+++ b/vm/IndirectRefTable.h
@@ -139,6 +139,16 @@
  * store it internally in a public structure, but the local JNI refs are
  * logically tied to interpreted stack frames anyway.)
  *
+ * Common alternative implementation: make IndirectRef a pointer to the
+ * actual reference slot.  Instead of getting a table and doing a lookup,
+ * the lookup can be done instantly.  Operations like determining the
+ * type and deleting the reference are more expensive because the table
+ * must be hunted for (i.e. you have to do a pointer comparison to see
+ * which table it's in), you can't move the table when expanding it (so
+ * realloc() is out), and tricks like serial number checking to detect
+ * stale references aren't possible (though we may be able to get similar
+ * benefits with other approaches).
+ *
  * TODO: consider a "lastDeleteIndex" for quick hole-filling when an
  * add immediately follows a delete; must invalidate after segment pop
  * (which could increase the cost/complexity of method call/return).
@@ -147,6 +157,12 @@
  * TODO: may want completely different add/remove algorithms for global
  * and local refs to improve performance.  A large circular buffer might
  * reduce the amortized cost of adding global references.
+ *
+ * TODO: if we can guarantee that the underlying storage doesn't move,
+ * e.g. by using oversized mmap regions to handle expanding tables, we may
+ * be able to avoid having to synchronize lookups.  Might make sense to
+ * add a "synchronized lookup" call that takes the mutex as an argument,
+ * and either locks or doesn't lock based on internal details.
  */
 typedef union IRTSegmentState {
     u4          all;
@@ -171,8 +187,8 @@
     //       for performance evaluation.
 } IndirectRefTable;
 
-/* initial value to use for the "cookie" */
-#define IRT_SEGMENT_INIT    0
+/* use as initial value for "cookie", and when table has only one segment */
+#define IRT_FIRST_SEGMENT   0
 
 /*
  * (This is PRIVATE, but we want it inside other inlines in this header.)
@@ -203,6 +219,14 @@
 }
 
 /*
+ * Determine what kind of indirect reference this is.
+ */
+INLINE IndirectRefKind dvmGetIndirectRefType(IndirectRef iref)
+{
+    return (u4) iref & 0x03;
+}
+
+/*
  * Initialize an IndirectRefTable.
  *
  * If "initialCount" != "maxCount", the table will expand as required.
diff --git a/vm/Jni.c b/vm/Jni.c
index 21ff19f..0a8103b 100644
--- a/vm/Jni.c
+++ b/vm/Jni.c
@@ -220,7 +220,7 @@
 
 /* fwd */
 static const struct JNINativeInterface gNativeInterface;
-static jobject addGlobalReference(jobject obj);
+static jobject addGlobalReference(Object* obj);
 
 
 #ifdef WITH_JNI_STACK_CHECK
@@ -347,7 +347,7 @@
         COMPUTE_STACK_SUM(_self)
 
 #define kGlobalRefsTableInitialSize 512
-#define kGlobalRefsTableMaxSize     51200       /* arbitrary */
+#define kGlobalRefsTableMaxSize     51200       /* arbitrary, must be < 64K */
 #define kGrefWaterInterval          100
 
 #define kTrackGrefUsage             true
@@ -572,12 +572,13 @@
 /*
  * Retrieve the ReferenceTable struct for the current thread.
  *
- * If we know the code isn't sharing JNIEnv pointers between threads, we
- * could put this into env and skip the TLS lookup.
+ * Going through "env" rather than dvmThreadSelf() is faster but will
+ * get weird if the JNI code is passing the wrong JNIEnv around.
  */
-static inline ReferenceTable* getLocalRefTable(void)
+static inline ReferenceTable* getLocalRefTable(JNIEnv* env)
 {
-    return &dvmThreadSelf()->jniLocalRefTable;
+    //return &dvmThreadSelf()->jniLocalRefTable;
+    return &((JNIEnvExt*)env)->self->jniLocalRefTable;
 }
 
 /*
@@ -592,17 +593,17 @@
  * Returns the local reference (currently just the same pointer that was
  * passed in), or NULL on failure.
  */
-static jobject addLocalReference(jobject obj)
+static jobject addLocalReference(JNIEnv* env, Object* obj)
 {
     if (obj == NULL)
         return NULL;
 
-    ReferenceTable* pRef = getLocalRefTable();
+    ReferenceTable* pRefTable = getLocalRefTable(env);
 
-    if (!dvmAddToReferenceTable(pRef, (Object*)obj)) {
-        dvmDumpReferenceTable(pRef, "JNI local");
+    if (!dvmAddToReferenceTable(pRefTable, obj)) {
+        dvmDumpReferenceTable(pRefTable, "JNI local");
         LOGE("Failed adding to JNI local ref table (has %d entries)\n",
-            (int) dvmReferenceTableEntries(pRef));
+            (int) dvmReferenceTableEntries(pRefTable));
         dvmDumpThread(dvmThreadSelf(), false);
         dvmAbort();     // spec says call FatalError; this is equivalent
     } else {
@@ -615,29 +616,42 @@
 }
 
 /*
+ * Convert an indirect reference to an Object reference.  The indirect
+ * reference may be local, global, or weak-global.
+ *
+ * If "jobj" is NULL or an invalid indirect reference, this returns NULL.
+ *
+ * [ this is currently a no-op ]
+ */
+Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj)
+{
+    return (Object*) jobj;
+}
+
+/*
  * Ensure that at least "capacity" references can be held in the local
  * refs table of the current thread.
  */
-static bool ensureLocalCapacity(int capacity)
+static bool ensureLocalCapacity(JNIEnv* env, int capacity)
 {
-    ReferenceTable* pRef = getLocalRefTable();
+    ReferenceTable* pRefTable = getLocalRefTable(env);
 
-    return (kJniLocalRefMax - (pRef->nextEntry - pRef->table) >= capacity);
+    return (kJniLocalRefMax - (pRefTable->nextEntry - pRefTable->table) >= capacity);
 }
 
 /*
  * Explicitly delete a reference from the local list.
  */
-static void deleteLocalReference(jobject obj)
+static void deleteLocalReference(JNIEnv* env, jobject jobj)
 {
-    if (obj == NULL)
+    if (jobj == NULL)
         return;
 
-    ReferenceTable* pRef = getLocalRefTable();
+    ReferenceTable* pRefTable = getLocalRefTable(env);
     Thread* self = dvmThreadSelf();
     Object** top = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefTop;
 
-    if (!dvmRemoveFromReferenceTable(pRef, top, (Object*) obj)) {
+    if (!dvmRemoveFromReferenceTable(pRefTable, top, (Object*) jobj)) {
         /*
          * Attempting to delete a local reference that is not in the
          * topmost local reference frame is a no-op.  DeleteLocalRef returns
@@ -646,7 +660,7 @@
          * going quite the way they expect.
          */
         LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry (valid=%d)\n",
-            obj, dvmIsValidObject((Object*) obj));
+            jobj, dvmIsValidObject((Object*) jobj));
     }
 }
 
@@ -656,7 +670,7 @@
  * We may add the same object more than once.  Add/remove calls are paired,
  * so it needs to appear on the list multiple times.
  */
-static jobject addGlobalReference(jobject obj)
+static jobject addGlobalReference(Object* obj)
 {
     if (obj == NULL)
         return NULL;
@@ -743,7 +757,7 @@
 
 bail:
     dvmUnlockMutex(&gDvm.jniGlobalRefLock);
-    return obj;
+    return (jobject) obj;
 }
 
 /*
@@ -753,18 +767,18 @@
  * Thought: if it's not the most recent entry, just null it out.  When we
  * fill up, do a compaction pass before we expand the list.
  */
-static void deleteGlobalReference(jobject obj)
+static void deleteGlobalReference(jobject jobj)
 {
-    if (obj == NULL)
+    if (jobj == NULL)
         return;
 
     dvmLockMutex(&gDvm.jniGlobalRefLock);
 
     if (!dvmRemoveFromReferenceTable(&gDvm.jniGlobalRefTable,
-            gDvm.jniGlobalRefTable.table, obj))
+            gDvm.jniGlobalRefTable.table, jobj))
     {
         LOGW("JNI: DeleteGlobalRef(%p) failed to find entry (valid=%d)\n",
-            obj, dvmIsValidObject((Object*) obj));
+            jobj, dvmIsValidObject((Object*) jobj));
         goto bail;
     }
 
@@ -782,6 +796,31 @@
 }
 
 /*
+ * Objects don't currently move, so we just need to create a reference
+ * that will ensure the array object isn't collected.
+ *
+ * Currently just using global references.
+ *
+ * TODO: if the same array gets pinned more than N times, print a warning
+ * (but only if CheckJNI is enabled)
+ */
+static void pinPrimitiveArray(ArrayObject* arrayObj)
+{
+    (void) addGlobalReference((Object*) arrayObj);
+}
+
+/*
+ * Un-pin the array object.  If an object was pinned twice, it must be
+ * unpinned twice before it's free to move.
+ *
+ * Currently just removing a global reference.
+ */
+static void unpinPrimitiveArray(ArrayObject* arrayObj)
+{
+    (void) deleteGlobalReference((jobject) arrayObj);
+}
+
+/*
  * GC helper function to mark all JNI global references.
  */
 void dvmGcMarkJniGlobalRefs()
@@ -886,32 +925,32 @@
  * something is both global and local, we can't tell them apart, and always
  * return "local".
  */
-jobjectRefType dvmGetJNIRefType(Object* obj)
+jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj)
 {
-    ReferenceTable* pRef = getLocalRefTable();
+    ReferenceTable* pRefTable = getLocalRefTable(env);
     Thread* self = dvmThreadSelf();
     //Object** top;
     Object** ptr;
 
     /* check args */
-    if (findInArgList(self, obj)) {
-        //LOGI("--- REF found %p on stack\n", obj);
+    if (findInArgList(self, jobj)) {
+        //LOGI("--- REF found %p on stack\n", jobj);
         return JNILocalRefType;
     }
 
     /* check locals */
     //top = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefTop;
-    if (dvmFindInReferenceTable(pRef, pRef->table, obj) != NULL) {
-        //LOGI("--- REF found %p in locals\n", obj);
+    if (dvmFindInReferenceTable(pRefTable, pRefTable->table, jobj) != NULL) {
+        //LOGI("--- REF found %p in locals\n", jobj);
         return JNILocalRefType;
     }
 
     /* check globals */
     dvmLockMutex(&gDvm.jniGlobalRefLock);
     if (dvmFindInReferenceTable(&gDvm.jniGlobalRefTable,
-            gDvm.jniGlobalRefTable.table, obj))
+            gDvm.jniGlobalRefTable.table, jobj))
     {
-        //LOGI("--- REF found %p in globals\n", obj);
+        //LOGI("--- REF found %p in globals\n", jobj);
         dvmUnlockMutex(&gDvm.jniGlobalRefLock);
         return JNIGlobalRefType;
     }
@@ -1042,14 +1081,15 @@
     }
 }
 
+
 /*
  * Track a JNI MonitorExit in the current thread.
  */
 static void trackMonitorExit(Thread* self, Object* obj)
 {
-    ReferenceTable* refTable = &self->jniMonitorRefTable;
+    ReferenceTable* pRefTable = &self->jniMonitorRefTable;
 
-    if (!dvmRemoveFromReferenceTable(refTable, refTable->table, obj)) {
+    if (!dvmRemoveFromReferenceTable(pRefTable, pRefTable->table, obj)) {
         LOGE("JNI monitor %p not found in tracking list\n", obj);
         /* keep going? */
     } else {
@@ -1062,13 +1102,13 @@
  */
 void dvmReleaseJniMonitors(Thread* self)
 {
-    ReferenceTable* refTable = &self->jniMonitorRefTable;
-    Object** top = refTable->table;
+    ReferenceTable* pRefTable = &self->jniMonitorRefTable;
+    Object** top = pRefTable->table;
 
     if (top == NULL)
         return;
 
-    Object** ptr = refTable->nextEntry;
+    Object** ptr = pRefTable->nextEntry;
     while (--ptr >= top) {
         if (!dvmUnlockObject(self, *ptr)) {
             LOGW("Unable to unlock monitor %p at thread detach\n", *ptr);
@@ -1078,7 +1118,7 @@
     }
 
     /* zap it */
-    refTable->nextEntry = refTable->table;
+    pRefTable->nextEntry = pRefTable->table;
 }
 
 #ifdef WITH_JNI_STACK_CHECK
@@ -1171,7 +1211,7 @@
     UNUSED_PARAMETER(bufLen);
 
     JNI_ENTER();
-    LOGW("Rejecting JNI DefineClass request\n");
+    LOGW("JNI DefineClass is not supported\n");
     JNI_EXIT();
     return NULL;
 }
@@ -1196,6 +1236,7 @@
 
     const Method* thisMethod;
     ClassObject* clazz;
+    jclass jclazz = NULL;
     Object* loader;
     char* descriptor = NULL;
 
@@ -1224,25 +1265,30 @@
     }
 
     clazz = dvmFindClassNoInit(descriptor, loader);
-    clazz = addLocalReference(clazz);
+    jclazz = addLocalReference(env, (Object*) clazz);
 
 bail:
     free(descriptor);
-    
+
     JNI_EXIT();
-    return (jclass)clazz;
+    return jclazz;
 }
 
 /*
  * Return the superclass of a class.
  */
-static jclass GetSuperclass(JNIEnv* env, jclass clazz)
+static jclass GetSuperclass(JNIEnv* env, jclass jclazz)
 {
     JNI_ENTER();
-    jclass super = (jclass) ((ClassObject*) clazz)->super;
-    super = addLocalReference(super);
+    jclass jsuper;
+
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+    if (clazz == NULL)
+        jsuper = NULL;
+    else
+        jsuper = addLocalReference(env, (Object*)clazz->super);
     JNI_EXIT();
-    return super;
+    return jsuper;
 }
 
 /*
@@ -1250,12 +1296,14 @@
  *
  * Like IsInstanceOf, but with a pair of class objects instead of obj+class.
  */
-static jboolean IsAssignableFrom(JNIEnv* env, jclass clazz1, jclass clazz2)
+static jboolean IsAssignableFrom(JNIEnv* env, jclass jclazz1, jclass jclazz2)
 {
     JNI_ENTER();
 
-    jboolean result;
-    result = dvmInstanceof((ClassObject*) clazz1, (ClassObject*) clazz2);
+    ClassObject* clazz1 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz1);
+    ClassObject* clazz2 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz2);
+
+    jboolean result = dvmInstanceof(clazz1, clazz2);
 
     JNI_EXIT();
     return result;
@@ -1264,11 +1312,12 @@
 /*
  * Given a java.lang.reflect.Method or .Constructor, return a methodID.
  */
-static jmethodID FromReflectedMethod(JNIEnv* env, jobject method)
+static jmethodID FromReflectedMethod(JNIEnv* env, jobject jmethod)
 {
     JNI_ENTER();
     jmethodID methodID;
-    methodID = (jmethodID) dvmGetMethodFromReflectObj((Object*)method);
+    Object* method = dvmDecodeIndirectRef(env, jmethod);
+    methodID = (jmethodID) dvmGetMethodFromReflectObj(method);
     JNI_EXIT();
     return methodID;
 }
@@ -1276,10 +1325,12 @@
 /*
  * Given a java.lang.reflect.Field, return a fieldID.
  */
-static jfieldID FromReflectedField(JNIEnv* env, jobject field)
+static jfieldID FromReflectedField(JNIEnv* env, jobject jfield)
 {
     JNI_ENTER();
-    jfieldID fieldID = (jfieldID) dvmGetFieldFromReflectObj((Object*)field);
+    jfieldID fieldID;
+    Object* field = dvmDecodeIndirectRef(env, jfield);
+    fieldID = (jfieldID) dvmGetFieldFromReflectObj(field);
     JNI_EXIT();
     return fieldID;
 }
@@ -1291,17 +1342,16 @@
  *
  * Throws OutOfMemory and returns NULL on failure.
  */
-static jobject ToReflectedMethod(JNIEnv* env, jclass cls, jmethodID methodID,
+static jobject ToReflectedMethod(JNIEnv* env, jclass jcls, jmethodID methodID,
     jboolean isStatic)
 {
     JNI_ENTER();
-    jobject obj;
-    obj = (jobject) dvmCreateReflectObjForMethod((ClassObject*) cls,
-            (Method*) methodID);
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
+    Object* obj = dvmCreateReflectObjForMethod(clazz, (Method*) methodID);
     dvmReleaseTrackedAlloc(obj, NULL);
-    obj = addLocalReference(obj);
+    jobject jobj = addLocalReference(env, obj);
     JNI_EXIT();
-    return obj;
+    return jobj;
 }
 
 /*
@@ -1311,50 +1361,50 @@
  *
  * Throws OutOfMemory and returns NULL on failure.
  */
-static jobject ToReflectedField(JNIEnv* env, jclass cls, jfieldID fieldID,
+static jobject ToReflectedField(JNIEnv* env, jclass jcls, jfieldID fieldID,
     jboolean isStatic)
 {
     JNI_ENTER();
-    jobject obj;
-    obj = (jobject) dvmCreateReflectObjForField((ClassObject*) cls,
-            (Field*) fieldID);
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
+    Object* obj = dvmCreateReflectObjForField(jcls, (Field*) fieldID);
     dvmReleaseTrackedAlloc(obj, NULL);
-    obj = addLocalReference(obj);
+    jobject jobj = addLocalReference(env, obj);
     JNI_EXIT();
-    return obj;
+    return jobj;
 }
 
-
 /*
  * Take this exception and throw it.
  */
-static jint Throw(JNIEnv* env, jthrowable obj)
+static jint Throw(JNIEnv* env, jthrowable jobj)
 {
     JNI_ENTER();
 
     jint retval;
 
-    if (obj != NULL) {
+    if (jobj != NULL) {
+        Object* obj = dvmDecodeIndirectRef(env, jobj);
         dvmSetException(_self, obj);
         retval = JNI_OK;
-    } else
+    } else {
         retval = JNI_ERR;
+    }
 
     JNI_EXIT();
     return retval;
 }
 
 /*
- * Constructs an exeption object from the specified class with the message
+ * Constructs an exception object from the specified class with the message
  * specified by "message", and throws it.
  */
-static jint ThrowNew(JNIEnv* env, jclass clazz, const char* message)
+static jint ThrowNew(JNIEnv* env, jclass jclazz, const char* message)
 {
     JNI_ENTER();
 
-    ClassObject* classObj = (ClassObject*) clazz;
-
-    dvmThrowExceptionByClass(classObj, message);
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+    dvmThrowExceptionByClass(clazz, message);
+    // TODO: should return failure if this didn't work (e.g. OOM)
 
     JNI_EXIT();
     return JNI_OK;
@@ -1373,10 +1423,10 @@
     JNI_ENTER();
 
     Object* exception;
-    Object* localException;
+    jobject localException;
 
-    exception = (Object*) dvmGetException(_self);
-    localException = addLocalReference(exception);
+    exception = dvmGetException(_self);
+    localException = addLocalReference(env, exception);
     if (localException == NULL && exception != NULL) {
         /*
          * We were unable to add a new local reference, and threw a new
@@ -1441,7 +1491,7 @@
 {
     JNI_ENTER();
     int result = JNI_OK;
-    if (!ensureLocalCapacity(capacity) ||
+    if (!ensureLocalCapacity(env, capacity) ||
         !dvmPushLocalFrame(_self /*dvmThreadSelf()*/, dvmGetCurrentJNIMethod()))
     {
         /* yes, OutOfMemoryError, not StackOverflowError */
@@ -1458,16 +1508,17 @@
  * Pop the local frame off.  If "result" is not null, add it as a
  * local reference on the now-current frame.
  */
-static jobject PopLocalFrame(JNIEnv* env, jobject result)
+static jobject PopLocalFrame(JNIEnv* env, jobject jresult)
 {
     JNI_ENTER();
+    Object* result = dvmDecodeIndirectRef(env, jresult);
     if (!dvmPopLocalFrame(_self /*dvmThreadSelf()*/)) {
         LOGW("JNI WARNING: too many PopLocalFrame calls\n");
         dvmClearException(_self);
         dvmThrowException("Ljava/lang/RuntimeException;",
             "too many PopLocalFrame calls");
     }
-    result = addLocalReference(result);
+    jresult = addLocalReference(env, result);
     JNI_EXIT();
     return result;
 }
@@ -1475,9 +1526,10 @@
 /*
  * Add a reference to the global list.
  */
-static jobject NewGlobalRef(JNIEnv* env, jobject obj)
+static jobject NewGlobalRef(JNIEnv* env, jobject jobj)
 {
     JNI_ENTER();
+    Object* obj = dvmDecodeIndirectRef(env, jobj);
     jobject retval = addGlobalReference(obj);
     JNI_EXIT();
     return retval;
@@ -1486,10 +1538,10 @@
 /*
  * Delete a reference from the global list.
  */
-static void DeleteGlobalRef(JNIEnv* env, jobject globalRef)
+static void DeleteGlobalRef(JNIEnv* env, jobject jglobalRef)
 {
     JNI_ENTER();
-    deleteGlobalReference(globalRef);
+    deleteGlobalReference(jglobalRef);
     JNI_EXIT();
 }
 
@@ -1497,12 +1549,11 @@
 /*
  * Add a reference to the local list.
  */
-static jobject NewLocalRef(JNIEnv* env, jobject ref)
+static jobject NewLocalRef(JNIEnv* env, jobject jref)
 {
     JNI_ENTER();
-
-    jobject retval = addLocalReference(ref);
-
+    Object* obj = dvmDecodeIndirectRef(env, jref);
+    jobject retval = addLocalReference(env, obj);
     JNI_EXIT();
     return retval;
 }
@@ -1510,10 +1561,10 @@
 /*
  * Delete a reference from the local list.
  */
-static void DeleteLocalRef(JNIEnv* env, jobject localRef)
+static void DeleteLocalRef(JNIEnv* env, jobject jlocalRef)
 {
     JNI_ENTER();
-    deleteLocalReference(localRef);
+    deleteLocalReference(env, jlocalRef);
     JNI_EXIT();
 }
 
@@ -1521,10 +1572,10 @@
  * Ensure that the local references table can hold at least this many
  * references.
  */
-static jint EnsureLocalCapacity(JNIEnv *env, jint capacity)
+static jint EnsureLocalCapacity(JNIEnv* env, jint capacity)
 {
     JNI_ENTER();
-    bool okay = ensureLocalCapacity(capacity);
+    bool okay = ensureLocalCapacity(env, capacity);
     if (!okay) {
         dvmThrowException("Ljava/lang/OutOfMemoryError;",
             "can't ensure local reference capacity");
@@ -1540,10 +1591,12 @@
 /*
  * Determine whether two Object references refer to the same underlying object.
  */
-static jboolean IsSameObject(JNIEnv* env, jobject ref1, jobject ref2)
+static jboolean IsSameObject(JNIEnv* env, jobject jref1, jobject jref2)
 {
     JNI_ENTER();
-    jboolean result = (ref1 == ref2);
+    Object* obj1 = dvmDecodeIndirectRef(env, jref1);
+    Object* obj2 = dvmDecodeIndirectRef(env, jref2);
+    jboolean result = (obj1 == obj2);
     JNI_EXIT();
     return result;
 }
@@ -1555,83 +1608,81 @@
 {
     JNI_ENTER();
 
-    ClassObject* clazz = (ClassObject*) jclazz;
-    jobject newObj;
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+    jobject result;
 
     if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
         assert(dvmCheckException(_self));
-        newObj = NULL;
+        result = NULL;
     } else {
-        newObj = (jobject) dvmAllocObject(clazz, ALLOC_DONT_TRACK);
-        newObj = addLocalReference(newObj);
+        Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+        result = addLocalReference(env, newObj);
     }
 
     JNI_EXIT();
-    return newObj;
+    return result;
 }
 
 /*
- * Construct a new object.
+ * Allocate a new object and invoke the supplied constructor.
  */
 static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...)
 {
     JNI_ENTER();
-
-    ClassObject* clazz = (ClassObject*) jclazz;
-    jobject newObj;
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+    jobject result;
 
     if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
         assert(dvmCheckException(_self));
-        newObj = NULL;
+        result = NULL;
     } else {
-        newObj = (jobject) dvmAllocObject(clazz, ALLOC_DONT_TRACK);
-        newObj = addLocalReference(newObj);
+        Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+        result = addLocalReference(env, newObj);
         if (newObj != NULL) {
             JValue unused;
             va_list args;
             va_start(args, methodID);
-            dvmCallMethodV(_self, (Method*) methodID, (Object*)newObj, &unused,
-                args);
+            dvmCallMethodV(_self, (Method*) methodID, newObj, &unused, args);
             va_end(args);
         }
     }
 
     JNI_EXIT();
-    return newObj;
+    return result;
 }
-static jobject NewObjectV(JNIEnv* env, jclass clazz, jmethodID methodID,
+static jobject NewObjectV(JNIEnv* env, jclass jclazz, jmethodID methodID,
     va_list args)
 {
     JNI_ENTER();
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+    jobject result;
 
-    jobject newObj;
-    newObj = (jobject) dvmAllocObject((ClassObject*) clazz, ALLOC_DONT_TRACK);
-    newObj = addLocalReference(newObj);
+    Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+    result = addLocalReference(env, newObj);
     if (newObj != NULL) {
         JValue unused;
-        dvmCallMethodV(_self, (Method*) methodID, (Object*)newObj, &unused,
-            args);
+        dvmCallMethodV(_self, (Method*) methodID, newObj, &unused, args);
     }
 
     JNI_EXIT();
-    return newObj;
+    return result;
 }
-static jobject NewObjectA(JNIEnv* env, jclass clazz, jmethodID methodID,
+static jobject NewObjectA(JNIEnv* env, jclass jclazz, jmethodID methodID,
     jvalue* args)
 {
     JNI_ENTER();
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+    jobject result;
 
-    jobject newObj;
-    newObj = (jobject) dvmAllocObject((ClassObject*) clazz, ALLOC_DONT_TRACK);
-    newObj = addLocalReference(newObj);
+    Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
+    result = addLocalReference(env, newObj);
     if (newObj != NULL) {
         JValue unused;
-        dvmCallMethodA(_self, (Method*) methodID, (Object*)newObj, &unused,
-            args);
+        dvmCallMethodA(_self, (Method*) methodID, newObj, &unused, args);
     }
 
     JNI_EXIT();
-    return newObj;
+    return result;
 }
 
 /*
@@ -1639,33 +1690,37 @@
  *
  * JNI spec says: obj must not be NULL.
  */
-static jclass GetObjectClass(JNIEnv* env, jobject obj)
+static jclass GetObjectClass(JNIEnv* env, jobject jobj)
 {
     JNI_ENTER();
 
-    assert(obj != NULL);
+    assert(jobj != NULL);
 
-    jclass clazz;
-    clazz = (jclass) ((Object*)obj)->clazz;
-    clazz = addLocalReference(clazz);
+    Object* obj = dvmDecodeIndirectRef(env, jobj);
+    jclass jclazz = addLocalReference(env, (Object*) obj->clazz);
 
     JNI_EXIT();
-    return clazz;
+    return jclazz;
 }
 
 /*
  * Determine whether "obj" is an instance of "clazz".
  */
-static jboolean IsInstanceOf(JNIEnv* env, jobject obj, jclass clazz)
+static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass jclazz)
 {
     JNI_ENTER();
 
+    assert(jclazz != NULL);
+
     jboolean result;
 
-    if (obj == NULL)
+    if (jobj == NULL) {
         result = true;
-    else
-        result = dvmInstanceof(((Object*)obj)->clazz, (ClassObject*) clazz);
+    } else {
+        Object* obj = dvmDecodeIndirectRef(env, jobj);
+        ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
+        result = dvmInstanceof(obj->clazz, clazz);
+    }
 
     JNI_EXIT();
     return result;
@@ -1685,7 +1740,7 @@
 {
     JNI_ENTER();
 
-    ClassObject* clazz = (ClassObject*) jclazz;
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
     jmethodID id = NULL;
 
     if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
@@ -1736,7 +1791,7 @@
 {
     JNI_ENTER();
 
-    ClassObject* clazz = (ClassObject*) jclazz;
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
     jfieldID id;
 
     if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
@@ -1762,7 +1817,7 @@
 {
     JNI_ENTER();
 
-    ClassObject* clazz = (ClassObject*) jclazz;
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
     jmethodID id;
 
     if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
@@ -1802,7 +1857,7 @@
 {
     JNI_ENTER();
 
-    ClassObject* clazz = (ClassObject*) jclazz;
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
     jfieldID id;
 
     if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
@@ -1823,15 +1878,19 @@
  * If we get an object reference, add it to the local refs list.
  */
 #define GET_STATIC_TYPE_FIELD(_ctype, _jname, _isref)                       \
-    static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass clazz,       \
+    static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass jclazz,      \
         jfieldID fieldID)                                                   \
     {                                                                       \
-        UNUSED_PARAMETER(clazz);                                            \
+        UNUSED_PARAMETER(jclazz);                                           \
         JNI_ENTER();                                                        \
         StaticField* sfield = (StaticField*) fieldID;                       \
-        _ctype value = dvmGetStaticField##_jname(sfield);                   \
-        if (_isref)     /* only when _ctype==jobject */                     \
-            value = (_ctype)(u4)addLocalReference((jobject)(u4)value);      \
+        _ctype value;                                                       \
+        if (_isref) {   /* only when _ctype==jobject */                     \
+            Object* obj = dvmGetStaticFieldObject(sfield);                  \
+            value = (_ctype)(u4)addLocalReference(env, obj);                \
+        } else {                                                            \
+            value = dvmGetStaticField##_jname(sfield);                      \
+        }                                                                   \
         JNI_EXIT();                                                         \
         return value;                                                       \
     }
@@ -1848,25 +1907,30 @@
 /*
  * Set a static field.
  */
-#define SET_STATIC_TYPE_FIELD(_ctype, _jname, _jvfld)                       \
-    static void SetStatic##_jname##Field(JNIEnv* env, jclass clazz,         \
+#define SET_STATIC_TYPE_FIELD(_ctype, _jname, _isref)                       \
+    static void SetStatic##_jname##Field(JNIEnv* env, jclass jclazz,        \
         jfieldID fieldID, _ctype value)                                     \
     {                                                                       \
-        UNUSED_PARAMETER(clazz);                                            \
+        UNUSED_PARAMETER(jclazz);                                           \
         JNI_ENTER();                                                        \
         StaticField* sfield = (StaticField*) fieldID;                       \
-        dvmSetStaticField##_jname(sfield, value);                           \
+        if (_isref) {   /* only when _ctype==jobject */                     \
+            Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \
+            dvmSetStaticFieldObject(sfield, valObj);                        \
+        } else {                                                            \
+            dvmSetStaticField##_jname(sfield, value);                       \
+        }                                                                   \
         JNI_EXIT();                                                         \
     }
-SET_STATIC_TYPE_FIELD(jobject, Object, l);
-SET_STATIC_TYPE_FIELD(jboolean, Boolean, z);
-SET_STATIC_TYPE_FIELD(jbyte, Byte, b);
-SET_STATIC_TYPE_FIELD(jchar, Char, c);
-SET_STATIC_TYPE_FIELD(jshort, Short, s);
-SET_STATIC_TYPE_FIELD(jint, Int, i);
-SET_STATIC_TYPE_FIELD(jlong, Long, j);
-SET_STATIC_TYPE_FIELD(jfloat, Float, f);
-SET_STATIC_TYPE_FIELD(jdouble, Double, d);
+SET_STATIC_TYPE_FIELD(jobject, Object, true);
+SET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
+SET_STATIC_TYPE_FIELD(jbyte, Byte, false);
+SET_STATIC_TYPE_FIELD(jchar, Char, false);
+SET_STATIC_TYPE_FIELD(jshort, Short, false);
+SET_STATIC_TYPE_FIELD(jint, Int, false);
+SET_STATIC_TYPE_FIELD(jlong, Long, false);
+SET_STATIC_TYPE_FIELD(jfloat, Float, false);
+SET_STATIC_TYPE_FIELD(jdouble, Double, false);
 
 /*
  * Get an instance field.
@@ -1874,14 +1938,19 @@
  * If we get an object reference, add it to the local refs list.
  */
 #define GET_TYPE_FIELD(_ctype, _jname, _isref)                              \
-    static _ctype Get##_jname##Field(JNIEnv* env, jobject obj,              \
+    static _ctype Get##_jname##Field(JNIEnv* env, jobject jobj,             \
         jfieldID fieldID)                                                   \
     {                                                                       \
         JNI_ENTER();                                                        \
+        Object* obj = dvmDecodeIndirectRef(env, jobj);                      \
         InstField* field = (InstField*) fieldID;                            \
-        _ctype value = dvmGetField##_jname((Object*) obj,field->byteOffset);\
-        if (_isref)     /* only when _ctype==jobject */                     \
-            value = (_ctype)(u4)addLocalReference((jobject)(u4)value);      \
+        _ctype value;                                                       \
+        if (_isref) {   /* only when _ctype==jobject */                     \
+            Object* valObj = dvmGetFieldObject(obj, field->byteOffset);     \
+            value = (_ctype)(u4)addLocalReference(env, valObj);             \
+        } else {                                                            \
+            value = dvmGetField##_jname(obj, field->byteOffset);            \
+        }                                                                   \
         JNI_EXIT();                                                         \
         return value;                                                       \
     }
@@ -1898,24 +1967,30 @@
 /*
  * Set an instance field.
  */
-#define SET_TYPE_FIELD(_ctype, _jname)                                      \
-    static void Set##_jname##Field(JNIEnv* env, jobject obj,                \
+#define SET_TYPE_FIELD(_ctype, _jname, _isref)                              \
+    static void Set##_jname##Field(JNIEnv* env, jobject jobj,               \
         jfieldID fieldID, _ctype value)                                     \
     {                                                                       \
         JNI_ENTER();                                                        \
+        Object* obj = dvmDecodeIndirectRef(env, jobj);                      \
         InstField* field = (InstField*) fieldID;                            \
-        dvmSetField##_jname((Object*) obj, field->byteOffset, value);       \
+        if (_isref) {   /* only when _ctype==jobject */                     \
+            Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \
+            dvmSetFieldObject(obj, field->byteOffset, valObj);              \
+        } else {                                                            \
+            dvmSetField##_jname(obj, field->byteOffset, value);             \
+        }                                                                   \
         JNI_EXIT();                                                         \
     }
-SET_TYPE_FIELD(jobject, Object);
-SET_TYPE_FIELD(jboolean, Boolean);
-SET_TYPE_FIELD(jbyte, Byte);
-SET_TYPE_FIELD(jchar, Char);
-SET_TYPE_FIELD(jshort, Short);
-SET_TYPE_FIELD(jint, Int);
-SET_TYPE_FIELD(jlong, Long);
-SET_TYPE_FIELD(jfloat, Float);
-SET_TYPE_FIELD(jdouble, Double);
+SET_TYPE_FIELD(jobject, Object, true);
+SET_TYPE_FIELD(jboolean, Boolean, false);
+SET_TYPE_FIELD(jbyte, Byte, false);
+SET_TYPE_FIELD(jchar, Char, false);
+SET_TYPE_FIELD(jshort, Short, false);
+SET_TYPE_FIELD(jint, Int, false);
+SET_TYPE_FIELD(jlong, Long, false);
+SET_TYPE_FIELD(jfloat, Float, false);
+SET_TYPE_FIELD(jdouble, Double, false);
 
 /*
  * Make a virtual method call.
@@ -1924,60 +1999,60 @@
  * returning an Object, we have to add it to the local references table.
  */
 #define CALL_VIRTUAL(_ctype, _jname, _retfail, _retok, _isref)              \
-    static _ctype Call##_jname##Method(JNIEnv* env, jobject obj,            \
+    static _ctype Call##_jname##Method(JNIEnv* env, jobject jobj,           \
         jmethodID methodID, ...)                                            \
     {                                                                       \
         JNI_ENTER();                                                        \
-        Object* dobj = (Object*) obj;                                       \
+        Object* obj = dvmDecodeIndirectRef(env, jobj);                      \
         const Method* meth;                                                 \
         va_list args;                                                       \
         JValue result;                                                      \
-        meth = dvmGetVirtualizedMethod(dobj->clazz, (Method*)methodID);     \
+        meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID);      \
         if (meth == NULL) {                                                 \
             JNI_EXIT();                                                     \
             return _retfail;                                                \
         }                                                                   \
         va_start(args, methodID);                                           \
-        dvmCallMethodV(_self, meth, dobj, &result, args);                   \
+        dvmCallMethodV(_self, meth, obj, &result, args);                    \
         va_end(args);                                                       \
         if (_isref)                                                         \
-            result.l = addLocalReference(result.l);                         \
+            result.l = addLocalReference(env, result.l);                    \
         JNI_EXIT();                                                         \
         return _retok;                                                      \
     }                                                                       \
-    static _ctype Call##_jname##MethodV(JNIEnv* env, jobject obj,           \
+    static _ctype Call##_jname##MethodV(JNIEnv* env, jobject jobj,          \
         jmethodID methodID, va_list args)                                   \
     {                                                                       \
         JNI_ENTER();                                                        \
-        Object* dobj = (Object*) obj;                                       \
+        Object* obj = dvmDecodeIndirectRef(env, jobj);                      \
         const Method* meth;                                                 \
         JValue result;                                                      \
-        meth = dvmGetVirtualizedMethod(dobj->clazz, (Method*)methodID);     \
+        meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID);      \
         if (meth == NULL) {                                                 \
             JNI_EXIT();                                                     \
             return _retfail;                                                \
         }                                                                   \
-        dvmCallMethodV(_self, meth, dobj, &result, args);                   \
+        dvmCallMethodV(_self, meth, obj, &result, args);                    \
         if (_isref)                                                         \
-            result.l = addLocalReference(result.l);                         \
+            result.l = addLocalReference(env, result.l);                    \
         JNI_EXIT();                                                         \
         return _retok;                                                      \
     }                                                                       \
-    static _ctype Call##_jname##MethodA(JNIEnv* env, jobject obj,           \
+    static _ctype Call##_jname##MethodA(JNIEnv* env, jobject jobj,          \
         jmethodID methodID, jvalue* args)                                   \
     {                                                                       \
         JNI_ENTER();                                                        \
-        Object* dobj = (Object*) obj;                                       \
+        Object* obj = dvmDecodeIndirectRef(env, jobj);                      \
         const Method* meth;                                                 \
         JValue result;                                                      \
-        meth = dvmGetVirtualizedMethod(dobj->clazz, (Method*)methodID);     \
+        meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID);      \
         if (meth == NULL) {                                                 \
             JNI_EXIT();                                                     \
             return _retfail;                                                \
         }                                                                   \
-        dvmCallMethodA(_self, meth, dobj, &result, args);                   \
+        dvmCallMethodA(_self, meth, obj, &result, args);                    \
         if (_isref)                                                         \
-            result.l = addLocalReference(result.l);                         \
+            result.l = addLocalReference(env, result.l);                    \
         JNI_EXIT();                                                         \
         return _retok;                                                      \
     }
@@ -2000,63 +2075,66 @@
  * Three versions (..., va_list, jvalue[]) for each return type.
  */
 #define CALL_NONVIRTUAL(_ctype, _jname, _retfail, _retok, _isref)           \
-    static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, jobject obj,  \
-        jclass clazz, jmethodID methodID, ...)                              \
+    static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, jobject jobj, \
+        jclass jclazz, jmethodID methodID, ...)                             \
     {                                                                       \
         JNI_ENTER();                                                        \
-        Object* dobj = (Object*) obj;                                       \
+        Object* obj = dvmDecodeIndirectRef(env, jobj);                      \
+        ClassObject* clazz =                                                \
+            (ClassObject*) dvmDecodeIndirectRef(env, jclazz);               \
         const Method* meth;                                                 \
         va_list args;                                                       \
         JValue result;                                                      \
-        meth = dvmGetVirtualizedMethod((ClassObject*)clazz,                 \
-                (Method*)methodID);                                         \
+        meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID);           \
         if (meth == NULL) {                                                 \
             JNI_EXIT();                                                     \
             return _retfail;                                                \
         }                                                                   \
         va_start(args, methodID);                                           \
-        dvmCallMethodV(_self, meth, dobj, &result, args);                   \
+        dvmCallMethodV(_self, meth, obj, &result, args);                    \
         if (_isref)                                                         \
-            result.l = addLocalReference(result.l);                         \
+            result.l = addLocalReference(env, result.l);                    \
         va_end(args);                                                       \
         JNI_EXIT();                                                         \
         return _retok;                                                      \
     }                                                                       \
-    static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject obj, \
-        jclass clazz, jmethodID methodID, va_list args)                     \
+    static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject jobj,\
+        jclass jclazz, jmethodID methodID, va_list args)                    \
     {                                                                       \
         JNI_ENTER();                                                        \
-        Object* dobj = (Object*) obj;                                       \
+        Object* obj = dvmDecodeIndirectRef(env, jobj);                      \
+        ClassObject* clazz =                                                \
+            (ClassObject*) dvmDecodeIndirectRef(env, jclazz);               \
         const Method* meth;                                                 \
         JValue result;                                                      \
-        meth = dvmGetVirtualizedMethod((ClassObject*)clazz,                 \
-                (Method*)methodID);                                         \
+        meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID);           \
         if (meth == NULL) {                                                 \
             JNI_EXIT();                                                     \
             return _retfail;                                                \
         }                                                                   \
-        dvmCallMethodV(_self, meth, dobj, &result, args);                   \
+        dvmCallMethodV(_self, meth, obj, &result, args);                    \
         if (_isref)                                                         \
-            result.l = addLocalReference(result.l);                         \
+            result.l = addLocalReference(env, result.l);                    \
         JNI_EXIT();                                                         \
         return _retok;                                                      \
     }                                                                       \
-    static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject obj, \
-        jclass clazz, jmethodID methodID, jvalue* args)                     \
+    static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject jobj,\
+        jclass jclazz, jmethodID methodID, jvalue* args)                    \
     {                                                                       \
         JNI_ENTER();                                                        \
-        Object* dobj = (Object*) obj;                                       \
+        Object* obj = dvmDecodeIndirectRef(env, jobj);                      \
+        ClassObject* clazz =                                                \
+            (ClassObject*) dvmDecodeIndirectRef(env, jclazz);               \
         const Method* meth;                                                 \
         JValue result;                                                      \
-        meth = dvmGetVirtualizedMethod((ClassObject*)clazz,                 \
-                (Method*)methodID);                                         \
+        meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID);           \
         if (meth == NULL) {                                                 \
             JNI_EXIT();                                                     \
             return _retfail;                                                \
         }                                                                   \
-        dvmCallMethodA(_self, meth, dobj, &result, args);                   \
+        dvmCallMethodA(_self, meth, obj, &result, args);                    \
         if (_isref)                                                         \
-            result.l = addLocalReference(result.l);                         \
+            result.l = addLocalReference(env, result.l);                    \
         JNI_EXIT();                                                         \
         return _retok;                                                      \
     }
@@ -2076,42 +2154,42 @@
  * Call a static method.
  */
 #define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref)               \
-    static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass clazz,     \
+    static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass jclazz,    \
         jmethodID methodID, ...)                                            \
     {                                                                       \
+        UNUSED_PARAMETER(jclazz);                                           \
         JNI_ENTER();                                                        \
         JValue result;                                                      \
         va_list args;                                                       \
-        assert((ClassObject*) clazz == ((Method*)methodID)->clazz);         \
         va_start(args, methodID);                                           \
         dvmCallMethodV(_self, (Method*) methodID, NULL, &result, args);     \
         va_end(args);                                                       \
         if (_isref)                                                         \
-            result.l = addLocalReference(result.l);                         \
+            result.l = addLocalReference(env, result.l);                    \
         JNI_EXIT();                                                         \
         return _retok;                                                      \
     }                                                                       \
-    static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass clazz,    \
+    static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz,   \
         jmethodID methodID, va_list args)                                   \
     {                                                                       \
+        UNUSED_PARAMETER(jclazz);                                           \
         JNI_ENTER();                                                        \
         JValue result;                                                      \
-        assert((ClassObject*) clazz == ((Method*)methodID)->clazz);         \
         dvmCallMethodV(_self, (Method*) methodID, NULL, &result, args);     \
         if (_isref)                                                         \
-            result.l = addLocalReference(result.l);                         \
+            result.l = addLocalReference(env, result.l);                    \
         JNI_EXIT();                                                         \
         return _retok;                                                      \
     }                                                                       \
-    static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass clazz,    \
+    static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass jclazz,   \
         jmethodID methodID, jvalue* args)                                   \
     {                                                                       \
+        UNUSED_PARAMETER(jclazz);                                           \
         JNI_ENTER();                                                        \
         JValue result;                                                      \
-        assert((ClassObject*) clazz == ((Method*)methodID)->clazz);         \
         dvmCallMethodA(_self, (Method*) methodID, NULL, &result, args);     \
         if (_isref)                                                         \
-            result.l = addLocalReference(result.l);                         \
+            result.l = addLocalReference(env, result.l);                    \
         JNI_EXIT();                                                         \
         return _retok;                                                      \
     }
@@ -2135,12 +2213,14 @@
 static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len)
 {
     JNI_ENTER();
+    jobject retval;
 
-    StringObject* jstr;
-    jstr = dvmCreateStringFromUnicode(unicodeChars, len);
-    if (jstr != NULL) {
+    StringObject* jstr = dvmCreateStringFromUnicode(unicodeChars, len);
+    if (jstr == NULL) {
+        retval = NULL;
+    } else {
         dvmReleaseTrackedAlloc((Object*) jstr, NULL);
-        jstr = addLocalReference((jstring) jstr);
+        retval = addLocalReference(env, (Object*) jstr);
     }
 
     JNI_EXIT();
@@ -2150,38 +2230,34 @@
 /*
  * Return the length of a String in Unicode character units.
  */
-static jsize GetStringLength(JNIEnv* env, jstring string)
+static jsize GetStringLength(JNIEnv* env, jstring jstr)
 {
     JNI_ENTER();
 
-    jsize len = dvmStringLen((StringObject*) string);
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+    jsize len = dvmStringLen(strObj);
 
     JNI_EXIT();
     return len;
 }
 
+
 /*
- * Get a pointer to the string's character data.
+ * Get a string's character data.
  *
  * The result is guaranteed to be valid until ReleaseStringChars is
- * called, which means we can't just hold a reference to it in the local
- * refs table.  We have to add it to the global refs.
- *
- * Technically, we don't need to hold a reference to the String, but rather
- * to the Char[] object within the String.
- *
- * We could also just allocate some storage and copy the data into it,
- * but it's a choice between our synchronized global reference table and
- * libc's synchronized heap allocator.
+ * called, which means we have to pin it or return a copy.
  */
-static const jchar* GetStringChars(JNIEnv* env, jstring string,
-    jboolean* isCopy)
+static const jchar* GetStringChars(JNIEnv* env, jstring jstr, jboolean* isCopy)
 {
     JNI_ENTER();
 
-    const u2* data = dvmStringChars((StringObject*) string);
-    addGlobalReference(string);
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+    ArrayObject* strChars = dvmStringCharArray(jstr);
 
+    pinPrimitiveArray(strChars);
+
+    const u2* data = dvmStringChars(strObj);
     if (isCopy != NULL)
         *isCopy = JNI_FALSE;
 
@@ -2192,10 +2268,12 @@
 /*
  * Release our grip on some characters from a string.
  */
-static void ReleaseStringChars(JNIEnv* env, jstring string, const jchar* chars)
+static void ReleaseStringChars(JNIEnv* env, jstring jstr, const jchar* chars)
 {
     JNI_ENTER();
-    deleteGlobalReference(string);
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+    ArrayObject* strChars = dvmStringCharArray(jstr);
+    unpinPrimitiveArray(strChars);
     JNI_EXIT();
 }
 
@@ -2209,30 +2287,30 @@
 {
     JNI_ENTER();
 
-    StringObject* newStr;
-    
+    jstring result;
+
     if (bytes == NULL) {
-        newStr = NULL;
+        result = NULL;
     } else {
-        newStr = dvmCreateStringFromCstr(bytes, ALLOC_DEFAULT);
-        if (newStr != NULL) {
-            dvmReleaseTrackedAlloc((Object*)newStr, NULL);
-            newStr = addLocalReference((jstring) newStr);
-        }
+        /* note newStr could come back NULL on OOM */
+        StringObject* newStr = dvmCreateStringFromCstr(bytes, ALLOC_DEFAULT);
+        result = addLocalReference(env, (Object*) newStr);
+        dvmReleaseTrackedAlloc((Object*)newStr, NULL);
     }
 
     JNI_EXIT();
-    return (jstring)newStr;
+    return result;
 }
 
 /*
  * Return the length in bytes of the modified UTF-8 form of the string.
  */
-static jsize GetStringUTFLength(JNIEnv* env, jstring string)
+static jsize GetStringUTFLength(JNIEnv* env, jstring jstr)
 {
     JNI_ENTER();
 
-    jsize len = dvmStringUtf8ByteLen((StringObject*) string);
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+    jsize len = dvmStringUtf8ByteLen(strObj);
 
     JNI_EXIT();
     return len;
@@ -2252,20 +2330,21 @@
  * which should catch this sort of thing during development.)  Certain other
  * VMs will crash with a segmentation fault.
  */
-static const char* GetStringUTFChars(JNIEnv* env, jstring string,
+static const char* GetStringUTFChars(JNIEnv* env, jstring jstr,
     jboolean* isCopy)
 {
     JNI_ENTER();
     char* newStr;
 
-    if (string == NULL) {
+    if (jstr == NULL) {
         /* this shouldn't happen; throw NPE? */
         newStr = NULL;
     } else {
         if (isCopy != NULL)
             *isCopy = JNI_TRUE;
 
-        newStr = dvmCreateCstrFromString((StringObject*) string);
+        StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+        newStr = dvmCreateCstrFromString(strObj);
         if (newStr == NULL) {
             /* assume memory failure */
             dvmThrowException("Ljava/lang/OutOfMemoryError;",
@@ -2280,7 +2359,7 @@
 /*
  * Release a string created by GetStringUTFChars().
  */
-static void ReleaseStringUTFChars(JNIEnv* env, jstring string, const char* utf)
+static void ReleaseStringUTFChars(JNIEnv* env, jstring jstr, const char* utf)
 {
     JNI_ENTER();
     free((char*)utf);
@@ -2290,11 +2369,12 @@
 /*
  * Return the capacity of the array.
  */
-static jsize GetArrayLength(JNIEnv* env, jarray array)
+static jsize GetArrayLength(JNIEnv* env, jarray jarr)
 {
     JNI_ENTER();
 
-    jsize length = ((ArrayObject*) array)->length;
+    ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
+    jsize length = arrObj->length;
 
     JNI_EXIT();
     return length;
@@ -2304,12 +2384,13 @@
  * Construct a new array that holds objects from class "elementClass".
  */
 static jobjectArray NewObjectArray(JNIEnv* env, jsize length,
-    jclass elementClass, jobject initialElement)
+    jclass jelementClass, jobject jinitialElement)
 {
     JNI_ENTER();
 
-    ClassObject* elemClassObj = (ClassObject*) elementClass;
-    ArrayObject* newObj = NULL;
+    jobjectArray newArray = NULL;
+    ClassObject* elemClassObj =
+        (ClassObject*) dvmDecodeIndirectRef(env, jelementClass);
 
     if (elemClassObj == NULL) {
         dvmThrowException("Ljava/lang/NullPointerException;",
@@ -2317,28 +2398,30 @@
         goto bail;
     }
 
-    newObj = dvmAllocObjectArray(elemClassObj, length, ALLOC_DEFAULT);
+    ArrayObject* newObj =
+        dvmAllocObjectArray(elemClassObj, length, ALLOC_DEFAULT);
     if (newObj == NULL) {
         assert(dvmCheckException(_self));
         goto bail;
     }
+    newArray = addLocalReference(env, (Object*) newObj);
     dvmReleaseTrackedAlloc((Object*) newObj, NULL);
 
     /*
      * Initialize the array.  Trashes "length".
      */
-    if (initialElement != NULL) {
+    if (jinitialElement != NULL) {
+        Object* initialElement = dvmDecodeIndirectRef(env, jinitialElement);
         Object** arrayData = (Object**) newObj->contents;
 
         while (length--)
-            *arrayData++ = (Object*) initialElement;
+            *arrayData++ = initialElement;
     }
 
-    newObj = addLocalReference((jobjectArray) newObj);
 
 bail:
     JNI_EXIT();
-    return (jobjectArray) newObj;
+    return newArray;
 }
 
 /*
@@ -2346,15 +2429,15 @@
  *
  * Add the object to the local references table in case the array goes away.
  */
-static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray array,
+static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
     jsize index)
 {
     JNI_ENTER();
 
-    ArrayObject* arrayObj = (ArrayObject*) array;
-    Object* value = NULL;
+    ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
+    jobject retval = NULL;
 
-    assert(array != NULL);
+    assert(arrayObj != NULL);
 
     /* check the array bounds */
     if (index < 0 || index >= (int) arrayObj->length) {
@@ -2363,25 +2446,25 @@
         goto bail;
     }
 
-    value = ((Object**) arrayObj->contents)[index];
-    value = addLocalReference(value);
+    Object* value = ((Object**) arrayObj->contents)[index];
+    retval = addLocalReference(env, value);
 
 bail:
     JNI_EXIT();
-    return (jobject) value;
+    return retval;
 }
 
 /*
  * Set one element of an Object array.
  */
-static void SetObjectArrayElement(JNIEnv* env, jobjectArray array,
-    jsize index, jobject value)
+static void SetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
+    jsize index, jobject jobj)
 {
     JNI_ENTER();
 
-    ArrayObject* arrayObj = (ArrayObject*) array;
+    ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
 
-    assert(array != NULL);
+    assert(arrayObj != NULL);
 
     /* check the array bounds */
     if (index < 0 || index >= (int) arrayObj->length) {
@@ -2392,7 +2475,12 @@
 
     //LOGV("JNI: set element %d in array %p to %p\n", index, array, value);
 
-    ((Object**) arrayObj->contents)[index] = (Object*) value;
+    Object* obj;
+    if (jobj == NULL)
+        obj = NULL;
+    else
+        obj = dvmDecodeIndirectRef(env, jobj);
+    ((Object**) arrayObj->contents)[index] = obj;
 
 bail:
     JNI_EXIT();
@@ -2408,12 +2496,13 @@
         ArrayObject* arrayObj;                                              \
         arrayObj = dvmAllocPrimitiveArray(_typechar, length,                \
             ALLOC_DEFAULT);                                                 \
+        jarray jarr = NULL;                                                 \
         if (arrayObj != NULL) {                                             \
+            jarr = addLocalReference(env, (Object*) arrayObj);              \
             dvmReleaseTrackedAlloc((Object*) arrayObj, NULL);               \
-            arrayObj = addLocalReference(arrayObj);                         \
         }                                                                   \
         JNI_EXIT();                                                         \
-        return (_artype)arrayObj;                                           \
+        return (_artype)jarr;                                               \
     }
 NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean, 'Z');
 NEW_PRIMITIVE_ARRAY(jbyteArray, Byte, 'B');
@@ -2428,22 +2517,20 @@
  * Get a pointer to a C array of primitive elements from an array object
  * of the matching type.
  *
- * We guarantee availability until Release is called, so we have to add
- * the array object to the global refs table.
- *
- * In a compacting GC, we either need to return a copy of the elements
- * or "pin" the memory.  Otherwise we run the risk of native code using
- * the buffer as the destination of a blocking read() call that wakes up
+ * In a compacting GC, we either need to return a copy of the elements or
+ * "pin" the memory.  Otherwise we run the risk of native code using the
+ * buffer as the destination of e.g. a blocking read() call that wakes up
  * during a GC.
  */
 #define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname)                        \
     static _ctype* Get##_jname##ArrayElements(JNIEnv* env,                  \
-        _ctype##Array array, jboolean* isCopy)                              \
+        _ctype##Array jarr, jboolean* isCopy)                               \
     {                                                                       \
         JNI_ENTER();                                                        \
         _ctype* data;                                                       \
-        ArrayObject* arrayObj = (ArrayObject*)array;                        \
-        addGlobalReference(arrayObj);                                       \
+        ArrayObject* arrayObj =                                             \
+            (ArrayObject*) dvmDecodeIndirectRef(env, jarr);                 \
+        pinPrimitiveArray(arrayObj);                                        \
         data = (_ctype*) arrayObj->contents;                                \
         if (isCopy != NULL)                                                 \
             *isCopy = JNI_FALSE;                                            \
@@ -2454,21 +2541,21 @@
 /*
  * Release the storage locked down by the "get" function.
  *
- * The API says, ""'mode' has no effect if 'elems' is not a copy of the
+ * The spec says, "'mode' has no effect if 'elems' is not a copy of the
  * elements in 'array'."  They apparently did not anticipate the need to
- * create a global reference to avoid GC race conditions.  We actually
- * want to delete the global reference in all circumstances that would
- * result in a copied array being freed.  This means anything but a
- * JNI_COMMIT release.
+ * un-pin memory.
  */
 #define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname)                    \
     static void Release##_jname##ArrayElements(JNIEnv* env,                 \
-        _ctype##Array array, _ctype* elems, jint mode)                      \
+        _ctype##Array jarr, _ctype* elems, jint mode)                       \
     {                                                                       \
         UNUSED_PARAMETER(elems);                                            \
         JNI_ENTER();                                                        \
-        if (mode != JNI_COMMIT)                                             \
-            deleteGlobalReference(array);                                   \
+        if (mode != JNI_COMMIT) {                                           \
+            ArrayObject* arrayObj =                                         \
+                (ArrayObject*) dvmDecodeIndirectRef(env, jarr);             \
+            unpinPrimitiveArray(arrayObj);                                  \
+        }                                                                   \
         JNI_EXIT();                                                         \
     }
 
@@ -2477,10 +2564,11 @@
  */
 #define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname)                          \
     static void Get##_jname##ArrayRegion(JNIEnv* env,                       \
-        _ctype##Array array, jsize start, jsize len, _ctype* buf)           \
+        _ctype##Array jarr, jsize start, jsize len, _ctype* buf)            \
     {                                                                       \
         JNI_ENTER();                                                        \
-        ArrayObject* arrayObj = (ArrayObject*)array;                        \
+        ArrayObject* arrayObj =                                             \
+            (ArrayObject*) dvmDecodeIndirectRef(env, jarr);                 \
         _ctype* data = (_ctype*) arrayObj->contents;                        \
         if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
             dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
@@ -2496,10 +2584,11 @@
  */
 #define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname)                          \
     static void Set##_jname##ArrayRegion(JNIEnv* env,                       \
-        _ctype##Array array, jsize start, jsize len, const _ctype* buf)     \
+        _ctype##Array jarr, jsize start, jsize len, const _ctype* buf)      \
     {                                                                       \
         JNI_ENTER();                                                        \
-        ArrayObject* arrayObj = (ArrayObject*)array;                        \
+        ArrayObject* arrayObj =                                             \
+            (ArrayObject*) dvmDecodeIndirectRef(env, jarr);                 \
         _ctype* data = (_ctype*) arrayObj->contents;                        \
         if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
             dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
@@ -2535,22 +2624,23 @@
 /*
  * Register one or more native functions in one class.
  */
-static jint RegisterNatives(JNIEnv* env, jclass clazz,
+static jint RegisterNatives(JNIEnv* env, jclass jclazz,
     const JNINativeMethod* methods, jint nMethods)
 {
     JNI_ENTER();
 
+    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
     jint retval;
     int i;
 
     if (gDvm.verboseJni) {
         LOGI("[Registering JNI native methods for class %s]\n",
-            ((ClassObject*) clazz)->descriptor);
+            clazz->descriptor);
     }
 
     for (i = 0; i < nMethods; i++) {
-        if (!dvmRegisterJNIMethod((ClassObject*) clazz,
-                methods[i].name, methods[i].signature, methods[i].fnPtr))
+        if (!dvmRegisterJNIMethod(clazz, methods[i].name,
+                methods[i].signature, methods[i].fnPtr))
         {
             retval = JNI_ERR;
             goto bail;
@@ -2566,7 +2656,7 @@
 /*
  * Un-register a native function.
  */
-static jint UnregisterNatives(JNIEnv* env, jclass clazz)
+static jint UnregisterNatives(JNIEnv* env, jclass jclazz)
 {
     JNI_ENTER();
     /*
@@ -2585,11 +2675,12 @@
  * We have to track all monitor enters and exits, so that we can undo any
  * outstanding synchronization before the thread exits.
  */
-static jint MonitorEnter(JNIEnv* env, jobject obj)
+static jint MonitorEnter(JNIEnv* env, jobject jobj)
 {
     JNI_ENTER();
-    dvmLockObject(_self, (Object*) obj);
-    trackMonitorEnter(_self, (Object*) obj);
+    Object* obj = dvmDecodeIndirectRef(env, jobj);
+    dvmLockObject(_self, obj);
+    trackMonitorEnter(_self, obj);
     JNI_EXIT();
     return JNI_OK;
 }
@@ -2598,17 +2689,18 @@
  * Unlock the monitor.
  *
  * Throws an IllegalMonitorStateException if the current thread
- * doesn't own the monitor. (dvmUnlockObject() takes care of the throw.)
+ * doesn't own the monitor.  (dvmUnlockObject() takes care of the throw.)
  *
  * According to the 1.6 spec, it's legal to call here with an exception
  * pending.  If this fails, we'll stomp the original exception.
  */
-static jint MonitorExit(JNIEnv* env, jobject obj)
+static jint MonitorExit(JNIEnv* env, jobject jobj)
 {
     JNI_ENTER();
-    bool success = dvmUnlockObject(_self, (Object*) obj);
+    Object* obj = dvmDecodeIndirectRef(env, jobj);
+    bool success = dvmUnlockObject(_self, obj);
     if (success)
-        trackMonitorExit(_self, (Object*) obj);
+        trackMonitorExit(_self, obj);
     JNI_EXIT();
     return success ? JNI_OK : JNI_ERR;
 }
@@ -2631,11 +2723,11 @@
 /*
  * Copies "len" Unicode characters, from offset "start".
  */
-static void GetStringRegion(JNIEnv* env, jstring str, jsize start, jsize len,
+static void GetStringRegion(JNIEnv* env, jstring jstr, jsize start, jsize len,
     jchar* buf)
 {
     JNI_ENTER();
-    StringObject* strObj = (StringObject*) str;
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
     if (start + len > dvmStringLen(strObj))
         dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
     else
@@ -2647,11 +2739,11 @@
  * Translates "len" Unicode characters, from offset "start", into
  * modified UTF-8 encoding.
  */
-static void GetStringUTFRegion(JNIEnv* env, jstring str, jsize start,
+static void GetStringUTFRegion(JNIEnv* env, jstring jstr, jsize start,
     jsize len, char* buf)
 {
     JNI_ENTER();
-    StringObject* strObj = (StringObject*) str;
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
     if (start + len > dvmStringLen(strObj))
         dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
     else
@@ -2665,15 +2757,15 @@
  * The caller is expected to call "release" before doing any JNI calls
  * or blocking I/O operations.
  *
- * In a compacting GC, we need to pin the memory or block GC.
+ * We need to pin the memory or block GC.
  */
-static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray array,
+static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray jarr,
     jboolean* isCopy)
 {
     JNI_ENTER();
     void* data;
-    ArrayObject* arrayObj = (ArrayObject*)array;
-    addGlobalReference(arrayObj);
+    ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
+    pinPrimitiveArray(arrayObj);
     data = arrayObj->contents;
     if (isCopy != NULL)
         *isCopy = JNI_FALSE;
@@ -2684,25 +2776,30 @@
 /*
  * Release an array obtained with GetPrimitiveArrayCritical.
  */
-static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array,
+static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray jarr,
     void* carray, jint mode)
 {
     JNI_ENTER();
-    if (mode != JNI_COMMIT)
-        deleteGlobalReference(array);
+    if (mode != JNI_COMMIT) {
+        ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
+        unpinPrimitiveArray(arrayObj);
+    }
     JNI_EXIT();
 }
 
 /*
  * Like GetStringChars, but with restricted use.
  */
-static const jchar* GetStringCritical(JNIEnv* env, jstring string,
+static const jchar* GetStringCritical(JNIEnv* env, jstring jstr,
     jboolean* isCopy)
 {
     JNI_ENTER();
-    const u2* data = dvmStringChars((StringObject*) string);
-    addGlobalReference(string);
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+    ArrayObject* strChars = dvmStringCharArray(strObj);
 
+    pinPrimitiveArray(strChars);
+
+    const u2* data = dvmStringChars(strObj);
     if (isCopy != NULL)
         *isCopy = JNI_FALSE;
 
@@ -2713,11 +2810,13 @@
 /*
  * Like ReleaseStringChars, but with restricted use.
  */
-static void ReleaseStringCritical(JNIEnv* env, jstring string,
+static void ReleaseStringCritical(JNIEnv* env, jstring jstr,
     const jchar* carray)
 {
     JNI_ENTER();
-    deleteGlobalReference(string);
+    StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
+    ArrayObject* strChars = dvmStringCharArray(jstr);
+    unpinPrimitiveArray(strChars);
     JNI_EXIT();
 }
 
@@ -2768,15 +2867,15 @@
  * the same time, so while the return value is accurate it may not tell
  * the whole story.
  */
-static jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj)
+static jobjectRefType GetObjectRefType(JNIEnv* env, jobject jobj)
 {
     JNI_ENTER();
     jobjectRefType type;
 
-    if (obj == NULL)
+    if (jobj == NULL)
         type = JNIInvalidRefType;
     else
-        type = dvmGetJNIRefType(obj);
+        type = dvmGetJNIRefType(env, jobj);
     JNI_EXIT();
     return type;
 }
@@ -2815,15 +2914,16 @@
     Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
     if (newObj != NULL) {
         /* call the (PlatformAddress, int, int) constructor */
-        newObj = addLocalReference(newObj);
+        result = addLocalReference(env, newObj);
         dvmCallMethod(self, gDvm.methJavaNioReadWriteDirectByteBuffer_init,
             newObj, &callResult, platformAddress, (jint) capacity, (jint) 0);
-        if (dvmGetException(self) != NULL)
+        if (dvmGetException(self) != NULL) {
+            deleteLocalReference(env, result);
+            result = NULL;
             goto bail;
+        }
     }
 
-    result = (jobject) newObj;
-
 bail:
     if (platformAddress != NULL)
         dvmReleaseTrackedAlloc(platformAddress, self);
@@ -2836,11 +2936,11 @@
  *
  * If this is not a "direct" buffer, we return NULL.
  */
-static void* GetDirectBufferAddress(JNIEnv* env, jobject buf)
+static void* GetDirectBufferAddress(JNIEnv* env, jobject jbuf)
 {
     JNI_ENTER();
 
-    Object* bufObj = (Object*) buf;
+    Object* bufObj = dvmDecodeIndirectRef(env, jbuf);
     Thread* self = _self /*dvmThreadSelf()*/;
     void* result;
 
@@ -2911,7 +3011,7 @@
  * this check, since it's expensive to determine, and just return the
  * capacity regardless.)
  */
-static jlong GetDirectBufferCapacity(JNIEnv* env, jobject buf)
+static jlong GetDirectBufferCapacity(JNIEnv* env, jobject jbuf)
 {
     JNI_ENTER();
 
@@ -2921,7 +3021,8 @@
      * (The "check" version should verify that this is actually a Buffer,
      * but we're not required to do so here.)
      */
-    jlong result = dvmGetFieldInt((Object*)buf, gDvm.offJavaNioBuffer_capacity);
+    Object* buf = dvmDecodeIndirectRef(env, jbuf);
+    jlong result = dvmGetFieldInt(buf, gDvm.offJavaNioBuffer_capacity);
 
     JNI_EXIT();
     return result;
@@ -3472,7 +3573,7 @@
 {
     JNIEnvExt* extEnv;
     JavaVMExt* extVm;
-    
+
     extEnv = dvmGetJNIEnvForThread();
     if (extEnv == NULL) {
         LOGE("dvmLateEnableCheckedJni: thread has no JNIEnv\n");
diff --git a/vm/JniInternal.h b/vm/JniInternal.h
index df3ca54..cbe9d8a 100644
--- a/vm/JniInternal.h
+++ b/vm/JniInternal.h
@@ -146,10 +146,15 @@
 void dvmLateEnableCheckedJni(void);
 
 /*
+ * Decode a local, global, or weak-global reference.
+ */
+Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj);
+
+/*
  * Verify that a reference passed in from native code is valid.  Returns
  * an indication of local/global/invalid.
  */
-jobjectRefType dvmGetJNIRefType(Object* obj);
+jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj);
 
 /*
  * Get the last method called on the interp stack.  This is the method
diff --git a/vm/UtfString.c b/vm/UtfString.c
index a5e0b35..dfb76bc 100644
--- a/vm/UtfString.c
+++ b/vm/UtfString.c
@@ -435,6 +435,15 @@
 }
 
 /*
+ * Get the char[] object from the String.
+ */
+ArrayObject* dvmStringCharArray(StringObject* jstr)
+{
+    return (ArrayObject*) dvmGetFieldObject((Object*) jstr,
+                                gDvm.offJavaLangString_value);
+}
+
+/*
  * Get the string's data.
  */
 const u2* dvmStringChars(StringObject* jstr)
diff --git a/vm/UtfString.h b/vm/UtfString.h
index 5ca2ce6..ca500a7 100644
--- a/vm/UtfString.h
+++ b/vm/UtfString.h
@@ -100,6 +100,11 @@
 int dvmStringLen(StringObject* jstr);
 
 /*
+ * Get the char[] object from the String.
+ */
+ArrayObject* dvmStringCharArray(StringObject* jstr);
+
+/*
  * Get a pointer to the Unicode data.
  */
 const u2* dvmStringChars(StringObject* jstr);
diff --git a/vm/test/TestIndirectRefTable.c b/vm/test/TestIndirectRefTable.c
index 72f8f33..64d843c 100644
--- a/vm/test/TestIndirectRefTable.c
+++ b/vm/test/TestIndirectRefTable.c
@@ -39,7 +39,7 @@
     Object* obj1 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
     Object* obj2 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
     Object* obj3 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
-    const u4 cookie = IRT_SEGMENT_INIT;
+    const u4 cookie = IRT_FIRST_SEGMENT;
     bool result = false;
 
     if (!dvmInitIndirectRefTable(&irt, kTableMax/2, kTableMax,
@@ -329,7 +329,7 @@
     {
         return false;
     }
-    cookie = segmentState[0] = IRT_SEGMENT_INIT;
+    cookie = segmentState[0] = IRT_FIRST_SEGMENT;
     DBUG_MSG("+++ objs %p %p %p %p\n", obj0, obj1, obj2, obj3);
 
     /*