Linux hosted mode now keeps a map of DispatchObjects to the underlying 
jsval's.  Entries are removed when the jsval is garbage collected.  This 
change supports stable identity of Java objects in JavaScript.

Patch by: scottb, jat (pair prog)



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2277 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/linux/src/com/google/gwt/dev/shell/moz/JsValueMoz.java b/dev/linux/src/com/google/gwt/dev/shell/moz/JsValueMoz.java
index 36f0798..e9d7cb3 100644
--- a/dev/linux/src/com/google/gwt/dev/shell/moz/JsValueMoz.java
+++ b/dev/linux/src/com/google/gwt/dev/shell/moz/JsValueMoz.java
@@ -154,9 +154,9 @@
 
   protected static native int _getInt(int jsRootedValue);
 
-  protected static native double _getNumber(int jsRootedValue);
+  protected static native int _getJsval(int jsRootedValue);
 
-  protected static native int _getObjectPointer(int jsRootedValue);
+  protected static native double _getNumber(int jsRootedValue);
 
   protected static native String _getString(int jsRootedValue);
 
@@ -191,6 +191,8 @@
   protected static native void _setJsRootedValue(int jsRootedValue,
       int jsOtherRootedValue);
 
+  protected static native void _setJsval(int jsRootedValue, int jsval);
+
   protected static native void _setNull(int jsRootedValue);
 
   protected static native void _setString(int jsRootedValue, String val);
@@ -294,7 +296,8 @@
 
   @Override
   public int getJavaScriptObjectPointer() {
-    return _getObjectPointer(jsRootedValue);
+    assert isJavaScriptObject();
+    return _getJsval(jsRootedValue);
   }
 
   /**
@@ -555,7 +558,13 @@
     } else {
       dispObj = new GeckoDispatchAdapter(cl, val);
     }
-    _setWrappedJavaObject(jsRootedValue, dispObj);
+    Integer jsval = LowLevelMoz.sObjectToJsval.get(dispObj);
+    if (jsval != null) {
+      _setJsval(jsRootedValue, jsval);
+    } else {
+      _setWrappedJavaObject(jsRootedValue, dispObj);
+      LowLevelMoz.sObjectToJsval.put(dispObj, _getJsval(jsRootedValue));
+    }
   }
 
   /**
diff --git a/dev/linux/src/com/google/gwt/dev/shell/moz/LowLevelMoz.java b/dev/linux/src/com/google/gwt/dev/shell/moz/LowLevelMoz.java
index 716a39f..c06b166 100644
--- a/dev/linux/src/com/google/gwt/dev/shell/moz/LowLevelMoz.java
+++ b/dev/linux/src/com/google/gwt/dev/shell/moz/LowLevelMoz.java
@@ -17,6 +17,9 @@
 
 import com.google.gwt.dev.shell.LowLevel;
 
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Map;
 import java.util.Vector;
 
 /**
@@ -76,6 +79,12 @@
     boolean gwtOnLoad(int scriptGlobalObject, String moduleName);
   }
 
+  /**
+   * Stores a map from DispatchObject/DispatchMethod to the live underlying jsval.  This is used to
+   * both preserve identity for the same Java Object and also prevent GC.
+   */
+  static Map<Object, Integer> sObjectToJsval = Collections.synchronizedMap(new IdentityHashMap<Object, Integer>());
+
   private static Vector<ExternalFactory> sExternalFactories = new Vector<ExternalFactory>();
   private static boolean sInitialized = false;
 
@@ -186,6 +195,13 @@
     System.out.flush();
   }
 
