Implement JNI "weak global" references.

The VM now supports the NewWeakGlobalRef and DeleteWeakGlobalRef calls,
which create a kind of weak reference that's directly visible to native
code.  While the JNI spec says that these can be used directly, the only
safe way to use them is to convert them to a strong local or global
reference first, so this is enforced.

The net result is very similar to manually creating a global reference to
a WeakReference object and manipulating it with method calls from native
code, but the JNI calls are faster and more convenient.
diff --git a/docs/jni-tips.html b/docs/jni-tips.html
index 56136e8..6d7fa55 100644
--- a/docs/jni-tips.html
+++ b/docs/jni-tips.html
@@ -528,10 +528,11 @@
     Java bytecodes or class files, so passing in binary class data
     doesn't work.  Translation facilities may be added in a future
     version of the VM.</li>
-    <li><code>NewWeakGlobalRef</code> and <code>DeleteWeakGlobalRef</code>
-    are not implemented.  The
-    VM supports weak references, but not JNI "weak global" references.
-    These will be supported in a future release.</li>
+    <li>"Weak global" references are implemented, but may only be passed
+    to <code>NewLocalRef</code>, <code>NewGlobalRef</code>, and
+    <code>DeleteWeakGlobalRef</code>.  (The spec strongly encourages
+    programmers to create hard references to weak globals before doing
+    anything with them, so this should not be at all limiting.)</li>
     <li><code>GetObjectRefType</code> (new in 1.6) is implemented but not fully
     functional -- it can't always tell the difference between "local" and
     "global" references.</li>
diff --git a/vm/CheckJni.c b/vm/CheckJni.c
index bc90527..71073f0 100644
--- a/vm/CheckJni.c
+++ b/vm/CheckJni.c
@@ -472,6 +472,14 @@
 
     JNI_ENTER();
 
