MacOSX hosted mode now keeps a map of DispatchObjects and DispatchMethods to
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
Review by: knorton
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2281 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/mac/src/com/google/gwt/dev/shell/mac/LowLevelSaf.java b/dev/mac/src/com/google/gwt/dev/shell/mac/LowLevelSaf.java
index 82825ae..426b2d9 100644
--- a/dev/mac/src/com/google/gwt/dev/shell/mac/LowLevelSaf.java
+++ b/dev/mac/src/com/google/gwt/dev/shell/mac/LowLevelSaf.java
@@ -17,6 +17,8 @@
import com.google.gwt.dev.shell.LowLevel;
+import java.util.IdentityHashMap;
+import java.util.Map;
import java.util.Stack;
/**
@@ -50,12 +52,19 @@
void setField(int jsContext, String name, int value);
}
- private static boolean jsValueProtectionCheckingEnabled;
+ /**
+ * 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 = new IdentityHashMap<Object, Integer>();
private static boolean initialized = false;
private static final ThreadLocal<Stack<Integer>> jsContextStack = new ThreadLocal<Stack<Integer>>();
+ private static boolean jsValueProtectionCheckingEnabled;
+
public static int executeScript(int jsContext, String script) {
final int[] rval = new int[1];
if (!executeScriptWithInfoImpl(jsContext, script, null, 0, rval)) {
@@ -119,7 +128,7 @@
}
LowLevel.init();
- if (!initImpl(DispatchObject.class, DispatchMethod.class)) {
+ if (!initImpl(DispatchObject.class, DispatchMethod.class, LowLevelSaf.class)) {
throw new RuntimeException("Unable to initialize LowLevelSaf");
}
@@ -271,19 +280,39 @@
public static int wrapDispatchMethod(int jsContext, String name,
DispatchMethod dispatch) {
- final int[] rval = new int[1];
- if (!wrapDispatchMethodImpl(jsContext, name, dispatch, rval)) {
- throw new RuntimeException("Failed to wrap DispatchMethod.");
+ Integer cached = LowLevelSaf.sObjectToJsval.get(dispatch);
+ if (cached != null) {
+ /*
+ * Add another lock to the cached jsval, since it will not have any.
+ */
+ LowLevelSaf.gcProtect(LowLevelSaf.getCurrentJsContext(), cached);
+ return cached;
+ } else {
+ final int[] rval = new int[1];
+ if (!wrapDispatchMethodImpl(jsContext, name, dispatch, rval)) {
+ throw new RuntimeException("Failed to wrap DispatchMethod.");
+ }
+ LowLevelSaf.sObjectToJsval.put(dispatch, rval[0]);
+ return rval[0];
}
- return rval[0];
}
public static int wrapDispatchObject(int jsContext, DispatchObject dispatcher) {
- final int[] rval = new int[1];
- if (!wrapDispatchObjectImpl(jsContext, dispatcher, rval)) {
- throw new RuntimeException("Failed to wrap DispatchObject.");
+ Integer cached = LowLevelSaf.sObjectToJsval.get(dispatcher);
+ if (cached != null) {
+ /*
+ * Add another lock to the cached jsval, since it will not have any.
+ */
+ LowLevelSaf.gcProtect(LowLevelSaf.getCurrentJsContext(), cached);
+ return cached;
+ } else {
+ final int[] rval = new int[1];
+ if (!wrapDispatchObjectImpl(jsContext, dispatcher, rval)) {
+ throw new RuntimeException("Failed to wrap DispatchObject.");
+ }
+ LowLevelSaf.sObjectToJsval.put(dispatcher, rval[0]);
+ return rval[0];
}
- return rval[0];
}
static native boolean isGcProtected(int jsValue);
@@ -299,6 +328,13 @@
return jsValueProtectionCheckingEnabled;
}
+ /**
+ * Native code accessor to remove the mapping upon GC.
+ */
+ static void releaseObject(Object o) {
+ sObjectToJsval.remove(o);
+ }
+
private static native boolean executeScriptWithInfoImpl(int jsContext,
String script, String url, int line, int[] rval);
@@ -306,7 +342,8 @@
private static native boolean initImpl(
Class<DispatchObject> dispatchObjectClass,
- Class<DispatchMethod> dispatchMethodClass);
+ Class<DispatchMethod> dispatchMethodClass,
+ Class<LowLevelSaf> lowLevelSafClass);
private static native boolean invokeImpl(int jsContext, int jsScriptObject,
String methodName, int thisObj, int[] args, int argsLength, int[] rval);
diff --git a/jni/mac/gwt-webkit.cpp b/jni/mac/gwt-webkit.cpp
index 293a2bc..477b312 100644
--- a/jni/mac/gwt-webkit.cpp
+++ b/jni/mac/gwt-webkit.cpp
@@ -677,10 +677,10 @@
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_initImpl
(JNIEnv *env, jclass klass, jclass dispatchObjectClass,
- jclass dispatchMethodClass) {
+ jclass dispatchMethodClass, jclass lowLevelSafClass) {
TR_ENTER();
TR_LEAVE();
- return static_cast<jboolean>(gwt::Initialize(env, dispatchObjectClass, dispatchMethodClass));
+ return static_cast<jboolean>(gwt::Initialize(env, dispatchObjectClass, dispatchMethodClass, lowLevelSafClass));
}
/*
diff --git a/jni/mac/java-dispatch.cpp b/jni/mac/java-dispatch.cpp
index 60ec10a..9cdef44 100644
--- a/jni/mac/java-dispatch.cpp
+++ b/jni/mac/java-dispatch.cpp
@@ -54,6 +54,12 @@
void DispatchMethodFinalize(JSObjectRef);
/*
+ * Call this when an underlying Java Object should be freed.
+ */
+ void ReleaseJavaObject(jobject jObject);
+
+
+ /*
* The class definition stuct for DispatchObjects.
*/
static JSClassDefinition _dispatchObjectClassDef = { 0,
@@ -85,10 +91,12 @@
static JNIEnv* _javaEnv;
static jclass _javaDispatchObjectClass;
static jclass _javaDispatchMethodClass;
+ static jclass _lowLevelSafClass;
static jmethodID _javaDispatchObjectSetFieldMethod;
static jmethodID _javaDispatchObjectGetFieldMethod;
static jmethodID _javaDispatchMethodInvokeMethod;
static jmethodID _javaDispatchObjectToStringMethod;
+ static jmethodID _lowLevelSafReleaseObject;
/*
* Structure to hold DispatchMethod private data.
@@ -100,7 +108,7 @@
DispatchMethodData(jobject jObject, std::string& utf8Name)
: _jObject(jObject), _utf8Name(utf8Name) { }
~DispatchMethodData() {
- _javaEnv->DeleteGlobalRef(_jObject);
+ ReleaseJavaObject(_jObject);
}
jobject _jObject;
std::string _utf8Name;
@@ -293,7 +301,7 @@
void DispatchObjectFinalize(JSObjectRef jsObject) {
TR_ENTER();
jobject jObject = reinterpret_cast<jobject>(JSObjectGetPrivate(jsObject));
- _javaEnv->DeleteGlobalRef(jObject);
+ ReleaseJavaObject(jObject);
TR_LEAVE();
}
@@ -445,9 +453,10 @@
*
*/
bool Initialize(JNIEnv* javaEnv, jclass javaDispatchObjectClass,
- jclass javaDispatchMethodClass) {
+ jclass javaDispatchMethodClass, jclass lowLevelSafClass) {
TR_ENTER();
- if (!javaEnv || !javaDispatchObjectClass || !javaDispatchMethodClass) {
+ if (!javaEnv || !javaDispatchObjectClass || !javaDispatchMethodClass
+ || !lowLevelSafClass) {
return false;
}
@@ -456,6 +465,8 @@
javaEnv->NewGlobalRef(javaDispatchObjectClass));
_javaDispatchMethodClass = static_cast<jclass>(
javaEnv->NewGlobalRef(javaDispatchMethodClass));
+ _lowLevelSafClass = static_cast<jclass>(
+ javaEnv->NewGlobalRef(lowLevelSafClass));
_javaDispatchObjectSetFieldMethod = javaEnv->GetMethodID(
javaDispatchObjectClass, "setField", "(ILjava/lang/String;I)V");
_javaDispatchObjectGetFieldMethod = javaEnv->GetMethodID(
@@ -464,10 +475,12 @@
javaDispatchMethodClass, "invoke", "(II[I[I)I");
_javaDispatchObjectToStringMethod = javaEnv->GetMethodID(
javaDispatchObjectClass, "toString", "()Ljava/lang/String;");
+ _lowLevelSafReleaseObject = javaEnv->GetStaticMethodID(
+ lowLevelSafClass, "releaseObject", "(Ljava/lang/Object;)V");
if (!_javaDispatchObjectSetFieldMethod || !_javaDispatchObjectGetFieldMethod
|| !_javaDispatchMethodInvokeMethod || !_javaDispatchObjectToStringMethod
- || javaEnv->ExceptionCheck()) {
+ || !_lowLevelSafReleaseObject || javaEnv->ExceptionCheck()) {
return false;
}
@@ -475,4 +488,15 @@
return true;
}
+void ReleaseJavaObject(jobject jObject) {
+ // Tell the Java code we're done with this object.
+ _javaEnv->CallStaticVoidMethod(_lowLevelSafClass, _lowLevelSafReleaseObject,
+ jObject);
+ if (_javaEnv->ExceptionCheck()) {
+ TR_FAIL();
+ return;
+ }
+ _javaEnv->DeleteGlobalRef(jObject);
+}
+
} // namespace gwt
diff --git a/jni/mac/java-dispatch.h b/jni/mac/java-dispatch.h
index 4ff15c9..f8fa80b 100644
--- a/jni/mac/java-dispatch.h
+++ b/jni/mac/java-dispatch.h
@@ -23,11 +23,11 @@
namespace gwt {
/*
- * Initializes static members needed by DispatchObjects and DispatchMethods.
- * This should be called before before calling either DispatchMethodCreate or
- * DispatchObjectCreate.
+ * Initializes static members needed by DispatchObject, DispatchMethod,
+ * and LowLevelSaf. This should be called before before calling anything
+ * else.
*/
-bool Initialize(JNIEnv*, jclass, jclass);
+bool Initialize(JNIEnv*, jclass, jclass, jclass);
/*
* Returns a shared reference to the DispatchObject class
diff --git a/jni/mac/prebuilt/libgwt-ll.jnilib b/jni/mac/prebuilt/libgwt-ll.jnilib
index 4f62d3d..c9db8c5 100755
--- a/jni/mac/prebuilt/libgwt-ll.jnilib
+++ b/jni/mac/prebuilt/libgwt-ll.jnilib
Binary files differ