+  /**
+   * Native code accessor to remove the mapping upon GC.
+   */
+  static void removeJsvalForObject(Object o) {
+    sObjectToJsval.remove(o);
+  }
+
   // CHECKSTYLE_NAMING_OFF: Non JSNI native code may have leading '_'s.
 
   private static native boolean _executeScriptWithInfo(int scriptObject,
@@ -220,7 +236,8 @@
    * @param jsthis the JS object with the named method
    * @param jsargs an array of arguments to the method
    */
-  @SuppressWarnings("unused") // kept for future debugging purposes
+  @SuppressWarnings("unused")
+  // kept for future debugging purposes
   private static void printInvocationParams(String methodName,
       JsValueMoz jsthis, JsValueMoz[] jsargs) {
     System.out.println("LowLevelMoz.invoke:");
diff --git a/jni/linux/JsValueMoz.cpp b/jni/linux/JsValueMoz.cpp
index 1faa49f..195306c 100644
--- a/jni/linux/JsValueMoz.cpp
+++ b/jni/linux/JsValueMoz.cpp
@@ -382,18 +382,17 @@
 
 /**
  * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
- * Method:    _getObjectPointer()
+ * Method:    _getJsval()
  * Signature: (I)I
  */
 extern "C" JNIEXPORT jint JNICALL
-Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getObjectPointer
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getJsval
   (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
 {
   JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
       (jsRootedValueInt);
   Tracer tracer("JsValueMoz._getObjectPointer", jsRootedValue);
-  JSObject* ptr = jsRootedValue->getObject();
-  int val = reinterpret_cast<int>(ptr);
+  int val = jsRootedValue->getValue();
   tracer.log("value=%d", val);
   return val;
 }
@@ -733,6 +732,23 @@
 }
 
 /*
+ * Set the Javascript value to a specific jsval.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _setJsval()
+ * Signature: (II)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setJsval
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jint jsval)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._setJsval", jsRootedValue);
+  jsRootedValue->setValue(jsval);
+}
+
+/*
  * Set the JavaScript value to be null.
  * 
  * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
@@ -821,7 +837,7 @@
   jsRootedValue->setObject(newObj); 
   tracer.log("jsobject=%08x", unsigned(newObj));
   
-  // TODO(jat): how does this globalref get freed?
+  // This is collected when the gwt_nativewrapper_class destructor runs.
   jobject dispObjRef = jniEnv->NewGlobalRef(obj);
   if (!dispObjRef || jniEnv->ExceptionCheck()) {
     tracer.throwHostedModeException(jniEnv,
diff --git a/jni/linux/NativeWrapper.cpp b/jni/linux/NativeWrapper.cpp
index be3963e..c017fd7 100644
--- a/jni/linux/NativeWrapper.cpp
+++ b/jni/linux/NativeWrapper.cpp
@@ -158,9 +158,23 @@
 static void JS_DLL_CALLBACK gwt_nativewrapper_finalize(JSContext *cx,
     JSObject *obj)
 {
+  Tracer tracer("gwt_nativewrapper_finalize");
   jobject dispObj = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
-  if (dispObj)
+  if (dispObj) {
+    // Remove this pairing from the global map.
+    jmethodID removeMethod = savedJNIEnv->GetStaticMethodID(lowLevelMozClass, "removeJsvalForObject",
+      "(Ljava/lang/Object;)V");
+    if (!removeMethod || savedJNIEnv->ExceptionCheck()) {
+      tracer.setFail("Cannot GetMethodID for removeJsvalForObject");
+      return;
+    }
+    savedJNIEnv->CallStaticVoidMethod(lowLevelMozClass, removeMethod, dispObj);
+    if (savedJNIEnv->ExceptionCheck()) {
+      tracer.setFail("Exception calling removeJsvalForObject");
+      return;
+    }
     savedJNIEnv->DeleteGlobalRef(dispObj);
+  }
 }
 
 static JSBool JS_DLL_CALLBACK gwt_nativewrapper_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
diff --git a/jni/linux/prebuilt/libgwt-ll.so b/jni/linux/prebuilt/libgwt-ll.so
index e941084..33af9c7 100755
--- a/jni/linux/prebuilt/libgwt-ll.so
+++ b/jni/linux/prebuilt/libgwt-ll.so
Binary files differ