Fixes issue #1281; linux hosted mode was crashing sometimes during
JS_SetReservedSlot(). There might have also been an unrelated issue with
storing stale JSContext*'s inside of JsRootedValues.
- Keeps a static stack of JSContext*'s instead of storing them; this is
safe to do because the JS engine is always higher on the call stack than
any client-side Java code (including gwtOnLoad).
- Protects newborns that could sometimes be insta-gc'd within
JsValueMoz._setWrappedJavaObject() and _setWrappedFunction().
- Reorganized some functions into more appropriate cpp files.
Patch by: jat, scottb
Review by: scottb, jat :)
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1268 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 81e9735..01f80d8 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
@@ -129,7 +129,7 @@
}
public void doCleanup() {
- JsValueMoz.destroyJsRootedValue(jsRootedValue);
+ _destroyJsRootedValue(jsRootedValue);
}
}
@@ -141,23 +141,14 @@
/**
* Flag to enable debug checks on underlying JsRootedValues.
*/
- private static final DebugLogging debugInfo = debugFlag ? new DebugLogging() : null;
+ private static final DebugLogging debugInfo = debugFlag ? new DebugLogging()
+ : null;
/**
* This must match the value from jsapi.h.
*/
private static final int JSVAL_VOID = 0x80000001;
- /**
- * Create a new undefined JavaScript value.
- *
- * @param scriptObject
- * @return JsValueMoz object with an undefined value.
- */
- public static JsValueMoz createUndefinedValue(int scriptObject) {
- return new JsValueMoz(scriptObject, JSVAL_VOID);
- }
-
// CHECKSTYLE_NAMING_OFF -- native methods start with '_'
protected static native boolean _getBoolean(int jsRootedValue);
@@ -212,32 +203,22 @@
private static native int _copyJsRootedValue(int jsRootedValue);
- private static native int _createJsRootedValue(int scriptObject, int jsval);
-
- private static native void _destroyJsRootedValue(int jsRootedValue);
-
- // CHECKSTYLE_NAMING_ON
-
/**
* Create a JsRootedValue and return a pointer to it as a Java int.
*
- * @param scriptObject opaque script object pointer as an integer.
* @param jsval JavaScript jsval for initial value
* @return pointer to JsRootedValue object as an integer
*/
- private static int createJsRootedValue(int scriptObject, int jsval) {
- int jsRootedValue = _createJsRootedValue(scriptObject, jsval);
- return jsRootedValue;
- }
+ private static native int _createJsRootedValue(int jsval);
/**
* Destroy a JsRootedValue.
*
* @param jsRootedValue pointer to underlying JsRootedValue as an integer.
*/
- private static void destroyJsRootedValue(int jsRootedValue) {
- _destroyJsRootedValue(jsRootedValue);
- }
+ private static native void _destroyJsRootedValue(int jsRootedValue);
+
+ // CHECKSTYLE_NAMING_ON
/**
* Convert an address to a hex string.
@@ -255,6 +236,16 @@
private int jsRootedValue;
/**
+ * Create a JsValueMoz object representing the undefined value.
+ */
+ public JsValueMoz() {
+ this.jsRootedValue = _createJsRootedValue(JSVAL_VOID);
+ if (debugFlag) {
+ debugInfo.createInstance(jsRootedValue);
+ }
+ }
+
+ /**
* Create a JsValueMoz object wrapping a JsRootedValue object given the
* pointer to it as an integer.
*
@@ -279,21 +270,6 @@
}
}
- /**
- * Create a JsValue object with the JavaScript value jsval.
- *
- * Only used internally.
- *
- * @param scriptObject reference to containing window object in JavaScript
- * @param jsval a JavaScript jsval as a 32-bit int
- */
- protected JsValueMoz(int scriptObject, int jsval) {
- jsRootedValue = _createJsRootedValue(scriptObject, jsval);
- if (debugFlag) {
- debugInfo.createInstance(jsRootedValue);
- }
- }
-
/*
* (non-Javadoc)
*
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 f4875a0..b95a9ff 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
@@ -35,9 +35,9 @@
* @param jsthis the wrapped Java object to invoke
* @param jsargs an array of JavaScript values to pass as parameters
* @param returnValue the JavaScript value in which to store the returned
- * value
+ * value
*/
- void invoke(int jscontext, int jsthis, int[] jsargs, int returnValue);
+ void invoke(int jsthis, int[] jsargs, int returnValue);
}
/**
@@ -119,8 +119,8 @@
*
* @throws RuntimeException if the invoke fails
*/
- public static void invoke(int scriptObject, String methodName,
- int jsthis, int[] jsargs, int retval) {
+ public static void invoke(int scriptObject, String methodName, int jsthis,
+ int[] jsargs, int retval) {
if (!_invoke(scriptObject, methodName, jsthis, jsargs, retval)) {
throw new RuntimeException("Failed to invoke native method: "
+ methodName + " with " + jsargs.length + " arguments.");
@@ -130,11 +130,9 @@
/**
* Call this to raise an exception in JavaScript before returning control.
* Currently, the JavaScript exception throw is always null.
- *
- * @param jscontext A JSContext pointer as a Java int
*/
- public static void raiseJavaScriptException(int jscontext) {
- if (!_raiseJavaScriptException(jscontext)) {
+ public static void raiseJavaScriptException() {
+ if (!_raiseJavaScriptException()) {
throw new RuntimeException(
"Failed to raise Java Exception into JavaScript.");
}
@@ -192,7 +190,6 @@
}
// CHECKSTYLE_NAMING_OFF: Non JSNI native code may have leading '_'s.
- private static native boolean _executeScript(int scriptObject, String code);
private static native boolean _executeScriptWithInfo(int scriptObject,
String newScript, String file, int line);
@@ -203,7 +200,7 @@
* @param scriptObject nsIScriptGlobalObject* as an int
* @param methodName name of JavaScript method
* @param jsThisInt JavaScript object to invoke the method on, as a
- * JsRootedValue int
+ * JsRootedValue int
* @param jsArgsInt array of arguments, as an array of JsRootedValue ints
* @param jsRetValint pointer to JsRootedValue to receive return value
* @return true on success
@@ -211,7 +208,7 @@
private static native boolean _invoke(int scriptObject, String methodName,
int jsThisInt, int[] jsArgsInt, int jsRetValInt);
- private static native boolean _raiseJavaScriptException(int jscontext);
+ private static native boolean _raiseJavaScriptException();
private static native boolean _registerExternalFactoryHandler();
@@ -219,6 +216,7 @@
/**
* Print debug information for a JS method invocation.
+ *
* TODO(jat): remove this method
*
* @param methodName the name of the JS method being invoked
diff --git a/dev/linux/src/com/google/gwt/dev/shell/moz/MethodDispatch.java b/dev/linux/src/com/google/gwt/dev/shell/moz/MethodDispatch.java
index 2297ee4..ea7301f 100644
--- a/dev/linux/src/com/google/gwt/dev/shell/moz/MethodDispatch.java
+++ b/dev/linux/src/com/google/gwt/dev/shell/moz/MethodDispatch.java
@@ -40,20 +40,18 @@
}
/**
- * Invoke a Java method from JavaScript.
- * This is called solely from native code.
+ * Invoke a Java method from JavaScript. This is called solely from native
+ * code.
*
- * @param jscontext JSContext* passed as an integer
* @param jsthis JavaScript reference to Java object
* @param jsargs array of JavaScript values for parameters
* @param returnValue JavaScript value to return result in
* @throws RuntimeException if improper arguments are supplied
*
* TODO(jat): lift most of this interface to platform-independent code (only
- * exceptions still need to be made platform-independent)
+ * exceptions still need to be made platform-independent)
*/
- public void invoke(int jscontext, int jsthisInt, int[] jsargsInt,
- int returnValueInt) {
+ public void invoke(int jsthisInt, int[] jsargsInt, int returnValueInt) {
JsValue jsthis = new JsValueMoz(jsthisInt);
JsValue jsargs[] = new JsValue[jsargsInt.length];
for (int i = 0; i < jsargsInt.length; ++i) {
@@ -89,11 +87,11 @@
// Java back into JavaScript
Throwable t = e.getTargetException();
// TODO(jat): if this was originally JavaScript exception, re-throw the
- // original exception rather than just a null.
+ // original exception rather than just a null.
ModuleSpaceMoz.setThrownJavaException(t);
- LowLevelMoz.raiseJavaScriptException(jscontext);
+ LowLevelMoz.raiseJavaScriptException();
} catch (IllegalArgumentException e) {
- // TODO(jat): log to treelogger instead? If so, how do I get to it?
+ // TODO(jat): log to treelogger instead? If so, how do I get to it?
System.err.println("MethodDispatch.invoke, method=" + method.toString()
+ ": argument mismatch");
for (int i = 0; i < argc; ++i) {
diff --git a/dev/linux/src/com/google/gwt/dev/shell/moz/ModuleSpaceMoz.java b/dev/linux/src/com/google/gwt/dev/shell/moz/ModuleSpaceMoz.java
index 2b1fa7d..e9430e4 100644
--- a/dev/linux/src/com/google/gwt/dev/shell/moz/ModuleSpaceMoz.java
+++ b/dev/linux/src/com/google/gwt/dev/shell/moz/ModuleSpaceMoz.java
@@ -79,7 +79,7 @@
protected JsValue doInvoke(String name, Object jthis, Class[] types,
Object[] args) {
- JsValueMoz jsthis = JsValueMoz.createUndefinedValue(window);
+ JsValueMoz jsthis = new JsValueMoz();
CompilingClassLoader isolatedClassLoader = getIsolatedClassLoader();
jsthis.setWrappedJavaObject(isolatedClassLoader, jthis);
@@ -87,11 +87,11 @@
JsValueMoz argv[] = new JsValueMoz[argc];
int[] jsArgsInt = new int[argc];
for (int i = 0; i < argc; ++i) {
- argv[i] = JsValueMoz.createUndefinedValue(window);
+ argv[i] = new JsValueMoz();
JsValueGlue.set(argv[i], isolatedClassLoader, types[i], args[i]);
jsArgsInt[i] = argv[i].getJsRootedValue();
}
- JsValueMoz returnVal = JsValueMoz.createUndefinedValue(window);
+ JsValueMoz returnVal = new JsValueMoz();
LowLevelMoz.invoke(window, name, jsthis.getJsRootedValue(), jsArgsInt,
returnVal.getJsRootedValue());
return returnVal;
diff --git a/jni/linux/ExternalWrapper.cpp b/jni/linux/ExternalWrapper.cpp
index eff2802..d13d8b1 100644
--- a/jni/linux/ExternalWrapper.cpp
+++ b/jni/linux/ExternalWrapper.cpp
@@ -41,6 +41,7 @@
{
Tracer tracer("gwtOnLoad");
tracer.log("context=%08x", unsigned(cx));
+ JsRootedValue::ContextManager context(cx);
JsRootedValue::ensureRuntime(cx);
if (argc < 2) {
tracer.setFail("less than 2 args");
@@ -120,6 +121,7 @@
JSObject *obj, jsval id, jsval *vp)
{
Tracer tracer("gwt_external_getProperty");
+ JsRootedValue::ContextManager context(cx);
if (*vp != JSVAL_VOID)
return JS_TRUE;
@@ -166,10 +168,13 @@
static void JS_DLL_CALLBACK gwt_external_finalize(JSContext *cx, JSObject *obj)
{
+ // We don't need to push a context if all we do is DeleteGlobalRef
+ Tracer tracer("gwt_external_finalize", obj);
jobject externalObject = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
- if (externalObject)
+ if (externalObject) {
savedJNIEnv->DeleteGlobalRef(externalObject);
- JS_FinalizeStub(cx,obj);
+ }
+ JS_FinalizeStub(cx, obj);
}
static JSBool JS_DLL_CALLBACK gwt_external_setProperty(JSContext *cx,
@@ -201,7 +206,14 @@
tracer.setFail("null script object pointer");
return NS_ERROR_INVALID_POINTER;
}
- if (!mScriptObject) {
+ if (!jsWindowExternalObject) {
+ JSContext* cx = NS_REINTERPRET_CAST(JSContext*,
+ aContext->GetNativeContext());
+ if (!cx) {
+ tracer.setFail("can't get JSContext");
+ return NS_ERROR_UNEXPECTED;
+ }
+ JsRootedValue::ContextManager context(cx);
*aScriptObject = 0;
nsIScriptGlobalObject* globalObject = aContext->GetGlobalObject();
@@ -239,12 +251,6 @@
tracer.setFail("can't get GlobalRef for external object");
return NS_ERROR_UNEXPECTED;
}
- JSContext* cx = NS_REINTERPRET_CAST(JSContext*,
- aContext->GetNativeContext());
- if (!cx) {
- tracer.setFail("can't get JSContext");
- return NS_ERROR_UNEXPECTED;
- }
JSObject* newObj = JS_NewObject(cx, &gwt_external_class, 0,
globalObject->GetGlobalJSObject());
if (!newObj) {
@@ -262,16 +268,16 @@
tracer.setFail("can't define gwtOnLoad function on JavaScript object");
return NS_ERROR_UNEXPECTED;
}
- mScriptObject = newObj;
+ jsWindowExternalObject = newObj;
}
- *aScriptObject = mScriptObject;
+ *aScriptObject = jsWindowExternalObject;
return NS_OK;
}
NS_IMETHODIMP ExternalWrapper::SetScriptObject(void* aScriptObject)
{
- mScriptObject = aScriptObject;
+ jsWindowExternalObject = aScriptObject;
return NS_OK;
}
diff --git a/jni/linux/ExternalWrapper.h b/jni/linux/ExternalWrapper.h
index 7cabc23..0168083 100644
--- a/jni/linux/ExternalWrapper.h
+++ b/jni/linux/ExternalWrapper.h
@@ -22,10 +22,10 @@
NS_DECL_ISUPPORTS
NS_IMETHOD GetScriptObject(nsIScriptContext *aContext, void** aScriptObject);
NS_IMETHOD SetScriptObject(void* aScriptObject);
- ExternalWrapper(): mScriptObject(0) { }
+ ExternalWrapper(): jsWindowExternalObject(0) { }
private:
~ExternalWrapper() { }
- void *mScriptObject;
+ void *jsWindowExternalObject;
};
class nsRpExternalFactory : public nsIFactory {
diff --git a/jni/linux/JsRootedValue.cpp b/jni/linux/JsRootedValue.cpp
index 90b890b..29b95ca 100644
--- a/jni/linux/JsRootedValue.cpp
+++ b/jni/linux/JsRootedValue.cpp
@@ -16,25 +16,29 @@
#include "JsRootedValue.h"
-// intialize static value used to hold the JavaScript String class.
+// Initialize static value used to hold the JavaScript String class.
JSClass* JsRootedValue::stringClass = 0;
-// intialize static reference to the sole JSRuntime value in Gecko
+// Initialize static reference to the sole JSRuntime value in Gecko.
JSRuntime* JsRootedValue::runtime = 0;
+// Static stack of JavaScript execution contexts.
+std::stack<JSContext*> JsRootedValue::contextStack;
+
/*
* Actually get the stringClass pointer from JavaScript.
*/
void JsRootedValue::fetchStringClass() const {
Tracer tracer("JsRootedValue::fetchStringClass");
- jsval val = JS_GetEmptyStringValue(context_);
+ JSContext* cx = currentContext();
+ jsval val = JS_GetEmptyStringValue(cx);
JSObject* obj;
// on error, leave stringClass null
- if (!JS_ValueToObject(context_, val, &obj)) return;
+ if (!JS_ValueToObject(cx, val, &obj)) return;
if (!obj) {
tracer.log("ensureStringClass: null object");
return;
}
- stringClass = JS_GET_CLASS(context_, obj);
+ stringClass = JS_GET_CLASS(cx, obj);
tracer.log("stringClass=%08x", unsigned(stringClass));
}
diff --git a/jni/linux/JsRootedValue.h b/jni/linux/JsRootedValue.h
index 881ac12..65c25af 100644
--- a/jni/linux/JsRootedValue.h
+++ b/jni/linux/JsRootedValue.h
@@ -20,6 +20,7 @@
#include "mozilla-headers.h"
#include "Tracer.h"
+#include <stack>
extern "C" JSClass gwt_nativewrapper_class;
@@ -42,13 +43,14 @@
{
private:
// the JavaScript String class
- static JSClass* stringClass;
+ static JSClass* stringClass;
// Javascript runtime
- static JSRuntime* runtime;
+ static JSRuntime* runtime;
- // Javascript context
- JSContext* context_;
+ // stack of Javascript contexts
+ static std::stack<JSContext*> contextStack;
+
// underlying Javascript value
jsval value_;
@@ -78,13 +80,64 @@
}
}
+ /*
+ * Push a new JavaScript execution context.
+ */
+ static void pushContext(JSContext* context) {
+ Tracer tracer("JsRootedValue::pushContext");
+ tracer.log("pushed context=%08x", unsigned(context));
+ contextStack.push(context);
+ }
+
+ /*
+ * Pop a JavaScript execution context from the context stack.
+ */
+ static void popContext() {
+ Tracer tracer("JsRootedValue::popContext");
+ JSContext* context = currentContext();
+ contextStack.pop();
+ tracer.log("popped context=%08x", unsigned(context));
+ }
+
public:
/*
+ * This is a helper class used to push/pop JSContext values automatically,
+ * using RAII. Simply create a ContextManager object on the stack and it
+ * will push the context at creation time and pop it at destruction time,
+ * so you don't have to worry about covering all the exit paths from a
+ * function.
+ */
+ class ContextManager {
+ public:
+ explicit ContextManager(JSContext* context) {
+ JsRootedValue::pushContext(context);
+ }
+ ~ContextManager() {
+ JsRootedValue::popContext();
+ }
+ };
+
+ /*
+ * This is a helper class to manage short-lived roots on the stack, using
+ * RAII to free the roots when no longer needed.
+ */
+ class Temp {
+ private:
+ void* ptr_;
+ public:
+ explicit Temp(void* ptr) : ptr_(ptr) {
+ JS_AddNamedRootRT(runtime, ptr_, "temporary root");
+ }
+ ~Temp() {
+ JS_RemoveRootRT(runtime, ptr_);
+ }
+ };
+
+ /*
* Copy constructor - make another rooted value that refers to the same
* JavaScript object (or has the same value if a primitive)
*/
- JsRootedValue(const JsRootedValue& rooted_value)
- : context_(rooted_value.context_), value_(rooted_value.value_)
+ JsRootedValue(const JsRootedValue& rooted_value) : value_(rooted_value.value_)
{
constructorHelper("JsRootedValue copy ctor");
}
@@ -92,8 +145,7 @@
/*
* Create a value with a given jsval value
*/
- JsRootedValue(JSContext* context, jsval value)
- : context_(context), value_(value)
+ JsRootedValue(jsval value) : value_(value)
{
constructorHelper("JsRootedValue jsval ctor");
}
@@ -101,7 +153,7 @@
/*
* Create a void value
*/
- JsRootedValue(JSContext* context) : context_(context), value_(JSVAL_VOID) {
+ JsRootedValue() : value_(JSVAL_VOID) {
constructorHelper("JsRootedValue void ctor");
}
@@ -110,7 +162,6 @@
*/
~JsRootedValue() {
Tracer tracer("~JsRootedValue", this);
- tracer.log("context=%08x", unsigned(context_));
// ignore error since currently it is not possible to fail
JS_RemoveRootRT(runtime, &value_);
}
@@ -123,32 +174,26 @@
}
/*
- * Return the JSContext* pointer.
+ * Return the current JavaScript execution context.
*/
- JSContext* getContext() const { return context_; }
-
- /*
- * Return the global object for this value's context.
- */
- JSObject* getGlobalObject() const { return JS_GetGlobalObject(getContext()); }
-
+ static JSContext* currentContext() {
+ Tracer tracer("JsRootedValue::currentContext");
+ if (contextStack.empty()) {
+ // TODO(jat): better error handling?
+ fprintf(stderr, "JsRootedValue::currentContext - context stack empty\n");
+ ::abort();
+ }
+ JSContext* context = contextStack.top();
+ tracer.log("context=%08x", unsigned(context));
+ return context;
+ }
+
/*
* Return the underlying JS object
*/
jsval getValue() const { return value_; }
/*
- * Sets the value of the underlying JS object and its context.
- *
- * Returns false if an error occurred.
- */
- bool setContextValue(JSContext* new_context, jsval new_value) {
- context_ = new_context;
- value_ = new_value;
- return true;
- }
-
- /*
* Sets the value of the underlying JS object.
*
* Returns false if an error occurred.
@@ -172,7 +217,7 @@
double getDouble() const {
jsdouble return_value=0.0;
// ignore return value -- if it fails, value will remain 0.0
- JS_ValueToNumber(getContext(), value_, &return_value);
+ JS_ValueToNumber(currentContext(), value_, &return_value);
return double(return_value);
}
/*
@@ -182,7 +227,7 @@
*/
bool setDouble(double val) {
jsval js_double;
- if(!JS_NewDoubleValue(getContext(), jsdouble(val), &js_double)) {
+ if(!JS_NewDoubleValue(currentContext(), jsdouble(val), &js_double)) {
return false;
}
return setValue(js_double);
@@ -261,7 +306,7 @@
* Return this value as a string, converting as necessary.
*/
JSString* asString() const {
- return JS_ValueToString(getContext(), value_);
+ return JS_ValueToString(currentContext(), value_);
}
/* Returns the string as a JSString pointer.
@@ -299,7 +344,7 @@
* Returns false on failure.
*/
bool setString(const wchar_t* utf16) {
- JSString* str = JS_NewUCStringCopyZ(getContext(),
+ JSString* str = JS_NewUCStringCopyZ(currentContext(),
reinterpret_cast<const jschar*>(utf16));
return setValue(STRING_TO_JSVAL(str));
}
@@ -310,7 +355,7 @@
* Returns false on failure.
*/
bool setString(const wchar_t* utf16, size_t len) {
- JSString* str = JS_NewUCStringCopyN(getContext(),
+ JSString* str = JS_NewUCStringCopyN(currentContext(),
reinterpret_cast<const jschar*>(utf16), len);
return setValue(STRING_TO_JSVAL(str));
}
@@ -336,7 +381,7 @@
* Result is undefined if it is not actually an object.
*/
const JSClass* getObjectClass() const {
- return isObject() ? JS_GET_CLASS(getContext(), getObject()) : 0;
+ return isObject() ? JS_GET_CLASS(currentContext(), getObject()) : 0;
}
/*
diff --git a/jni/linux/JsValueMoz.cpp b/jni/linux/JsValueMoz.cpp
index 5398b76..be30ea4 100644
--- a/jni/linux/JsValueMoz.cpp
+++ b/jni/linux/JsValueMoz.cpp
@@ -34,15 +34,18 @@
* Returns null on failure. Caller is responsible for deleting
* returned JsRootedValue when done with it.
*/
-JsRootedValue* GetFieldAsRootedValue(JSContext* context, jclass clazz,
+static JsRootedValue* GetFieldAsRootedValue(JSContext* cx, jclass clazz,
jobject obj, jstring fieldName)
{
+ Tracer tracer("GetFieldAsRootedValue");
+ JsRootedValue::ContextManager context(cx);
jmethodID getFieldMeth = savedJNIEnv->GetMethodID(clazz, "getField",
"(Ljava/lang/String;I)V");
- if (!getFieldMeth || savedJNIEnv->ExceptionCheck())
+ if (!getFieldMeth || savedJNIEnv->ExceptionCheck()) {
return 0;
+ }
- JsRootedValue* jsRootedValue = new JsRootedValue(context);
+ JsRootedValue* jsRootedValue = new JsRootedValue();
savedJNIEnv->CallVoidMethod(obj, getFieldMeth, fieldName,
reinterpret_cast<jint>(jsRootedValue));
if (savedJNIEnv->ExceptionCheck()) {
@@ -63,18 +66,22 @@
*
* returns true on success, false on failure
*/
-bool SetFieldFromRootedValue(JSContext* context, jclass clazz,
+static bool SetFieldFromRootedValue(JSContext* cx, jclass clazz,
jobject obj, jstring fieldName, JsRootedValue* jsRootedValue)
{
+ Tracer tracer("SetFieldAsRootedValue");
+ JsRootedValue::ContextManager context(cx);
jmethodID getFieldMeth = savedJNIEnv->GetMethodID(clazz, "setField",
"(Ljava/lang/String;I)V");
- if (!getFieldMeth || savedJNIEnv->ExceptionCheck())
+ if (!getFieldMeth || savedJNIEnv->ExceptionCheck()) {
return false;
+ }
savedJNIEnv->CallVoidMethod(obj, getFieldMeth, fieldName,
reinterpret_cast<jint>(jsRootedValue));
- if (savedJNIEnv->ExceptionCheck())
+ if (savedJNIEnv->ExceptionCheck()) {
return false;
+ }
return true;
}
@@ -88,7 +95,9 @@
jniEnv->ThrowNew(exceptionClass, msg);
}
-
+/*
+ * Types of jsvals.
+ */
enum JsValueType {
JSVAL_TYPE_VOID=0,
JSVAL_TYPE_NULL,
@@ -99,6 +108,9 @@
JSVAL_TYPE_UNKNOWN,
};
+/*
+ * Names of jsval types -- must match the order of the enum above.
+ */
static const char* JsValueTypeStrings[]={
"undefined",
"null",
@@ -109,6 +121,9 @@
"unknown",
};
+/*
+ * Return the type of a jsval.
+ */
static JsValueType GetValueType(jsval val) {
if(JSVAL_IS_VOID(val)) {
return JSVAL_TYPE_VOID;
@@ -128,24 +143,156 @@
}
/*
+ * Called from JavaScript to call a Java method that has previously been
+ * wrapped. See _setWrappedFunction for use.
+ */
+static JSBool invokeJavaMethod(JSContext *cx, JSObject *obj, uintN argc,
+ jsval *argv, jsval *rval)
+{
+ Tracer tracer("invokeJavaMethod");
+ JsRootedValue::ContextManager context(cx);
+
+ // I kid you not; this is how XPConnect gets their function object so they can
+ // multiplex dispatch the call from a common site. See XPCDispObject.cpp(466)
+ //
+ // I now have a secondary confirmation that this trick is legit.
+ // brandon@mozilla.org writes:
+ //
+ // argv[-2] is part of the JS API, unabstracted. Just as argv[0] is the
+ // first argument (if argc != 0), argv[-1] is the |this| parameter (equal
+ // to OBJECT_TO_JSVAL(obj) in a native method with the standard |obj|
+ // second formal parameter name), and argv[-2] is the callee object, tagged
+ // as a jsval.
+ if (JS_TypeOfValue(cx, argv[-2]) != JSTYPE_FUNCTION) {
+ tracer.setFail("not a function type");
+ return JS_FALSE;
+ }
+ JSObject* funObj = JSVAL_TO_OBJECT(argv[-2]);
+
+ // Pull the wrapper object out of the funObj's reserved slot
+ jsval jsCleanupObj;
+ if (!JS_GetReservedSlot(cx, funObj, 0, &jsCleanupObj)) {
+ tracer.setFail("JS_GetReservedSlot failed");
+ return JS_FALSE;
+ }
+ JSObject* cleanupObj = JSVAL_TO_OBJECT(jsCleanupObj);
+ if (!cleanupObj) {
+ tracer.setFail("cleanupObj is null");
+ return JS_FALSE;
+ }
+
+ // Get DispatchMethod instance out of the wrapper object
+ jobject dispMeth =
+ NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, cleanupObj));
+ if (!dispMeth) {
+ tracer.setFail("dispMeth is null");
+ return JS_FALSE;
+ }
+ jclass dispClass = savedJNIEnv->GetObjectClass(dispMeth);
+ if (!dispClass || savedJNIEnv->ExceptionCheck()) {
+ tracer.setFail("GetObjectClass returns null");
+ return JS_FALSE;
+ }
+
+ // lookup the invoke method on the dispatch object
+ jmethodID invokeID =
+ savedJNIEnv->GetMethodID(dispClass, "invoke", "(I[II)V");
+ if (!invokeID || savedJNIEnv->ExceptionCheck()) {
+ tracer.setFail("GetMethodID failed");
+ return JS_FALSE;
+ }
+
+ // create an array of integers to hold the JsRootedValue pointers passed
+ // to the invoke method
+ jintArray args = savedJNIEnv->NewIntArray(argc);
+ if (!args || savedJNIEnv->ExceptionCheck()) {
+ tracer.setFail("NewIntArray failed");
+ return JS_FALSE;
+ }
+
+ // these arguments are already rooted by the JS interpreter, but we
+ // can't easily take advantage of that without complicating the JsRootedValue
+ // interface.
+
+ // argv[-1] is OBJECT_TO_JSVAL(this)
+ JsRootedValue* jsThis = new JsRootedValue(argv[-1]);
+ tracer.log("jsthis=%08x, RV=%08x", unsigned(argv[-1]), unsigned(jsThis));
+
+ // create JsRootedValues for arguments
+ JsRootedValue *jsArgs[argc];
+ for (uintN i = 0; i < argc; ++i) {
+ jsArgs[i] = new JsRootedValue(argv[i]);
+ }
+ savedJNIEnv->SetIntArrayRegion(args, 0, argc,
+ reinterpret_cast<jint*>(jsArgs));
+ if (savedJNIEnv->ExceptionCheck()) {
+ tracer.setFail("SetIntArrayRegion failed");
+ return JS_FALSE;
+ }
+
+ // slot for return value
+ JsRootedValue* jsReturnVal = new JsRootedValue();
+
+ // TODO(jat): small window here where invocation may fail before Java
+ // takes ownership of the JsRootedValue objects. One solution would be
+ // to reference-count them between Java and C++ (so the reference count
+ // would always be 0, 1, or 2). Also setField has a similar problem.
+ // I plan to fix this when switching away from Java holding pointers to
+ // C++ objects as part of the fix for 64-bit support (which we could
+ // accomplish inefficiently by changing int to long everywhere, but there
+ // are other 64-bit issues to resolve and we need to reduce the number of
+ // roots the JS interpreter has to search.
+
+ // call Java method
+ savedJNIEnv->CallVoidMethod(dispMeth, invokeID, reinterpret_cast<int>(jsThis),
+ args, reinterpret_cast<int>(jsReturnVal));
+
+ JSBool returnValue = JS_TRUE;
+
+ if (savedJNIEnv->ExceptionCheck()) {
+ tracer.log("dispMeth=%08x", unsigned(dispMeth));
+ tracer.setFail("java exception is active:");
+ jobject exception = savedJNIEnv->ExceptionOccurred();
+ if (exception) {
+ fprintf(stderr, "Exception occurred in MethodDispatch.invoke:\n");
+ savedJNIEnv->ExceptionDescribe();
+ savedJNIEnv->DeleteLocalRef(exception);
+ }
+ returnValue = JS_FALSE;
+ } else if (JS_IsExceptionPending(cx)) {
+ tracer.setFail("js exception is active");
+ returnValue = JS_FALSE;
+ }
+
+ // extract return value
+ *rval = jsReturnVal->getValue();
+
+#if 0
+ // NOTE: C++ objects are not cleaned up here because Java now owns them.
+ // TODO(jat): if reference-counted, they *do* need to be Released here.
+
+ // free JsRootedValues
+ for (uintN i = 0; i < argc; ++i) {
+ delete jsArgs[i];
+ }
+ delete jsThis;
+ delete jsReturnVal;
+#endif
+
+ return returnValue;
+}
+
+/*
* Class: com_google_gwt_dev_shell_moz_JsValueMoz
* Method: _createJsRootedValue()
- * Signature: (II)I
+ * Signature: (I)I
*/
extern "C" JNIEXPORT jint JNICALL
Java_com_google_gwt_dev_shell_moz_JsValueMoz__1createJsRootedValue
- (JNIEnv* jniEnv, jclass, jint scriptObjInt, jint jsval)
+ (JNIEnv* jniEnv, jclass, jint jsval)
{
Tracer tracer("JsValueMoz._createJsRootedValue");
- nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
- nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
- if (!scriptContext) {
- ThrowHostedModeException(jniEnv, "Unable to get script context");
- tracer.setFail("Unable to get script context");
- return 0;
- }
- JSContext* context = NS_REINTERPRET_CAST(JSContext*, scriptContext->GetNativeContext());
- JsRootedValue* jsRootedValue = new JsRootedValue(context, jsval);
+ JsRootedValue* jsRootedValue = new JsRootedValue(jsval);
return NS_REINTERPRET_CAST(jint, jsRootedValue);
}
@@ -203,8 +350,6 @@
* Signature: (I)I
*
* @see com.google.gwt.dev.shell.moz.JsValueMoz#getInt()
- *
- * TODO(jat): unboxing Javascript Integer type?
*/
extern "C" JNIEXPORT jint JNICALL
Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getInt
@@ -274,14 +419,14 @@
(jsRootedValueInt);
Tracer tracer("JsValueMoz._getTypeString", jsRootedValue);
jsval val = jsRootedValue->getValue();
- JSContext* context = jsRootedValue->getContext();
+ JSContext* cx = JsRootedValue::currentContext();
JsValueType valueType = GetValueType(val);
const char* typeString = 0;
char buf[256];
if(valueType == JSVAL_TYPE_OBJECT) {
JSObject* jsObject = JSVAL_TO_OBJECT(val);
- JSClass* objClass = JS_GET_CLASS(context, jsObject);
- if (JS_InstanceOf(context, jsObject,
+ JSClass* objClass = JS_GET_CLASS(cx, jsObject);
+ if (JS_InstanceOf(cx, jsObject,
&gwt_nativewrapper_class, 0)) {
typeString = "Java object";
} else {
@@ -315,14 +460,14 @@
return 0;
}
JSObject* jsObject = JSVAL_TO_OBJECT(val);
- JSContext* context = jsRootedValue->getContext();
- if(!JS_InstanceOf(context, jsObject, &gwt_nativewrapper_class, 0)) {
+ JSContext* cx = JsRootedValue::currentContext();
+ if(!JS_InstanceOf(cx, jsObject, &gwt_nativewrapper_class, 0)) {
tracer.throwHostedModeException(jniEnv,
"Javascript object not a Java object");
return 0;
}
jobject javaObject
- = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(context, jsObject));
+ = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, jsObject));
return javaObject;
}
@@ -374,7 +519,7 @@
bool returnValue = false;
if(JSVAL_IS_OBJECT(val)) {
JSObject* jsObject = JSVAL_TO_OBJECT(val);
- returnValue = !JS_InstanceOf(jsRootedValue->getContext(), jsObject,
+ returnValue = !JS_InstanceOf(JsRootedValue::currentContext(), jsObject,
&gwt_nativewrapper_class, 0);
tracer.log("jsobject=%08x, isJSObject=%s", unsigned(jsObject),
returnValue ? "true" : "false");
@@ -485,7 +630,7 @@
bool returnValue = false;
if(JSVAL_IS_OBJECT(val)) {
JSObject* jsObject = JSVAL_TO_OBJECT(val);
- returnValue = JS_InstanceOf(jsRootedValue->getContext(), jsObject,
+ returnValue = JS_InstanceOf(JsRootedValue::currentContext(), jsObject,
&gwt_nativewrapper_class, 0);
tracer.log("jsobject=%08x, wrappedJava=%s", unsigned(jsObject),
returnValue ? "true" : "false");
@@ -567,8 +712,7 @@
JsRootedValue* jsOtherRootedValue = reinterpret_cast<JsRootedValue*>
(jsOtherRootedValueInt);
Tracer tracer("JsValueMoz._setJsRootedValue", jsRootedValue);
- jsRootedValue->setContextValue(jsOtherRootedValue->getContext(),
- jsOtherRootedValue->getValue());
+ jsRootedValue->setValue(jsOtherRootedValue->getValue());
}
/*
@@ -647,15 +791,17 @@
JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
(jsRootedValueInt);
Tracer tracer("JsValueMoz._setWrappedJavaObject", jsRootedValue);
- JSContext* context = jsRootedValue->getContext();
- JSObject* scriptWindow = JS_GetGlobalObject(context);
- JSObject* newObj = JS_NewObject(context, &gwt_nativewrapper_class, 0,
+ JSContext* cx = JsRootedValue::currentContext();
+ JSObject* scriptWindow = JS_GetGlobalObject(cx);
+ JSObject* newObj = JS_NewObject(cx, &gwt_nativewrapper_class, 0,
scriptWindow);
if (!newObj) {
tracer.throwHostedModeException(jniEnv,
"Unable to allocate JS object to wrap Java object");
return;
}
+ // Save in output value so it won't get GCed.
+ jsRootedValue->setObject(newObj);
tracer.log("jsobject=%08x", unsigned(newObj));
// TODO(jat): how does this globalref get freed?
@@ -665,7 +811,7 @@
"Unable to allocate global reference for JS wrapper");
return;
}
- if (!JS_SetPrivate(context, newObj, dispObjRef)) {
+ if (!JS_SetPrivate(cx, newObj, dispObjRef)) {
jniEnv->DeleteGlobalRef(dispObjRef);
tracer.throwHostedModeException(jniEnv,
"Unable to allocate global reference for JS wrapper");
@@ -674,39 +820,34 @@
// forcibly setup a "toString" method to override the default
jclass dispClass = jniEnv->GetObjectClass(obj);
if (jniEnv->ExceptionCheck()) {
- jniEnv->DeleteGlobalRef(dispObjRef);
- tracer.setFail("can't get object class");
+ tracer.throwHostedModeException(jniEnv, "Can't get object class");
return;
}
jmethodID getFieldMeth = jniEnv->GetMethodID(dispClass, "getField",
"(Ljava/lang/String;I)V");
if (!getFieldMeth || jniEnv->ExceptionCheck()) {
- jniEnv->DeleteGlobalRef(dispObjRef);
- tracer.setFail("can't get getField method");
+ tracer.throwHostedModeException(jniEnv, "Can't get getField method");
return;
}
jstring ident = jniEnv->NewStringUTF("@java.lang.Object::toString()");
if (!ident || jniEnv->ExceptionCheck()) {
- jniEnv->DeleteGlobalRef(dispObjRef);
- tracer.setFail("can't create Java string for toString method name");
+ tracer.throwHostedModeException(jniEnv,
+ "Can't create Java string for toString method name");
return;
}
// allocate a new root to hold the result of the getField call
- JsRootedValue* toStringFunc = new JsRootedValue(context, JSVAL_VOID);
+ JsRootedValue* toStringFunc = new JsRootedValue();
jniEnv->CallVoidMethod(obj, getFieldMeth, ident,
NS_REINTERPRET_CAST(jint, toStringFunc));
if (toStringFunc->isUndefined() || jniEnv->ExceptionCheck()) {
- jniEnv->DeleteGlobalRef(dispObjRef);
- tracer.setFail("getField(toString) failed");
+ tracer.throwHostedModeException(jniEnv, "getField(toString) failed");
return;
}
- if (!JS_DefineProperty(context, newObj, "toString", toStringFunc->getValue(),
+ if (!JS_DefineProperty(cx, newObj, "toString", toStringFunc->getValue(),
JS_PropertyStub, JS_PropertyStub, JSPROP_READONLY | JSPROP_PERMANENT)) {
- jniEnv->DeleteGlobalRef(dispObjRef);
- tracer.setFail("can't define JS toString method");
+ tracer.throwHostedModeException(jniEnv, "Can't define JS toString method");
return;
}
- jsRootedValue->setObject(newObj);
}
/*
@@ -724,8 +865,8 @@
JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
(jsRootedValueInt);
Tracer tracer("JsValueMoz._setWrappedFunction", jsRootedValue);
- JSContext* context = jsRootedValue->getContext();
- JSObject* scriptWindow = JS_GetGlobalObject(context);
+ JSContext* cx = JsRootedValue::currentContext();
+ JSObject* scriptWindow = JS_GetGlobalObject(cx);
JStringWrap nameStr(jniEnv, methodName);
if (!nameStr.str()) {
tracer.throwHostedModeException(jniEnv,
@@ -734,8 +875,8 @@
}
tracer.log("JsRootedValue=%08x, method=%s, obj=%08x", jsRootedValueInt,
nameStr.str(), unsigned(dispatchMethod));
- JSFunction* function = JS_NewFunction(context, invokeJavaMethod, 0, JSFUN_LAMBDA, 0,
- nameStr.str());
+ JSFunction* function = JS_NewFunction(cx, invokeJavaMethod, 0,
+ JSFUN_LAMBDA, 0, nameStr.str());
if (!function) {
tracer.throwHostedModeException(jniEnv, "JS_NewFunction failed");
return;
@@ -745,31 +886,35 @@
tracer.throwHostedModeException(jniEnv, "JS_GetFunctionObject failed");
return;
}
- // Create a wrapper object to hold and clean up dispMeth
- JSObject* cleanupObj = JS_NewObject(context, &gwt_functionwrapper_class, 0,
+ // Save in output value so it won't get GCed.
+ jsRootedValue->setObject(funObj);
+
+ // Create a cleanup object to hold and clean up dispMeth
+ JSObject* cleanupObj = JS_NewObject(cx, &gwt_functionwrapper_class, 0,
scriptWindow);
if (!cleanupObj) {
tracer.throwHostedModeException(jniEnv, "JS_NewObject failed");
return;
}
- jobject dispMethRef = jniEnv->NewGlobalRef(dispatchMethod);
- if (!dispMethRef || jniEnv->ExceptionCheck()) {
+ tracer.log("funObj=%08x, cleanupObj=%08x", unsigned(funObj),
+ unsigned(cleanupObj));
+ // Store the cleanup object in funObj's reserved slot; now GC protected.
+ if(!JS_SetReservedSlot(cx, funObj, 0, OBJECT_TO_JSVAL(cleanupObj))) {
+ tracer.throwHostedModeException(jniEnv, "JS_SetReservedSlot failed");
return;
}
-
+ jobject dispMethRef = jniEnv->NewGlobalRef(dispatchMethod);
+ if (!dispMethRef || jniEnv->ExceptionCheck()) {
+ tracer.throwHostedModeException(jniEnv,
+ "NewGlobalRef(dispatchMethod) failed");
+ return;
+ }
// Store our global ref in the wrapper object
- if (!JS_SetPrivate(context, cleanupObj, dispMethRef)) {
+ if (!JS_SetPrivate(cx, cleanupObj, dispMethRef)) {
jniEnv->DeleteGlobalRef(dispMethRef);
tracer.throwHostedModeException(jniEnv, "JS_SetPrivate(cleanupObj) failed");
return;
}
- // Store the wrapper object in funObj's reserved slot
- if(!JS_SetReservedSlot(context, funObj, 0, OBJECT_TO_JSVAL(cleanupObj))) {
- // TODO(jat): what to do with global ref?
- tracer.throwHostedModeException(jniEnv, "JS_SetReservedSlot failed");
- return;
- }
- jsRootedValue->setObject(funObj);
}
/*
@@ -787,7 +932,7 @@
(jsRootedValueInt);
Tracer tracer("JsValueMoz._toString", jsRootedValue);
jsval val = jsRootedValue->getValue();
- JSContext* cx = jsRootedValue->getContext();
+ JSContext* cx = JsRootedValue::currentContext();
// if it is a JavaScript object that has a toString member function
// call that, otherwise call JS_ValueToString
diff --git a/jni/linux/LowLevelMoz.cpp b/jni/linux/LowLevelMoz.cpp
index 1711b20..b0ee1ed 100644
--- a/jni/linux/LowLevelMoz.cpp
+++ b/jni/linux/LowLevelMoz.cpp
@@ -48,9 +48,6 @@
// include javah-generated header to make sure we match
#include "LowLevelMoz.h"
-// TODO(jat) should be in a header
-extern nsCID kGwtExternalCID;
-
JNIEnv* savedJNIEnv = 0;
jclass lowLevelMozClass;
@@ -202,199 +199,13 @@
}
/*
- * Print the current Java exception.
- */
-static void PrintJavaException(JNIEnv* env) {
- jobject exception = env->ExceptionOccurred();
- if (!exception) return;
- fprintf(stderr, "Exception occurred:\n");
- env->ExceptionDescribe();
- env->DeleteLocalRef(exception);
-}
-
-/* Called from JavaScript to call a Java method that has previously been
- * wrapped.
- */
-JSBool invokeJavaMethod(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
- jsval *rval)
-{
- Tracer tracer("invokeJavaMethod");
-
- // I kid you not; this is how XPConnect gets their function object so they can
- // multiplex dispatch the call from a common site. See XPCDispObject.cpp(466)
- //
- // I now have a secondary confirmation that this trick is legit.
- // brandon@mozilla.org writes:
- //
- // argv[-2] is part of the JS API, unabstracted. Just as argv[0] is the
- // first argument (if argc != 0), argv[-1] is the |this| parameter (equal
- // to OBJECT_TO_JSVAL(obj) in a native method with the standard |obj|
- // second formal parameter name), and argv[-2] is the callee object, tagged
- // as a jsval.
- if (JS_TypeOfValue(cx, argv[-2]) != JSTYPE_FUNCTION) {
- tracer.setFail("not a function type");
- return JS_FALSE;
- }
- JSObject* funObj = JSVAL_TO_OBJECT(argv[-2]);
-
- // Pull the wrapper object out of the funObj's reserved slot
- jsval jsCleanupObj;
- if (!JS_GetReservedSlot(cx, funObj, 0, &jsCleanupObj)) {
- tracer.setFail("JS_GetReservedSlot failed");
- return JS_FALSE;
- }
- JSObject* cleanupObj = JSVAL_TO_OBJECT(jsCleanupObj);
- if (!cleanupObj) {
- tracer.setFail("cleanupObj is null");
- return JS_FALSE;
- }
-
- // Get DispatchMethod instance out of the wrapper object
- jobject dispMeth =
- NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, cleanupObj));
- if (!dispMeth) {
- tracer.setFail("dispMeth is null");
- return JS_FALSE;
- }
- jclass dispClass = savedJNIEnv->GetObjectClass(dispMeth);
- if (!dispClass || savedJNIEnv->ExceptionCheck()) {
- tracer.setFail("GetObjectClass returns null");
- return JS_FALSE;
- }
-
- // lookup the invoke method on the dispatch object
- jmethodID invokeID =
- savedJNIEnv->GetMethodID(dispClass, "invoke", "(II[II)V");
- if (!invokeID || savedJNIEnv->ExceptionCheck()) {
- tracer.setFail("GetMethodID failed");
- return JS_FALSE;
- }
-
- // create an array of integers to hold the JsRootedValue pointers passed
- // to the invoke method
- jintArray args = savedJNIEnv->NewIntArray(argc);
- if (!args || savedJNIEnv->ExceptionCheck()) {
- tracer.setFail("NewIntArray failed");
- return JS_FALSE;
- }
-
- // these arguments are already rooted by the JS interpreter, but we
- // can't easily take advantage of that without complicating the JsRootedValue
- // interface.
-
- // argv[-1] is OBJECT_TO_JSVAL(this)
- JsRootedValue* jsThis = new JsRootedValue(cx, argv[-1]);
- tracer.log("jsthis=%08x, RV=%08x", unsigned(argv[-1]), unsigned(jsThis));
-
- // create JsRootedValues for arguments
- JsRootedValue *jsArgs[argc];
- for (uintN i = 0; i < argc; ++i) {
- jsArgs[i] = new JsRootedValue(cx, argv[i]);
- }
- savedJNIEnv->SetIntArrayRegion(args, 0, argc,
- reinterpret_cast<jint*>(jsArgs));
- if (savedJNIEnv->ExceptionCheck()) {
- tracer.setFail("SetIntArrayRegion failed");
- return JS_FALSE;
- }
-
- // slot for return value
- JsRootedValue* jsReturnVal = new JsRootedValue(cx);
-
- // TODO(jat): small window here where invocation may fail before Java
- // takes ownership of the JsRootedValue objects. One solution would be
- // to reference-count them between Java and C++ (so the reference count
- // would always be 0, 1, or 2). Also setField has a similar problem.
- // I plan to fix this when switching away from Java holding pointers to
- // C++ objects as part of the fix for 64-bit support (which we could
- // accomplish inefficiently by changing int to long everywhere, but there
- // are other 64-bit issues to resolve and we need to reduce the number of
- // roots the JS interpreter has to search.
-
- // call Java method
- savedJNIEnv->CallVoidMethod(dispMeth, invokeID, reinterpret_cast<int>(cx),
- reinterpret_cast<int>(jsThis), args,
- reinterpret_cast<int>(jsReturnVal));
-
- JSBool returnValue = JS_TRUE;
-
- if (savedJNIEnv->ExceptionCheck()) {
- tracer.log("dispMeth=%08x", unsigned(dispMeth));
- tracer.setFail("java exception is active:");
- PrintJavaException(savedJNIEnv);
- returnValue = JS_FALSE;
- } else if (JS_IsExceptionPending(cx)) {
- tracer.setFail("js exception is active");
- returnValue = JS_FALSE;
- }
-
- // extract return value
- *rval = jsReturnVal->getValue();
-
-#if 0
- // NOTE: C++ objects are not cleaned up here because Java now owns them.
- // TODO(jat): if reference-counted, they *do* need to be Released here.
-
- // free JsRootedValues
- for (uintN i = 0; i < argc; ++i) {
- delete jsArgs[i];
- }
- delete jsThis;
- delete jsReturnVal;
-#endif
-
- return returnValue;
-}
-
-/*
- * Helper function to get reference Java attributes from Javascript.
- *
- * cx - JSContext pointer
- * obj - JavaScript object which is a wrapped Java object
- * id - property name, as a jsval string
- * dispClass - output parameter of DispatchMethod subclass
- * dispObj - output parameter of Java object
- * jident - output parameter of property name as a Java string
- */
-JSBool getJavaPropertyStats(JSContext *cx, JSObject *obj, jsval id,
- jclass& dispClass, jobject& dispObj, jstring& jident)
-{
- Tracer tracer("getJavaPropertyStats");
- if (!JSVAL_IS_STRING(id)) {
- tracer.setFail("id is not a string");
- return JS_FALSE;
- }
-
- jident = savedJNIEnv->NewString(JS_GetStringChars(JSVAL_TO_STRING(id)),
- JS_GetStringLength(JSVAL_TO_STRING(id)));
- if (!jident || savedJNIEnv->ExceptionCheck()) {
- tracer.setFail("unable to create Java string");
- return JS_FALSE;
- }
-
- dispObj = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
- if (!dispObj) {
- tracer.setFail("can't get dispatch object");
- return JS_FALSE;
- }
-
- dispClass = savedJNIEnv->GetObjectClass(dispObj);
- if (savedJNIEnv->ExceptionCheck()) {
- tracer.setFail("can't get class of dispatch object");
- return JS_FALSE;
- }
-
- return JS_TRUE;
-}
-
-/*
* Class: com_google_gwt_dev_shell_moz_LowLevelMoz
* Method: _executeScriptWithInfo
* Signature: (ILjava/lang/String;Ljava/lang/String;I)Z
*/
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1executeScriptWithInfo
- (JNIEnv* env, jclass llClass, jint scriptObject, jstring code,
+ (JNIEnv* env, jclass llClass, jint scriptObjectInt, jstring code,
jstring file, jint line)
{
Tracer tracer("LowLevelMoz._executeScriptWithInfo");
@@ -409,18 +220,20 @@
return JNI_FALSE;
}
tracer.log("code=%s, file=%s, line=%d", jcode.str(), jfile.str(), line);
+ JSContext* cx = JsRootedValue::currentContext();
+ nsCOMPtr<nsIScriptContext> scriptContext(GetScriptContextFromJSContext(cx));
- nsIScriptGlobalObject* globalObject =
- NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObject);
- nsCOMPtr<nsIScriptContext> scriptContext(globalObject->GetContext());
+ nsIScriptGlobalObject* scriptObject =
+ NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjectInt);
+ JSObject* scriptWindow =
+ reinterpret_cast<JSObject*>(scriptObject->GetGlobalJSObject());
nsXPIDLString scriptString;
scriptString = jcode.jstr();
nsXPIDLString aRetValue;
PRBool aIsUndefined;
- if (NS_FAILED(scriptContext->EvaluateString(scriptString,
- globalObject->GetGlobalJSObject(), 0, jfile.str(), line, 0,
- aRetValue, &aIsUndefined))) {
+ if (NS_FAILED(scriptContext->EvaluateString(scriptString, scriptWindow, 0,
+ jfile.str(), line, 0, aRetValue, &aIsUndefined))) {
tracer.setFail("EvaluateString failed");
return JNI_FALSE;
}
@@ -448,16 +261,9 @@
jint jsArgc = env->GetArrayLength(jsArgsInt);
tracer.log("method=%s, jsthis=%08x, #args=%d", methodStr.str(), jsThisInt,
jsArgc);
-
+ JSContext* cx = JsRootedValue::currentContext();
nsIScriptGlobalObject* scriptObject =
NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
- nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
- if (!scriptContext) {
- tracer.setFail("can't get script context");
- return JNI_FALSE;
- }
- JSContext* cx
- = reinterpret_cast<JSContext*>(scriptContext->GetNativeContext());
JSObject* scriptWindow
= reinterpret_cast<JSObject*>(scriptObject->GetGlobalJSObject());
@@ -519,15 +325,14 @@
/*
* Class: com_google_gwt_dev_shell_moz_LowLevelMoz
* Method: _raiseJavaScriptException
- * Signature: (I)Z
+ * Signature: ()Z
*/
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1raiseJavaScriptException
- (JNIEnv* env, jclass, jint jscontext)
+ (JNIEnv* env, jclass)
{
Tracer tracer("LowLevelMoz._raiseJavaScriptException");
- JSContext* cx = reinterpret_cast<JSContext*>(jscontext);
- JS_SetPendingException(cx, JSVAL_NULL);
+ JS_SetPendingException(JsRootedValue::currentContext(), JSVAL_NULL);
return JNI_TRUE;
}
diff --git a/jni/linux/NativeWrapper.cpp b/jni/linux/NativeWrapper.cpp
index 26014e2..be3963e 100644
--- a/jni/linux/NativeWrapper.cpp
+++ b/jni/linux/NativeWrapper.cpp
@@ -24,16 +24,120 @@
#include "gwt-jni.h"
#include "Tracer.h"
-extern "C" {
+/*
+ * Helper function to get reference Java attributes from Javascript.
+ *
+ * cx - JSContext pointer
+ * obj - JavaScript object which is a wrapped Java object
+ * id - property name, as a jsval string
+ * dispClass - output parameter of DispatchMethod subclass
+ * dispObj - output parameter of Java object
+ * jident - output parameter of property name as a Java string
+ */
+static JSBool getJavaPropertyStats(JSContext *cx, JSObject *obj, jsval id,
+ jclass& dispClass, jobject& dispObj, jstring& jident)
+{
+ Tracer tracer("getJavaPropertyStats");
+ if (!JSVAL_IS_STRING(id)) {
+ tracer.setFail("id is not a string");
+ return JS_FALSE;
+ }
+
+ jident = savedJNIEnv->NewString(JS_GetStringChars(JSVAL_TO_STRING(id)),
+ JS_GetStringLength(JSVAL_TO_STRING(id)));
+ if (!jident || savedJNIEnv->ExceptionCheck()) {
+ tracer.setFail("unable to create Java string");
+ return JS_FALSE;
+ }
+
+ dispObj = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
+ if (!dispObj) {
+ tracer.setFail("can't get dispatch object");
+ return JS_FALSE;
+ }
+
+ dispClass = savedJNIEnv->GetObjectClass(dispObj);
+ if (savedJNIEnv->ExceptionCheck()) {
+ tracer.setFail("can't get class of dispatch object");
+ return JS_FALSE;
+ }
+
+ return JS_TRUE;
+}
+/*
+ * Returns the value of a field on a Java object.
+ *
+ * context - JavaScript context
+ * clazz - class of obj
+ * obj - Java object to retreive field from
+ * fieldName - name of field on Java object to retrieve
+ *
+ * Returns null on failure. Caller is responsible for deleting
+ * returned JsRootedValue when done with it.
+ */
+static JsRootedValue* GetFieldAsRootedValue(JSContext* cx, jclass clazz,
+ jobject obj, jstring fieldName)
+{
+ Tracer tracer("GetFieldAsRootedValue");
+ JsRootedValue::ContextManager context(cx);
+ jmethodID getFieldMeth = savedJNIEnv->GetMethodID(clazz, "getField",
+ "(Ljava/lang/String;I)V");
+ if (!getFieldMeth || savedJNIEnv->ExceptionCheck()) {
+ return 0;
+ }
+
+ JsRootedValue* jsRootedValue = new JsRootedValue();
+ savedJNIEnv->CallVoidMethod(obj, getFieldMeth, fieldName,
+ reinterpret_cast<jint>(jsRootedValue));
+ if (savedJNIEnv->ExceptionCheck()) {
+ delete jsRootedValue;
+ return 0;
+ }
+ return jsRootedValue;
+}
+
+/*
+ * Sets the value of a field on a Java object.
+ *
+ * context - JavaScript context
+ * clazz - class of obj
+ * obj - Java object to store into field
+ * fieldName - name of field on Java object to store into
+ * jsRootedValue - the value to store in the field
+ *
+ * returns true on success, false on failure
+ */
+static bool SetFieldFromRootedValue(JSContext* cx, jclass clazz,
+ jobject obj, jstring fieldName, JsRootedValue* jsRootedValue)
+{
+ Tracer tracer("SetFieldAsRootedValue");
+ JsRootedValue::ContextManager context(cx);
+ jmethodID getFieldMeth = savedJNIEnv->GetMethodID(clazz, "setField",
+ "(Ljava/lang/String;I)V");
+ if (!getFieldMeth || savedJNIEnv->ExceptionCheck()) {
+ return false;
+ }
+
+ savedJNIEnv->CallVoidMethod(obj, getFieldMeth, fieldName,
+ reinterpret_cast<jint>(jsRootedValue));
+ if (savedJNIEnv->ExceptionCheck()) {
+ return false;
+ }
+
+ return true;
+}
+
static JSBool JS_DLL_CALLBACK gwt_nativewrapper_getProperty(JSContext *cx,
JSObject *obj, jsval id, jsval *vp)
{
Tracer tracer("gwt_nativewrapper_getProperty");
tracer.log("context=%08x", unsigned(cx));
-
- if (*vp != JSVAL_VOID)
+ if (*vp != JSVAL_VOID) {
return JS_TRUE;
+ }
+ JsRootedValue::ContextManager context(cx);
+
jclass dispClass;
jobject dispObj;
jstring ident;
@@ -63,22 +167,23 @@
{
Tracer tracer("gwt_nativewrapper_setProperty");
tracer.log("context=%08x", unsigned(cx));
+ JsRootedValue::ContextManager context(cx);
jclass dispClass;
jobject dispObj;
jstring ident;
- if (!getJavaPropertyStats(cx,obj,id,dispClass,dispObj,ident)) {
+ if (!getJavaPropertyStats(cx, obj, id, dispClass, dispObj, ident)) {
tracer.setFail("getJavaPropertyStats failed");
return JS_FALSE;
}
- JsRootedValue* js_rooted_value = new JsRootedValue(cx, *vp);
- if (!SetFieldFromRootedValue(cx, dispClass, dispObj, ident, js_rooted_value)) {
+ JsRootedValue* js_rooted_value = new JsRootedValue(*vp);
+ if (!SetFieldFromRootedValue(cx, dispClass, dispObj, ident,
+ js_rooted_value)) {
tracer.setFail("can't set field");
return JS_FALSE;
}
return JS_TRUE;
}
-
JSClass gwt_nativewrapper_class = {
"gwt_nativewrapper_class", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, gwt_nativewrapper_getProperty,
@@ -93,5 +198,3 @@
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, gwt_nativewrapper_finalize,
JSCLASS_NO_OPTIONAL_MEMBERS
};
-
-} // extern "C"
diff --git a/jni/linux/gwt-jni.h b/jni/linux/gwt-jni.h
index 1b19019..c477f1c 100644
--- a/jni/linux/gwt-jni.h
+++ b/jni/linux/gwt-jni.h
@@ -23,20 +23,10 @@
extern JNIEnv* savedJNIEnv;
extern jclass lowLevelMozClass;
-// JavaScript class objects
-extern "C" JSClass gwt_nativewrapper_class;
-extern "C" JSClass gwt_functionwrapper_class;
+extern nsCID kGwtExternalCID;
-extern jobject NewJsValueMoz(JSContext* context);
-extern jobject NewJsValueMoz(JsRootedValue* js_rooted_value);
-extern JsRootedValue* GetJsRootedValue(jobject jsvalue);
-extern JsRootedValue* GetFieldAsRootedValue(JSContext* context, jclass clazz,
- jobject obj, jstring field_name);
-extern bool SetFieldFromRootedValue(JSContext* context, jclass clazz,
- jobject obj, jstring field_name, JsRootedValue* js_rooted_value);
-extern JSBool getJavaPropertyStats(JSContext *cx, JSObject *obj, jsval id,
- jclass& dispClass, jobject& dispObj, jstring& jident);
-extern JSBool invokeJavaMethod(JSContext *cx, JSObject *obj, uintN argc,
- jsval *argv, jsval *rval);
-
+// JavaScript class objects
+extern JSClass gwt_nativewrapper_class;
+extern JSClass gwt_functionwrapper_class;
+
#endif /*JNI_LINUX_GWT_JNI_H_*/
diff --git a/jni/linux/prebuilt/libgwt-ll.so b/jni/linux/prebuilt/libgwt-ll.so
index 40622ed..48351ee 100755
--- a/jni/linux/prebuilt/libgwt-ll.so
+++ b/jni/linux/prebuilt/libgwt-ll.so
Binary files differ