+    if (dvmIsWeakGlobalRef(jobj)) {
+        /*
+         * Normalize and continue.  This will tell us if the PhantomReference
+         * object is valid.
+         */
+        jobj = dvmNormalizeWeakGlobalRef((jweak) jobj);
+    }
+
     if (dvmGetJNIRefType(env, jobj) == JNIInvalidRefType) {
         LOGW("JNI WARNING: %p is not a valid JNI reference\n", jobj);
         printWarn = true;
diff --git a/vm/Globals.h b/vm/Globals.h
index 23b7497..5dd056f 100644
--- a/vm/Globals.h
+++ b/vm/Globals.h
@@ -187,6 +187,7 @@
     ClassObject* classJavaLangReflectMethodArray;
     ClassObject* classJavaLangReflectProxy;
     ClassObject* classJavaLangExceptionInInitializerError;
+    ClassObject* classJavaLangRefPhantomReference;
     ClassObject* classJavaLangRefReference;
     ClassObject* classJavaNioReadWriteDirectByteBuffer;
     ClassObject* classJavaSecurityAccessController;
@@ -279,6 +280,7 @@
     /* constructor method pointers; no vtable involved, so use Method* */
     Method*     methJavaLangStackTraceElement_init;
     Method*     methJavaLangExceptionInInitializerError_init;
+    Method*     methJavaLangRefPhantomReference_init;
     Method*     methJavaLangReflectConstructor_init;
     Method*     methJavaLangReflectField_init;
     Method*     methJavaLangReflectMethod_init;
@@ -425,6 +427,9 @@
     ReferenceTable  jniPinRefTable;
     pthread_mutex_t jniPinRefLock;
 
+    /* special ReferenceQueue for JNI weak globals */
+    Object*     jniWeakGlobalRefQueue;
+
     /*
      * Native shared library table.
      */
diff --git a/vm/Jni.c b/vm/Jni.c
index d7d1122..54a1f45 100644
--- a/vm/Jni.c
+++ b/vm/Jni.c
@@ -318,12 +318,30 @@
 
     dvmInitMutex(&gDvm.jniPinRefLock);
 
+    Method* meth;
+
+    /*
+     * Grab the PhantomReference constructor.
+     */
+    gDvm.classJavaLangRefPhantomReference =
+        dvmFindSystemClassNoInit("Ljava/lang/ref/PhantomReference;");
+    if (gDvm.classJavaLangRefPhantomReference == NULL) {
+        LOGE("Unable to find PhantomReference class\n");
+        return false;
+    }
+    meth= dvmFindDirectMethodByDescriptor(gDvm.classJavaLangRefPhantomReference,
+        "<init>", "(Ljava/lang/Object;Ljava/lang/ref/ReferenceQueue;)V");
+    if (meth == NULL) {
+        LOGE("Unable to find constructor for PhantomReference\n");
+        return false;
+    }
+    gDvm.methJavaLangRefPhantomReference_init = meth;
+
+
     /*
      * Look up and cache pointers to some direct buffer classes, fields,
      * and methods.
      */
-    Method* meth;
-
     ClassObject* platformAddressClass =
         dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddress;");
     ClassObject* platformAddressFactoryClass =
@@ -573,6 +591,7 @@
         break;
     case kIndirectKindWeakGlobal:
         {
+            // TODO: implement
             LOGE("weak-global not yet supported\n");
             result = NULL;
             dvmAbort();
@@ -898,6 +917,143 @@
     dvmUnlockMutex(&gDvm.jniGlobalRefLock);
 }
 
+
+/*
+ * Get the "magic" JNI weak global ReferenceQueue.  It's allocated on
+ * first use.
+ *
+ * Returns NULL with an exception raised if allocation fails.
+ */
+static Object* getWeakGlobalRefQueue(void)
+{
+    /* use an indirect variable to avoid "type-punned pointer" complaints */
+    Object** pGlobalQ = &gDvm.jniWeakGlobalRefQueue;
+
+    if (*pGlobalQ != NULL)
+        return *pGlobalQ;
+
+    ClassObject* clazz = dvmFindSystemClass("Ljava/lang/ref/ReferenceQueue;");
+    if (clazz == NULL) {
+        LOGE("Unable to find java.lang.ref.ReferenceQueue");
+        dvmAbort();
+    }
+
+    /*
+     * Create an instance of ReferenceQueue.  The object is never actually
+     * used for anything, so we don't need to call a constructor.  (We could
+     * get away with using an instance of Object, but this is cleaner.)
+     */
+    Object* queue = dvmAllocObject(clazz, ALLOC_DEFAULT);
+    if (queue == NULL) {
+        LOGW("Failed allocating weak global ref queue\n");
+        assert(dvmCheckException(dvmThreadSelf()));
+        return NULL;
+    }
+    dvmReleaseTrackedAlloc(queue, NULL);
+
+    /*
+     * Save it, using atomic ops to ensure we don't double-up.  The gDvm
+     * field is known to the GC.
+     */
+    if (!ATOMIC_CMP_SWAP((int*) pGlobalQ, 0, (int) queue)) {
+        LOGD("WOW: lost race to create weak global ref queue\n");
+        queue = *pGlobalQ;
+    }
+
+    return queue;
+}
+
+
+/*
+ * We create a PhantomReference that references the object, add a
+ * global reference to it, and then flip some bits before returning it.
+ * The last step ensures that we detect it as special and that only
+ * appropriate calls will accept it.
+ *
+ * On failure, returns NULL with an exception pending.
+ */
+static jweak createWeakGlobalRef(JNIEnv* env, jobject jobj)
+{
+    if (jobj == NULL)
+        return NULL;
+
+    Thread* self = ((JNIEnvExt*)env)->self;
+    Object* obj = dvmDecodeIndirectRef(env, jobj);
+    Object* weakGlobalQueue = getWeakGlobalRefQueue();
+    Object* phantomObj;
+    jobject phantomRef;
+
+    /*
+     * Allocate a PhantomReference, then call the constructor to set
+     * the referent and the reference queue.
+     *
+     * We use a "magic" reference queue that the GC knows about; it behaves
+     * more like a queueless WeakReference, clearing the referent and
+     * not calling enqueue().
+     */
+    if (!dvmIsClassInitialized(gDvm.classJavaLangRefPhantomReference))
+        dvmInitClass(gDvm.classJavaLangRefPhantomReference);
+    phantomObj = dvmAllocObject(gDvm.classJavaLangRefPhantomReference,
+            ALLOC_DEFAULT);
+    if (phantomObj == NULL) {
+        assert(dvmCheckException(self));
+        LOGW("Failed on WeakGlobalRef alloc\n");
+        return NULL;
+    }
+
+    JValue unused;
+    dvmCallMethod(self, gDvm.methJavaLangRefPhantomReference_init, phantomObj,
+        &unused, jobj, weakGlobalQueue);
+    dvmReleaseTrackedAlloc(phantomObj, self);
+
+    if (dvmCheckException(self)) {
+        LOGW("PhantomReference init failed\n");
+        return NULL;
+    }
+
+    LOGV("+++ WGR: created phantom ref %p for object %p\n", phantomObj, obj);
+
+    /*
+     * Add it to the global reference table, and mangle the pointer.
+     */
+    phantomRef = addGlobalReference(phantomObj);
+    return dvmObfuscateWeakGlobalRef(phantomRef);
+}
+
+/*
+ * Delete the global reference that's keeping the PhantomReference around.
+ * The PhantomReference will eventually be discarded by the GC.
+ */
+static void deleteWeakGlobalRef(JNIEnv* env, jweak wref)
+{
+    if (wref == NULL)
+        return;
+
+    jobject phantomRef = dvmNormalizeWeakGlobalRef(wref);
+    deleteGlobalReference(phantomRef);
+}
+
+/*
+ * Extract the referent from a PhantomReference.  Used for weak global
+ * references.
+ *
+ * "jwobj" is a "mangled" WGR pointer.
+ */
+static Object* getPhantomReferent(JNIEnv* env, jweak jwobj)
+{
+    jobject jobj = dvmNormalizeWeakGlobalRef(jwobj);
+    Object* obj = dvmDecodeIndirectRef(env, jobj);
+
+    if (obj->clazz != gDvm.classJavaLangRefPhantomReference) {
+        LOGE("%p is not a phantom reference (%s)\n",
+            jwobj, obj->clazz->descriptor);
+        return NULL;
+    }
+
+    return dvmGetFieldObject(obj, gDvm.offJavaLangRefReference_referent);
+}
+
+
 /*
  * Objects don't currently move, so we just need to create a reference
  * that will ensure the array object isn't collected.
@@ -1121,6 +1277,10 @@
     //Object** top;
     Object** ptr;
 
+    if (dvmIsWeakGlobalRef(jobj)) {
+        return JNIWeakGlobalRefType;
+    }
+
     /* check args */
     if (findInArgList(self, jobj)) {
         //LOGI("--- REF found %p on stack\n", jobj);
@@ -2003,8 +2163,13 @@
  */
 static jobject NewGlobalRef(JNIEnv* env, jobject jobj)
 {
+    Object* obj;
+
     JNI_ENTER();
-    Object* obj = dvmDecodeIndirectRef(env, jobj);
+    if (dvmIsWeakGlobalRef(jobj))
+        obj = getPhantomReferent(env, (jweak) jobj);
+    else
+        obj = dvmDecodeIndirectRef(env, jobj);
     jobject retval = addGlobalReference(obj);
     JNI_EXIT();
     return retval;
@@ -2024,10 +2189,15 @@
 /*
  * Add a reference to the local list.
  */
-static jobject NewLocalRef(JNIEnv* env, jobject jref)
+static jobject NewLocalRef(JNIEnv* env, jobject jobj)
 {
+    Object* obj;
+
     JNI_ENTER();
-    Object* obj = dvmDecodeIndirectRef(env, jref);
+    if (dvmIsWeakGlobalRef(jobj))
+        obj = getPhantomReferent(env, (jweak) jobj);
+    else
+        obj = dvmDecodeIndirectRef(env, jobj);
     jobject retval = addLocalReference(env, obj);
     JNI_EXIT();
     return retval;
@@ -3295,23 +3465,18 @@
 static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj)
 {
     JNI_ENTER();
-    // TODO - implement
-    jobject gref = NULL;
-    LOGE("JNI ERROR: NewWeakGlobalRef not implemented\n");
-    dvmAbort();
+    jweak wref = createWeakGlobalRef(env, obj);
     JNI_EXIT();
-    return gref;
+    return wref;
 }
 
 /*
  * Delete the specified weak global reference.
  */
-static void DeleteWeakGlobalRef(JNIEnv* env, jweak obj)
+static void DeleteWeakGlobalRef(JNIEnv* env, jweak wref)
 {
     JNI_ENTER();
-    // TODO - implement
-    LOGE("JNI ERROR: DeleteWeakGlobalRef not implemented\n");
-    dvmAbort();
+    deleteWeakGlobalRef(env, wref);
     JNI_EXIT();
 }
 
diff --git a/vm/JniInternal.h b/vm/JniInternal.h
index 37920ca..3c15ce4 100644
--- a/vm/JniInternal.h
+++ b/vm/JniInternal.h
@@ -201,4 +201,42 @@
  */
 void dvmReleaseJniMonitors(Thread* self);
 
+
+/*
+ * This mask is applied to weak global reference values returned to
+ * native code.  The goal is to create an invalid pointer that will cause
+ * a crash if misused.  The mmap region for the virtual heap is typically
+ * around 0x40xxxxxx.
+ *
+ * To make weak global references easily distinguishable from other kinds
+ * of references when !USE_INDIRECT_REF, we XOR the low bits.  Assuming >=
+ * 64-bit alignment of objects, this changes the low 3 bits from all clear
+ * to all set.
+ */
+#define WEAK_GLOBAL_XOR 0x9e0fffff
+
+/*
+ * "Obfuscate" a weak global reference pointer.
+ */
+INLINE jweak dvmObfuscateWeakGlobalRef(jobject jobj) {
+    return (jweak) ((u4) jobj ^ WEAK_GLOBAL_XOR);
+}
+
+/*
+ * Undo the obfuscation.
+ */
+INLINE jobject dvmNormalizeWeakGlobalRef(jweak ref) {
+    return (jobject) ((u4) ref ^ WEAK_GLOBAL_XOR);
+}
+
+/*
+ * Returns "true" if this looks like a weak global reference.
+ *
+ * Relies on the low 3 bits being set instead of clear (the latter is
+ * guaranteed by 64-bit alignment of objects).
+ */
+INLINE bool dvmIsWeakGlobalRef(jobject jobj) {
+    return (((u4) jobj & 0x07) == 0x07);
+}
+
 #endif /*_DALVIK_JNIINTERNAL*/
diff --git a/vm/Native.c b/vm/Native.c
index 31832c2..6c14c1c 100644
--- a/vm/Native.c
+++ b/vm/Native.c
@@ -746,7 +746,7 @@
     int len;
 
     if (meth->clazz->classLoader != pLib->classLoader) {
-        LOGD("+++ not scanning '%s' for '%s' (wrong CL)\n",
+        LOGV("+++ not scanning '%s' for '%s' (wrong CL)\n",
             pLib->pathName, meth->name);
         return 0;
     } else
diff --git a/vm/alloc/MarkSweep.c b/vm/alloc/MarkSweep.c
index 4bb917b..634cfda 100644
--- a/vm/alloc/MarkSweep.c
+++ b/vm/alloc/MarkSweep.c
@@ -349,6 +349,7 @@
     dvmMarkObjectNonNull(gDvm.outOfMemoryObj);
     dvmMarkObjectNonNull(gDvm.internalErrorObj);
     dvmMarkObjectNonNull(gDvm.noClassDefFoundErrorObj);
+    dvmMarkObject(gDvm.jniWeakGlobalRefQueue);
 //TODO: scan object references sitting in gDvm;  use pointer begin & end
 
     HPROF_CLEAR_GC_SCAN_STATE();
@@ -861,7 +862,21 @@
                  * (The referent will be marked outside of this loop,
                  * after handing all references of this strength, in
                  * case multiple references point to the same object.)
+                 *
+                 * One exception: JNI "weak global" references are handled
+                 * as a special case.  They're identified by the queue.
                  */
+                if (gDvm.jniWeakGlobalRefQueue != NULL) {
+                    Object* queue = dvmGetFieldObject(reference,
+                            gDvm.offJavaLangRefReference_queue);
+                    if (queue == gDvm.jniWeakGlobalRefQueue) {
+                        LOGV("+++ WGR: clearing + not queueing %p:%p\n",
+                            reference, referent);
+                        schedClear = clearReference(reference);
+                        schedEnqueue = false;
+                        break;
+                    }
+                }
                 schedClear = false;
 
                 /* A PhantomReference is only useful with a
@@ -926,6 +941,9 @@
     /* Walk though the reference list again, and mark any non-clear/marked
      * referents.  Only PhantomReferences can have non-clear referents
      * at this point.
+     *
+     * (Could skip this for JNI weak globals, since we know they've been
+     * cleared.)
      */
     if (refType == REF_PHANTOM) {
         bool scanRequired = false;