Fixes issues #472, #627, #736, #739
Refactor hosted mode code and rewrite Mozilla code to properly handle garbage
collection of JavaScript objects held as references within Java objects. The
Mac changes simply adapt the existing Mac code to the new infrastructure, but
do not fix the outstanding memory leak problems -- these will be addressed in
a future patch.
Also added sample eclipse projects for the Mac and Linux JNI code.
Review by: scottb
knorton
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@554 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/jni/linux/ExternalWrapper.cpp b/jni/linux/ExternalWrapper.cpp
new file mode 100644
index 0000000..4f0ae72
--- /dev/null
+++ b/jni/linux/ExternalWrapper.cpp
@@ -0,0 +1,305 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/*
+ * Defines the JavaScript class gwt_external_class, which interface via JNI
+ * to Java objects.
+ */
+
+#include <jni.h>
+#include "JsRootedValue.h"
+#include "gwt-jni.h"
+#include "ExternalWrapper.h"
+#include "Tracer.h"
+#include "JsStringWrap.h"
+
+// note that this does not use the NS_DEFINE_CID macro because it defines
+// the variable as const, which by default has internal linkage. I could
+// work around it by putting extern in front of the macro, but that seems
+// fragile if the macro changes.
+nsCID kGwtExternalCID = GWT_EXTERNAL_FACTORY_CID;
+
+/*
+ * definition of JavaScript gwtOnLoad method which maps to the Java
+ * gwtOnLoad method.
+ */
+static JSBool gwtOnLoad(JSContext *cx, JSObject *obj, uintN argc,
+ jsval *argv, jsval *rval)
+{
+ Tracer tracer("gwtOnLoad");
+ if (argc < 2) {
+ tracer.setFail("less than 2 args");
+ return JS_FALSE;
+ }
+
+ JSObject* scriptWindow = 0;
+ if (argv[0] != JSVAL_NULL && argv[0] != JSVAL_VOID) {
+ if (!JS_ValueToObject(cx, argv[0], &scriptWindow)) {
+ tracer.setFail("can't get script window object");
+ return JS_FALSE;
+ }
+ }
+
+ JSString* moduleName = 0;
+ if (argv[1] != JSVAL_NULL && argv[1] != JSVAL_VOID) {
+ moduleName = JS_ValueToString(cx, argv[1]);
+ }
+
+ nsCOMPtr<nsIScriptGlobalObject> scriptGlobal(0);
+ if (scriptWindow) {
+ nsCOMPtr<nsIXPConnect> xpConnect = do_GetService(nsIXPConnect::GetCID());
+ nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
+ xpConnect->GetWrappedNativeOfJSObject(cx, scriptWindow,
+ getter_AddRefs(wrappedNative));
+ if (wrappedNative) {
+ nsCOMPtr<nsISupports> native;
+ wrappedNative->GetNative(getter_AddRefs(native));
+ scriptGlobal = do_QueryInterface(native);
+ }
+ }
+ jstring jModuleName(0);
+ if (moduleName) {
+ jModuleName = savedJNIEnv->NewString(JS_GetStringChars(moduleName),
+ JS_GetStringLength(moduleName));
+ if (!jModuleName || savedJNIEnv->ExceptionCheck()) {
+ tracer.setFail("can't get module name in Java string");
+ return JS_FALSE;
+ }
+ }
+
+ jobject externalObject = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
+ jclass objClass = savedJNIEnv->GetObjectClass(externalObject);
+ if (!objClass || savedJNIEnv->ExceptionCheck()) {
+ tracer.setFail("can't get LowLevelMoz.ExternalObject class");
+ return JS_FALSE;
+ }
+
+ jmethodID methodID = savedJNIEnv->GetMethodID(objClass, "gwtOnLoad",
+ "(ILjava/lang/String;)Z");
+ if (!methodID || savedJNIEnv->ExceptionCheck()) {
+ tracer.setFail("can't get gwtOnLoad method");
+ return JS_FALSE;
+ }
+
+ jboolean result = savedJNIEnv->CallBooleanMethod(externalObject, methodID,
+ NS_REINTERPRET_CAST(jint, scriptGlobal.get()), jModuleName);
+ if (savedJNIEnv->ExceptionCheck()) {
+ tracer.setFail("LowLevelMoz.ExternalObject.gwtOnLoad() threw an exception");
+ return JS_FALSE;
+ }
+ *rval = BOOLEAN_TO_JSVAL((result == JNI_FALSE) ? JS_FALSE : JS_TRUE);
+ return JS_TRUE;
+}
+
+/*=======================================================================*/
+/* Implementation of the getProperty, setProperty, and finalize methods */
+/* for the JavaScript class gwt_external_class. */
+/*=======================================================================*/
+
+static JSBool JS_DLL_CALLBACK gwt_external_getProperty(JSContext *cx,
+ JSObject *obj, jsval id, jsval *vp)
+{
+ Tracer tracer("gwt_external_getProperty");
+ if (*vp != JSVAL_VOID)
+ return JS_TRUE;
+
+ if (!JSVAL_IS_STRING(id)) {
+ tracer.setFail("not a string");
+ return JS_FALSE;
+ }
+ JsStringWrap jsStr(cx, id);
+ tracer.log("obj=%08x, property=%.*s", obj, jsStr.length(), jsStr.bytes());
+
+// All this is for calling resolveReference which only returns void. So,
+// just replace this code to return void for now. TODO(jat): revisit when
+// merging in Toby's code.
+#if 0
+ jstring jident = savedJNIEnv->NewString(jsStr.chars(), jsStr.length());
+ if (!jident || savedJNIEnv->ExceptionCheck()) {
+ tracer.setFail("can't create Java string ");
+ return JS_FALSE;
+ }
+ jobject externalObject = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
+ jclass objClass = savedJNIEnv->GetObjectClass(externalObject);
+ if (!objClass || savedJNIEnv->ExceptionCheck()) {
+ tracer.setFail("can't find Java class for JS object");
+ return JS_FALSE;
+ }
+
+ jmethodID methodID = savedJNIEnv->GetMethodID(objClass, "resolveReference",
+ "(Ljava/lang/String;)I");
+ if (!methodID || savedJNIEnv->ExceptionCheck()) {
+ tracer.setFail("exception getting method for int resolveReference(String)");
+ return JS_FALSE;
+ }
+ int retval = savedJNIEnv->CallIntMethod(externalObject, methodID, jident);
+ if (savedJNIEnv->ExceptionCheck()) {
+ tracer.setFail("exception calling int resolveReference(String)");
+ return JS_FALSE;
+ }
+ *vp = retval;
+#else
+ *vp = JSVAL_VOID;
+#endif
+ return JS_TRUE;
+}
+
+static void JS_DLL_CALLBACK gwt_external_finalize(JSContext *cx, JSObject *obj)
+{
+ jobject externalObject = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
+ if (externalObject)
+ savedJNIEnv->DeleteGlobalRef(externalObject);
+ JS_FinalizeStub(cx,obj);
+}
+
+static JSBool JS_DLL_CALLBACK gwt_external_setProperty(JSContext *cx,
+ JSObject *obj, jsval id, jsval *vp)
+{
+ return JS_FALSE;
+}
+
+static JSClass gwt_external_class = {
+ "gwt_external_class", JSCLASS_HAS_PRIVATE,
+ JS_PropertyStub, JS_PropertyStub, gwt_external_getProperty,
+ gwt_external_setProperty, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,
+ gwt_external_finalize,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+NS_IMPL_ISUPPORTS1(ExternalWrapper, nsIScriptObjectOwner)
+
+/*
+ * Create a new LowLevelMoz.ExternalObject instance and a JavaScript object
+ * that refers to it, mapping the gwtOnLoad member function mapping to
+ * a Javascript method.
+ */
+NS_IMETHODIMP ExternalWrapper::GetScriptObject(nsIScriptContext *aContext,
+ void** aScriptObject)
+{
+ Tracer tracer("ExternalWrapper::GetScriptObject");
+ if (!aScriptObject) {
+ tracer.setFail("null script object pointer");
+ return NS_ERROR_INVALID_POINTER;
+ }
+ if (!mScriptObject) {
+ *aScriptObject = 0;
+
+ nsIScriptGlobalObject* globalObject = aContext->GetGlobalObject();
+ nsCOMPtr<nsIDOMWindow> domWindow(do_QueryInterface(globalObject));
+ if (!domWindow) {
+ tracer.setFail("can't get DOM window");
+ return NS_ERROR_UNEXPECTED;
+ }
+ nsCOMPtr<nsIDOMWindow> topWindow;
+ domWindow->GetTop(getter_AddRefs(topWindow));
+ if (!topWindow) {
+ tracer.setFail("Can't get top window");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ tracer.log("savedJNIEnv=%08x, llClass=%08x", unsigned(savedJNIEnv),
+ lowLevelMozClass);
+
+ jmethodID methodID = savedJNIEnv->GetStaticMethodID(lowLevelMozClass,
+ "createExternalObjectForDOMWindow",
+ "(I)Lcom/google/gwt/dev/shell/moz/LowLevelMoz$ExternalObject;");
+ if (!methodID || savedJNIEnv->ExceptionCheck()) {
+ tracer.setFail("Can't get createExternalObjectForDOMWindow method");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ jobject externalObject = savedJNIEnv->CallStaticObjectMethod(
+ lowLevelMozClass, methodID, NS_REINTERPRET_CAST(jint, topWindow.get()));
+ if (!externalObject || savedJNIEnv->ExceptionCheck()) {
+ tracer.setFail("createExternalObjectForDOMWindow failed");
+ return NS_ERROR_UNEXPECTED;
+ }
+ externalObject = savedJNIEnv->NewGlobalRef(externalObject);
+ if (!externalObject || savedJNIEnv->ExceptionCheck()) {
+ 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) {
+ tracer.setFail("can't create new gwt_external_class object");
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ if (!JS_SetPrivate(cx, newObj, externalObject)) {
+ savedJNIEnv->DeleteGlobalRef(externalObject);
+ tracer.setFail("can't store external object private reference");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (!JS_DefineFunction(cx, newObj, "gwtOnLoad", gwtOnLoad, 3,
+ JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) {
+ tracer.setFail("can't define gwtOnLoad function on JavaScript object");
+ return NS_ERROR_UNEXPECTED;
+ }
+ mScriptObject = newObj;
+ }
+
+ *aScriptObject = mScriptObject;
+ return NS_OK;
+}
+
+NS_IMETHODIMP ExternalWrapper::SetScriptObject(void* aScriptObject)
+{
+ mScriptObject = aScriptObject;
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS1(nsRpExternalFactory, nsIFactory)
+
+NS_IMETHODIMP nsRpExternalFactory::CreateInstance(nsISupports *aOuter,
+ const nsIID& aIID, void** aResult)
+{
+ Tracer tracer("nsRpExternalFactory::CreateInstance");
+ if (!aResult) {
+ tracer.setFail("null pointer for return value");
+ return NS_ERROR_INVALID_POINTER;
+ }
+ *aResult = NULL;
+
+ if (aOuter) {
+ tracer.setFail("aOuter is not null");
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ nsISupports* object = new ExternalWrapper();
+ if (!object) {
+ tracer.setFail("can't create a new ExternalWrapper");
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsresult result = object->QueryInterface(aIID, aResult);
+ if (!*aResult || NS_FAILED(result)) {
+ tracer.setFail("ExternalWrapper::QueryInterface failed");
+ delete object;
+ }
+ return result;
+}
+
+NS_IMETHODIMP nsRpExternalFactory::LockFactory(PRBool lock)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
diff --git a/jni/linux/ExternalWrapper.h b/jni/linux/ExternalWrapper.h
new file mode 100644
index 0000000..7cabc23
--- /dev/null
+++ b/jni/linux/ExternalWrapper.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2006 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#ifndef EXTERNALWRAPPER_H_
+#define EXTERNALWRAPPER_H_
+
+class ExternalWrapper : public nsIScriptObjectOwner {
+public:
+ NS_DECL_ISUPPORTS
+ NS_IMETHOD GetScriptObject(nsIScriptContext *aContext, void** aScriptObject);
+ NS_IMETHOD SetScriptObject(void* aScriptObject);
+ ExternalWrapper(): mScriptObject(0) { }
+private:
+ ~ExternalWrapper() { }
+ void *mScriptObject;
+};
+
+class nsRpExternalFactory : public nsIFactory {
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIFACTORY
+ nsRpExternalFactory() { }
+private:
+ ~nsRpExternalFactory() { }
+};
+
+#define GWT_EXTERNAL_FACTORY_CID \
+{ 0xF56E23F8, 0x5D06, 0x47F9, \
+{ 0x88, 0x5A, 0xD9, 0xCA, 0xC3, 0x38, 0x41, 0x7F } }
+#define GWT_EXTERNAL_CONTRACTID "@com.google/GWT/external;1"
+
+#endif /*EXTERNALWRAPPER_H_*/
diff --git a/jni/linux/JStringWrap.h b/jni/linux/JStringWrap.h
new file mode 100644
index 0000000..5243776
--- /dev/null
+++ b/jni/linux/JStringWrap.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#ifndef JNI_LINUX_JSTRINGWRAP_H_
+#define JNI_LINUX_JSTRINGWRAP_H_
+
+/*
+ * Wrapper arouond Java Strings, keeps pointers to unpacked strings
+ * and makes sure they are cleaned up.
+ */
+class JStringWrap {
+ public:
+ JStringWrap(JNIEnv* env, jstring str): env(env), s(str), p(0), jp(0) { }
+ ~JStringWrap() {
+ if (p) env->ReleaseStringUTFChars(s, p);
+ if (jp) env->ReleaseStringChars(s, jp);
+ }
+ const char* str() {
+ if (!p) p = env->GetStringUTFChars(s, 0);
+ return p;
+ }
+ const jchar* jstr() {
+ if (!jp) jp = env->GetStringChars(s, 0);
+ return jp;
+ }
+ private:
+ JNIEnv* env;
+ jstring s;
+ const char* p;
+ const jchar* jp;
+};
+
+
+#endif // JNI_LINUX_JSTRINGWRAP_H_
diff --git a/jni/linux/JsRootedValue.cpp b/jni/linux/JsRootedValue.cpp
new file mode 100644
index 0000000..08dd7cb
--- /dev/null
+++ b/jni/linux/JsRootedValue.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include "JsRootedValue.h"
+
+// intialize static value used to hold the JavaScript String class.
+JSClass* JsRootedValue::stringClass = 0;
+
+/*
+ * Actually get the stringClass pointer from JavaScript.
+ */
+void JsRootedValue::fetchStringClass() const {
+ Tracer tracer("JsRootedValue::fetchStringClass");
+ jsval val = JS_GetEmptyStringValue(context_);
+ JSObject* obj;
+ // on error, leave stringClass null
+ if (!JS_ValueToObject(context_, val, &obj)) return;
+ if (!obj) {
+ tracer.log("ensureStringClass: null object");
+ return;
+ }
+ stringClass = JS_GET_CLASS(context_, obj);
+ tracer.log("stringClass=%08x", unsigned(stringClass));
+}
diff --git a/jni/linux/JsRootedValue.h b/jni/linux/JsRootedValue.h
new file mode 100644
index 0000000..663200e
--- /dev/null
+++ b/jni/linux/JsRootedValue.h
@@ -0,0 +1,385 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+#ifndef JNI_LINUX_JSROOTEDVALUE_H_
+#define JNI_LINUX_JSROOTEDVALUE_H_
+
+// Mozilla header files
+#include "mozilla-headers.h"
+
+#include "Tracer.h"
+
+extern JSClass gwt_nativewrapper_class;
+
+/*
+ * Holds a root for Javascript objects, so the JS interpreter knows not to
+ * garbage-collect the underlying object as long as this object exists.
+ * Java code will pass a pointer to this object around (as an int/long) for
+ * referring to the underlying Javascript object.
+ *
+ * There are also convenience routines for manipulating the underlying value.
+ * Note that all get* methods assume the type is correct, so the corresponding
+ * is* method should be called first if you aren't sure of the type.
+ *
+ * See http://developer.mozilla.org/en/docs/JS_AddRoot for details.
+ *
+ * TODO(jat): handle unboxing Javascript objects like Boolean/etc.
+ * TODO(jat): rewrite this to minimize the number of roots held and to
+ * improve 64-bit compatibility.
+ */
+class JsRootedValue
+{
+private:
+ // the JavaScript String class
+ static JSClass* stringClass;
+
+ // Javascript context
+ JSContext* context_;
+ // underlying Javascript value
+ jsval value_;
+
+protected:
+ /*
+ * Fetch the JavaScript String class.
+ * Not inlined to minimize code bloat since it should only be called once.
+ */
+ void fetchStringClass() const;
+
+ /*
+ * Make sure we have the JS code to identify String objects installed.
+ */
+ void ensureStringClass() const {
+ if(stringClass) return;
+ fetchStringClass();
+ }
+
+ /*
+ * Make this value rooted if the current value is a pointer type
+ *
+ * Returns false if an error occurred.
+ */
+ bool setRoot() {
+ Tracer tracer("JsRootedValue::setRoot", this);
+ tracer.log("context=%08x, value=%08x", context_, value_);
+ if(JSVAL_IS_GCTHING(value_)) {
+ tracer.log("value is GC - %s", JS_GetTypeName(context_,
+ JS_TypeOfValue(context_, value_)));
+ bool returnVal = JS_AddRoot(context_, &value_);
+ if (!returnVal) {
+ tracer.log("*** JS_AddRoot failed");
+ }
+ return returnVal;
+ }
+ return true;
+ }
+ /*
+ * Remove this value from the roots list if it was there
+ *
+ * Returns false if an error occurred (note that currently JS_RemoveRoot
+ * is documented to always return true, so this is not currently possible).
+ */
+ bool removeRoot() {
+ Tracer tracer("JsRootedValue::removeRoot", this);
+ tracer.log("context=%08x, value=%08x", context_, value_);
+ if(JSVAL_IS_GCTHING(value_)) {
+ tracer.log("value is GC - %s", JS_GetTypeName(context_,
+ JS_TypeOfValue(context_, value_)));
+ bool returnVal = JS_RemoveRoot(context_, &value_);
+ if (!returnVal) {
+ tracer.log("*** JS_RemoveRoot failed");
+ }
+ return returnVal;
+ }
+ return true;
+ }
+
+public:
+ /*
+ * 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_)
+ {
+ Tracer tracer("JsRootedValue copy constr", this);
+ tracer.log("other jsRootedVal=%08x", reinterpret_cast<unsigned>(&rooted_value));
+ if (!JS_AddRoot(context_, &value_)) {
+ tracer.log("JsRootedValue copy constructor: JS_AddRoot failed");
+ // TODO(jat): handle errors
+ }
+ }
+
+ JsRootedValue(JSContext* context, jsval value) : context_(context),
+ value_(value)
+ {
+ Tracer tracer("JsRootedValue jsval constr", this);
+ tracer.log("jsval=%08x", value);
+ if (!JS_AddRoot(context_, &value_)) {
+ tracer.log("JsRootedValue jsval constructor: JS_AddRoot failed");
+ // TODO(jat): handle errors
+ }
+ }
+
+ /*
+ * Create a void value - safe since no errors can occur
+ */
+ JsRootedValue(JSContext* context) : context_(context), value_(JSVAL_VOID) { }
+
+ /*
+ * Destroy this object.
+ */
+ virtual ~JsRootedValue() {
+ Tracer tracer("~JsRootedValue", this);
+ // ignore error since currently it is not possible to fail
+ JS_RemoveRoot(context_, &value_);
+ }
+
+ /*
+ * Return the JSContext* pointer.
+ */
+ JSContext* getContext() const { return context_; }
+
+ /*
+ * Return the global object for this value's context.
+ */
+ JSObject* getGlobalObject() const { return JS_GetGlobalObject(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.
+ */
+ bool setValue(jsval new_value) {
+ value_ = new_value;
+ return true;
+ }
+
+ /*
+ * Returns true if the underlying value is of some number type.
+ */
+ bool isNumber() const {
+ return JSVAL_IS_NUMBER(value_);
+ }
+ /*
+ * Returns the underlying value as a double.
+ * Result is 0.0 if the underlying value is not a number
+ * type.
+ */
+ double getDouble() const {
+ jsdouble return_value=0.0;
+ void(JS_ValueToNumber(context_, value_, &return_value));
+ return double(return_value);
+ }
+ /*
+ * Set the underlying value to a double value.
+ *
+ * Returns false on failure.
+ */
+ bool setDouble(double val) {
+ jsval js_double;
+ if(!JS_NewDoubleValue(context_, jsdouble(val), &js_double)) {
+ return false;
+ }
+ return setValue(js_double);
+ }
+
+ /*
+ * Returns the underlying value as an integer value. Note that the result
+ * is undefined if isInt() does not return true.
+ */
+ int getInt() {
+ return JSVAL_TO_INT(value_);
+ }
+
+ /*
+ * Set the underlying value to an integer value.
+ *
+ * Returns false on failure.
+ */
+ bool setInt(int val) {
+ return setValue(INT_TO_JSVAL(val));
+ }
+
+ /*
+ * Returns true if the underlying value is a boolean.
+ */
+ bool isBoolean() const {
+ return JSVAL_IS_BOOLEAN(value_);
+ }
+ /*
+ * Returns the underlying value as a boolean.
+ * Result is undefined if the value is not actually
+ * a boolean.
+ */
+ bool getBoolean() const {
+ return value_ != JSVAL_FALSE;
+ }
+ /*
+ * Set the underlying value to a boolean value.
+ *
+ * Returns false on failure (impossible?).
+ */
+ bool setBoolean(bool val) {
+ return setValue(val ? JSVAL_TRUE : JSVAL_FALSE);
+ }
+
+ /*
+ * Returns true if the underlying value is a string.
+ */
+ bool isInt() const {
+ return JSVAL_IS_INT(value_);
+ }
+
+ /*
+ * Returns true if the underlying value is a string.
+ */
+ bool isString() const {
+ return JSVAL_IS_STRING(value_);
+ }
+
+ /*
+ * Check if the value is a JavaScript String object.
+ */
+ bool isJavaScriptStringObject() const {
+ if (!isObject()) return false;
+ ensureStringClass();
+ return getObjectClass() == stringClass;
+ }
+
+ /*
+ * Return this value as a string, converting as necessary.
+ */
+ JSString* asString() const {
+ return JS_ValueToString(context_, value_);
+ }
+
+ /* Returns the string as a JSString pointer.
+ * Result is undefined if the value is not actually a string or String object.
+ */
+ const JSString* getString() const {
+ if (JSVAL_IS_STRING(value_)) {
+ return JSVAL_TO_STRING(value_);
+ }
+ return asString();
+ }
+ /*
+ * Returns the string as a zero-terminated array of UTF16 characters.
+ * Note that this pointer may become invalid when JS performs GC, so it
+ * may only be used without calling other JS functions. Result is
+ * undefined if the value is not actually a string.
+ */
+ const wchar_t* getStringChars() const {
+ return reinterpret_cast<const wchar_t*>(JS_GetStringChars(
+ const_cast<JSString*>(getString())));
+ }
+
+ /*
+ * Returns the length of the underlying string. Result is undefined
+ * if the value is not actually a string.
+ */
+ int getStringLength() const {
+ return JS_GetStringLength(const_cast<JSString*>(getString()));
+ }
+
+ /*
+ * Sets the underlying value, defined by a null-terminated array of UTF16 chars.
+ *
+ * Returns false on failure.
+ */
+ bool setString(const wchar_t* utf16) {
+ JSString* str = JS_NewUCStringCopyZ(context_, reinterpret_cast<const jschar*>(utf16));
+ return setValue(STRING_TO_JSVAL(str));
+ }
+
+ /*
+ * Returns true if the underlying value is an object.
+ */
+ bool isObject() const {
+ return JSVAL_IS_OBJECT(value_);
+ }
+
+ /*
+ * Returns the underlying value as an object.
+ * Result is undefined if it is not actually an object.
+ */
+ JSObject* getObject() const {
+ return isObject() ? JSVAL_TO_OBJECT(value_) : 0;
+ }
+
+ /*
+ * Returns the class name of the underlying value.
+ * Result is undefined if it is not actually an object.
+ */
+ const JSClass* getObjectClass() const {
+ return isObject() ? JS_GET_CLASS(context_, getObject()) : 0;
+ }
+
+ /*
+ * Sets the underlying value to be an object.
+ * Returns false on failure.
+ */
+ bool setObject(JSObject* obj) {
+ return setValue(OBJECT_TO_JSVAL(obj));
+ }
+
+ /*
+ * Returns true if the underlying value is undefined (void).
+ */
+ bool isUndefined() const {
+ return JSVAL_IS_VOID(value_);
+ }
+
+ /*
+ * Sets the underlying value to be undefined (void).
+ *
+ * Returns false on failure (impossible?)
+ */
+ bool setUndefined() {
+ return setValue(JSVAL_VOID);
+ }
+
+ /*
+ * Returns true if the underlying value is null.
+ */
+ bool isNull() const {
+ return JSVAL_IS_NULL(value_);
+ }
+ /*
+ * Sets the underlying value to be null.
+ *
+ * Returns false on failure (impossible?)
+ */
+ bool setNull() {
+ return setValue(JSVAL_NULL);
+ }
+};
+
+#endif /*JNI_LINUX_JSROOTEDVALUE_H_*/
diff --git a/jni/linux/JsStringWrap.h b/jni/linux/JsStringWrap.h
new file mode 100644
index 0000000..7ab0ce6
--- /dev/null
+++ b/jni/linux/JsStringWrap.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#ifndef JNI_LINUX_JSSTRINGWRAP_H_
+#define JNI_LINUX_JSSTRINGWRAP_H_
+
+/*
+ * Wrapper arouond JavaScript Strings, keeps pointers to unpacked strings
+ * and makes sure they are not cleaned up early.
+ */
+class JsStringWrap {
+ private:
+ JSContext* context_;
+ JSString* string_;
+ const char* bytes_;
+ const wchar_t* chars_;
+
+ public:
+ JsStringWrap(JSContext* context, JSString* str)
+ : context_(context), string_(str), bytes_(0), chars_(0) {
+ JS_AddRoot(context_, &string_);
+ JS_AddRoot(context_, &bytes_);
+ JS_AddRoot(context_, &chars_);
+ }
+ JsStringWrap(JSContext* context, jsval str)
+ : context_(context), string_(JSVAL_TO_STRING(str)), bytes_(0), chars_(0) {
+ JS_AddRoot(context_, &string_);
+ JS_AddRoot(context_, &bytes_);
+ JS_AddRoot(context_, &chars_);
+ }
+ ~JsStringWrap() {
+ JS_RemoveRoot(context_, &string_);
+ JS_RemoveRoot(context_, &bytes_);
+ JS_RemoveRoot(context_, &chars_);
+ }
+ const char* bytes() {
+ if (!bytes_) bytes_ = JS_GetStringBytes(string_);
+ return bytes_;
+ }
+ const wchar_t* chars() {
+ if (!chars_) {
+ chars_ = reinterpret_cast<wchar_t*>(JS_GetStringChars(string_));
+ }
+ return chars_;
+ }
+ int length() {
+ return JS_GetStringLength(string_);
+ }
+};
+
+#endif // JNI_LINUX_JSSTRINGWRAP_H_
diff --git a/jni/linux/JsValueMoz.cpp b/jni/linux/JsValueMoz.cpp
new file mode 100644
index 0000000..b383a37
--- /dev/null
+++ b/jni/linux/JsValueMoz.cpp
@@ -0,0 +1,814 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include <jni.h>
+#include "JsRootedValue.h"
+#include "gwt-jni.h"
+#include "ExternalWrapper.h"
+#include "Tracer.h"
+
+// include javah-generated header to make sure things match
+#include "JsValueMoz.h"
+
+/*
+ * 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.
+ */
+JsRootedValue* GetFieldAsRootedValue(JSContext* context, jclass clazz,
+ jobject obj, jstring fieldName)
+{
+ jmethodID getFieldMeth = savedJNIEnv->GetMethodID(clazz, "getField",
+ "(Ljava/lang/String;I)V");
+ if (!getFieldMeth || savedJNIEnv->ExceptionCheck())
+ return 0;
+
+ JsRootedValue* jsRootedValue = new JsRootedValue(context);
+ 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
+ */
+bool SetFieldFromRootedValue(JSContext* context, jclass clazz,
+ jobject obj, jstring fieldName, JsRootedValue* jsRootedValue)
+{
+ 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;
+}
+
+/*
+ * Throws a HostedModeException with the specified message.
+ */
+static void ThrowHostedModeException(JNIEnv* jniEnv, const char* msg) {
+ jclass exceptionClass
+ = jniEnv->FindClass("com/google/gwt/dev/shell/HostedModeException");
+ jniEnv->ThrowNew(exceptionClass, msg);
+}
+
+
+enum JsValueType {
+ JSVAL_TYPE_VOID=0,
+ JSVAL_TYPE_NULL,
+ JSVAL_TYPE_BOOLEAN,
+ JSVAL_TYPE_NUMBER,
+ JSVAL_TYPE_STRING,
+ JSVAL_TYPE_OBJECT,
+ JSVAL_TYPE_UNKNOWN,
+};
+
+static const char* JsValueTypeStrings[]={
+ "undefined",
+ "null",
+ "boolean",
+ "number",
+ "string",
+ "object",
+ "unknown",
+};
+
+static JsValueType GetValueType(jsval val) {
+ if(JSVAL_IS_VOID(val)) {
+ return JSVAL_TYPE_VOID;
+ } else if(JSVAL_IS_NULL(val)) {
+ return JSVAL_TYPE_NULL;
+ } else if(JSVAL_IS_BOOLEAN(val)) {
+ return JSVAL_TYPE_BOOLEAN;
+ } else if(JSVAL_IS_NUMBER(val)) {
+ return JSVAL_TYPE_NUMBER;
+ } else if(JSVAL_IS_STRING(val)) {
+ return JSVAL_TYPE_STRING;
+ } else if(JSVAL_IS_OBJECT(val)) {
+ return JSVAL_TYPE_OBJECT;
+ } else {
+ return JSVAL_TYPE_UNKNOWN;
+ }
+}
+
+/*
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _createJsRootedValue()
+ * Signature: (II)I
+ */
+extern "C" JNIEXPORT jint JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1createJsRootedValue
+ (JNIEnv* jniEnv, jclass, jint scriptObjInt, 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);
+ return NS_REINTERPRET_CAST(jint, jsRootedValue);
+}
+
+/*
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _copyJsRootedValue()
+ * Signature: (I)I
+ */
+extern "C" JNIEXPORT jint JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1copyJsRootedValue
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+ const JsRootedValue* jsRootedValue = reinterpret_cast<const JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._copyJsRootedValue", jsRootedValue);
+ JsRootedValue* newRootedValue = new JsRootedValue(*jsRootedValue);
+ return NS_REINTERPRET_CAST(jint, newRootedValue);
+}
+
+/*
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _destroyJsRootedValue()
+ * Signature: (I)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1destroyJsRootedValue
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._destroyJsRootedValue", jsRootedValue);
+ delete jsRootedValue;
+}
+
+/*
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _getBoolean()
+ * Signature: (I)Z
+ *
+ * TODO(jat): unboxing Javascript Boolean type?
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getBoolean
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._getBoolean", jsRootedValue);
+ return jsRootedValue->getBoolean();
+}
+
+/**
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _getInt()
+ * 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
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._getInt", jsRootedValue);
+ int val = jsRootedValue->getInt();
+ tracer.log("value=%d", val);
+ return val;
+}
+
+/**
+ * Return a Javascript number as a Java double.
+ *
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _getNumber()
+ * Signature: (I)D
+ */
+extern "C" JNIEXPORT jdouble JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getNumber
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._getNumber", jsRootedValue);
+ return jsRootedValue->getDouble();
+}
+
+/**
+ * Return a Javascript string as a Java string.
+ *
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _getString()
+ * Signature: (I)Ljava/lang/String;
+ *
+ * Note that this relies on jschar being assignment compatible with jchar
+ */
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getString
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._getString", jsRootedValue);
+ const JSString* str = jsRootedValue->getString();
+ int len = JS_GetStringLength(const_cast<JSString*>(str));
+ jstring javaStr =jniEnv->NewString(reinterpret_cast<const jchar*>(
+ JS_GetStringChars(const_cast<JSString*>(str))), len);
+ return javaStr;
+}
+
+/*
+ * Returns a human-readable Java string describing the type of a
+ * JavaScript object.
+ *
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _getTypeString
+ * Signature: (I)Ljava/lang/String;
+ */
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getTypeString
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._getTypeString", jsRootedValue);
+ jsval val = jsRootedValue->getValue();
+ JSContext* context = jsRootedValue->getContext();
+ 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,
+ &gwt_nativewrapper_class, 0)) {
+ typeString = "Java object";
+ } else {
+ snprintf(buf, sizeof(buf), "class %s", objClass->name);
+ typeString = buf;
+ }
+ } else {
+ typeString = JsValueTypeStrings[valueType];
+ }
+ jstring returnValue = jniEnv->NewStringUTF(typeString);
+ return returnValue;
+}
+
+/*
+ * Unwraps a wrapped Java object from a JS object.
+ *
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _getWrappedJavaObject
+ * Signature: (I)Ljava/lang/Object;
+ */
+extern "C" JNIEXPORT jobject JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getWrappedJavaObject
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._getWrappedJavaObject", jsRootedValue);
+ jsval val = jsRootedValue->getValue();
+ if(!JSVAL_IS_OBJECT(val)) {
+ tracer.throwHostedModeException(jniEnv, "Javascript value not an object");
+ return 0;
+ }
+ JSObject* jsObject = JSVAL_TO_OBJECT(val);
+ JSContext* context = jsRootedValue->getContext();
+ if(!JS_InstanceOf(context, 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));
+ return javaObject;
+}
+
+/*
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _isBoolean()
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isBoolean
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._isBoolean", jsRootedValue);
+ return jsRootedValue->isBoolean();
+}
+
+/*
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _isInt()
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isInt
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._isBoolean", jsRootedValue);
+ return jsRootedValue->isInt();
+}
+
+/*
+ * Checks if a JS object is a JavaScript object.
+ *
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _isJavaScriptObject
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isJavaScriptObject
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._isJavaScriptObject", jsRootedValue);
+ jsval val = jsRootedValue->getValue();
+ bool returnValue = false;
+ if(JSVAL_IS_OBJECT(val)) {
+ JSObject* jsObject = JSVAL_TO_OBJECT(val);
+ returnValue = !JS_InstanceOf(jsRootedValue->getContext(), jsObject,
+ &gwt_nativewrapper_class, 0);
+ tracer.log("jsobject=%08x, isJSObject=%s", unsigned(jsObject),
+ returnValue ? "true" : "false");
+ } else {
+ tracer.log("not an object");
+ }
+ return returnValue;
+}
+
+/*
+ * Checks if a JS object is a JavaScript String object.
+ *
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _isJavaScriptString
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isJavaScriptString
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._isJavaScriptString", jsRootedValue);
+ bool returnValue = jsRootedValue->isJavaScriptStringObject();
+ tracer.log("value=%s", returnValue ? "true" : "false");
+ return returnValue;
+}
+
+/*
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _isNull()
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isNull
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._isNull", jsRootedValue);
+ return jsRootedValue->isNull();
+}
+
+/*
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _isNumber()
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isNumber
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._isNumber", jsRootedValue);
+ return jsRootedValue->isNumber();
+}
+
+/*
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _isString()
+ * Signature: (I)Z
+ *
+ * Handles the case of JavaScript String objects as well
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isString
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._isString", jsRootedValue);
+ return jsRootedValue->isString();
+}
+
+/*
+ * Checks if a JS object is undefined (void)
+ *
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _isUndefined
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isUndefined
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._isUndefined", jsRootedValue);
+ return jsRootedValue->isUndefined();
+}
+
+/*
+ * Checks if a JS object is a wrapped Java object.
+ *
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _isWrappedJavaObject
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isWrappedJavaObject
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._isWrappedJavaObject", jsRootedValue);
+ jsval val = jsRootedValue->getValue();
+ bool returnValue = false;
+ if(JSVAL_IS_OBJECT(val)) {
+ JSObject* jsObject = JSVAL_TO_OBJECT(val);
+ returnValue = JS_InstanceOf(jsRootedValue->getContext(), jsObject,
+ &gwt_nativewrapper_class, 0);
+ tracer.log("jsobject=%08x, wrappedJava=%s", unsigned(jsObject),
+ returnValue ? "true" : "false");
+ } else {
+ tracer.log("not an object");
+ }
+ return returnValue;
+}
+
+/*
+ * Set the JavaScript value to be a boolean of the supplied value.
+ *
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _setBoolean()
+ * Signature: (IZ)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setBoolean
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jboolean val)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._setBoolean", jsRootedValue);
+ jsRootedValue->setBoolean(val == JNI_TRUE);
+ return;
+}
+
+/*
+ * Set the JavaScript value to be a double of the supplied value.
+ *
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _setDouble()
+ * Signature: (ID)V
+ */
+extern "C" JNIEXPORT void
+JNICALL Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setDouble
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jdouble val)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._setDouble", jsRootedValue);
+ if(!jsRootedValue->setDouble(val)) {
+ tracer.throwHostedModeException(jniEnv, "Unable to allocate JS double");
+ return;
+ }
+}
+
+/*
+ * Set the Javascript value to be an integer.
+ *
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _setInt()
+ * Signature: (II)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setInt
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jint val)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._setInt", jsRootedValue);
+ tracer.log("val=%d", val);
+ jsRootedValue->setInt(val);
+}
+
+/*
+ * Set the Javascript value to be another JsRootedValue's value.
+ *
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _setJsRootedValue()
+ * Signature: (II)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setJsRootedValue
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jint jsOtherRootedValueInt)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ JsRootedValue* jsOtherRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsOtherRootedValueInt);
+ Tracer tracer("JsValueMoz._setJsRootedValue", jsRootedValue);
+ jsRootedValue->setContextValue(jsOtherRootedValue->getContext(),
+ jsOtherRootedValue->getValue());
+}
+
+/*
+ * Set the JavaScript value to be null.
+ *
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _setNull()
+ * Signature: (I)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setNull
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._setNull", jsRootedValue);
+ jsRootedValue->setNull();
+}
+
+/*
+ * Set the JavaScript value to be a string of the supplied value.
+ *
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _setString()
+ * Signature: (ILjava/lang/String;)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setString
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jstring val)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._setString", jsRootedValue);
+ JStringWrap strVal(jniEnv, val);
+ const jchar* stringUTF16 = strVal.jstr();
+ if(!stringUTF16) {
+ tracer.throwHostedModeException(jniEnv, "Unable to retrieve Java string");
+ return;
+ }
+ tracer.log("string=%s", strVal.str());
+ if(!jsRootedValue->setString(reinterpret_cast<const wchar_t*>(stringUTF16))) {
+ tracer.throwHostedModeException(jniEnv, "Unable to allocate JS string");
+ return;
+ }
+}
+
+/*
+ * Set the JavaScript value to be undefined (void).
+ *
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _setUndefined()
+ * Signature: (I)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setUndefined
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._setUndefined", jsRootedValue);
+ jsRootedValue->setUndefined();
+}
+
+/*
+ * Wraps a Java object in a JS object.
+ *
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _setWrappedJavaObject
+ * Signature: (ILjava/lang/Object)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setWrappedJavaObject
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jobject obj)
+{
+ 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,
+ scriptWindow);
+ if (!newObj) {
+ tracer.throwHostedModeException(jniEnv,
+ "Unable to allocate JS object to wrap Java object");
+ return;
+ }
+ tracer.log("jsobject=%08x", unsigned(newObj));
+
+ // TODO(jat): how does this globalref get freed?
+ jobject dispObjRef = jniEnv->NewGlobalRef(obj);
+ if (!dispObjRef || jniEnv->ExceptionCheck()) {
+ tracer.throwHostedModeException(jniEnv,
+ "Unable to allocate global reference for JS wrapper");
+ return;
+ }
+ if (!JS_SetPrivate(context, newObj, dispObjRef)) {
+ jniEnv->DeleteGlobalRef(dispObjRef);
+ tracer.throwHostedModeException(jniEnv,
+ "Unable to allocate global reference for JS wrapper");
+ return;
+ }
+ // 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");
+ 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");
+ 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");
+ return;
+ }
+ // allocate a new root to hold the result of the getField call
+ JsRootedValue* toStringFunc = new JsRootedValue(context, JSVAL_VOID);
+ jniEnv->CallVoidMethod(obj, getFieldMeth, ident,
+ NS_REINTERPRET_CAST(jint, toStringFunc));
+ if (toStringFunc->isUndefined() || jniEnv->ExceptionCheck()) {
+ jniEnv->DeleteGlobalRef(dispObjRef);
+ tracer.setFail("getField(toString) failed");
+ return;
+ }
+ if (!JS_DefineProperty(context, newObj, "toString", toStringFunc->getValue(),
+ JS_PropertyStub, JS_PropertyStub, JSPROP_READONLY | JSPROP_PERMANENT)) {
+ jniEnv->DeleteGlobalRef(dispObjRef);
+ tracer.setFail("can't define JS toString method");
+ return;
+ }
+ jsRootedValue->setObject(newObj);
+}
+
+/*
+ * Wraps a Java function in a JS object.
+ *
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _setWrappedFunction
+ * Signature: (ILjava/lang/String;Lcom/google/gwt/dev/shell/moz/DispatchMethod;)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setWrappedFunction
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jstring methodName,
+ jobject dispatchMethod)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._setWrappedFunction", jsRootedValue);
+ JSContext* context = jsRootedValue->getContext();
+ JSObject* scriptWindow = JS_GetGlobalObject(context);
+ JStringWrap nameStr(jniEnv, methodName);
+ if (!nameStr.str()) {
+ tracer.throwHostedModeException(jniEnv,
+ "null method name passed to setWrappedFunction");
+ return;
+ }
+ 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());
+ if (!function) {
+ tracer.throwHostedModeException(jniEnv, "JS_NewFunction failed");
+ return;
+ }
+ JSObject* funObj = JS_GetFunctionObject(function);
+ if (!funObj) {
+ 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,
+ scriptWindow);
+ if (!cleanupObj) {
+ tracer.throwHostedModeException(jniEnv, "JS_NewObject failed");
+ return;
+ }
+ jobject dispMethRef = jniEnv->NewGlobalRef(dispatchMethod);
+ if (!dispMethRef || jniEnv->ExceptionCheck()) {
+ return;
+ }
+
+ // Store our global ref in the wrapper object
+ if (!JS_SetPrivate(context, 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);
+}
+
+/*
+ * Returns a JavaScript value as a string.
+ *
+ * Class: com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method: _toString
+ * Signature: (I)Ljava/lang/String;
+ */
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1toString
+ (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+ JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+ (jsRootedValueInt);
+ Tracer tracer("JsValueMoz._toString", jsRootedValue);
+ jsval val = jsRootedValue->getValue();
+ JSContext* cx = jsRootedValue->getContext();
+
+ // if it is a JavaScript object that has a toString member function
+ // call that, otherwise call JS_ValueToString
+ if(JSVAL_IS_OBJECT(val)) {
+ JSObject* jsObject = JSVAL_TO_OBJECT(val);
+ jsval fval;
+ jsval rval;
+ if (!JS_InstanceOf(cx, jsObject, &gwt_nativewrapper_class, 0)
+ && JS_GetProperty(cx, jsObject, "toString", &fval)
+ && JS_ValueToFunction(cx, fval)
+ && JS_CallFunctionValue(cx, jsObject, fval, 0, 0, &rval)) {
+ // all the steps succeeded, so use the result of toString() instead
+ // of the value for JS_ValueToString below
+ val = rval;
+ }
+ }
+ JSString* str = JS_ValueToString(cx, val);
+ if (!str) {
+ return 0;
+ }
+ int len = JS_GetStringLength(str);
+ jstring javaStr =jniEnv->NewString(reinterpret_cast<const jchar*>(
+ JS_GetStringChars(str)), len);
+ return javaStr;
+}
diff --git a/jni/linux/Makefile b/jni/linux/Makefile
index f5e2993..ede5520 100644
--- a/jni/linux/Makefile
+++ b/jni/linux/Makefile
@@ -1,4 +1,4 @@
-# Copyright 2006 Google Inc.
+# Copyright 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
@@ -15,25 +15,40 @@
##
# Target settings
##
+MOZ_SRC=/usr/local/google/home/jat/src/mozilla
+JAVA_HOME=/usr/lib/j2sdk1.5-sun
+GWT_TOOLS=../../../gwt-tools
+
GWT_ROOT = ../../
OBJDIR := $(GWT_ROOT)build/out/jni/linux/
OUTDIR := $(GWT_ROOT)build/jni/linux/
+STAGING := $(GWT_ROOT)build/staging/gwt-linux-0.0.0/
OUT := $(OUTDIR)libgwt-ll.so
##
+# The location to get .class files from for javah
+##
+CLASSDIR := $(GWT_ROOT)build/out/dev/linux/bin
+# use this if you want to use eclipse to build the class files during
+# development
+#CLASSDIR := $(GWT_ROOT)eclipse/dev/linux/bin
+
+##
# Tools
##
-CXX := gcc
+CXX := g++
AR := ar
STRIP := strip
LD := $(CXX)
+JAVAH := javah
##
# List of source, object, and dependency paths plus the path to them
##
SRCDIRS := ./:../core/
VPATH := .:../core
-SRCS := gwt-ll.cpp Moz.cpp
+SRCS := gwt-ll.cpp Moz.cpp JsValueMoz.cpp Tracer.cpp \
+ ExternalWrapper.cpp NativeWrapper.cpp JsRootedValue.cpp
OBJS := $(addprefix $(OBJDIR),$(SRCS:.cpp=.o))
DEPS := $(addprefix $(OBJDIR),$(SRCS:.cpp=.d))
@@ -46,7 +61,8 @@
$(GWT_TOOLS)/sdk/mozilla-1.7.12/include \
$(GWT_TOOLS)/sdk/mozilla-1.7.12/include/extra
-INCS := $(addprefix -i ,$(INCS)) $(addprefix -isystem ,$(SYSINCS))
+INCS := $(OBJDIR)
+INCS := $(addprefix -I ,$(INCS)) $(addprefix -isystem ,$(SYSINCS))
##
# Libraries and library path
@@ -58,8 +74,8 @@
# for notes on auto-dependency generation, see
# http://make.paulandlesley.org/autodep.html
# -MP obviates the need for sed hackery
-CFLAGS := -Os -fPIC -fno-omit-frame-pointer -fno-strict-aliasing -D_REENTRANT -c -MMD -MP -Wno-system-headers $(CFLAGS)
-LDFLAGS := -s -fPIC -fno-omit-frame-pointer -fno-strict-aliasing -D_REENTRANT -Wl,-shared-gcc $(LDFLAGS)
+CFLAGS := -ggdb -m32 -Os -fPIC -fno-omit-frame-pointer -fno-strict-aliasing -D_REENTRANT -MMD -MP -Wno-system-headers $(CFLAGS)
+LDFLAGS := -ggdb -m32 -s -fPIC -fno-omit-frame-pointer -fno-strict-aliasing -D_REENTRANT -Wl,-shared-gcc $(LDFLAGS)
#-------------------------------------------------------------------------------
# Rules
@@ -69,6 +85,11 @@
# default rule
##
all: $(OUT)
+ @[ -d $(STAGING) ] || mkdir -p $(STAGING)
+ cp $(OUT) $(STAGING)
+
+install: $(OUT)
+ cp $(OUT) prebuilt/
##
# Include the dependency rules
@@ -76,11 +97,33 @@
-include $(DEPS)
##
+# javah-generated headers for native methods
+##
+$(OBJDIR)LowLevelMoz.h : $(CLASSDIR)/com/google/gwt/dev/shell/moz/LowLevelMoz.class
+ $(JAVAH) -classpath $(CLASSDIR) -o $(OBJDIR)LowLevelMoz.h com.google.gwt.dev.shell.moz.LowLevelMoz
+
+$(OBJDIR)JsValueMoz.h : $(CLASSDIR)/com/google/gwt/dev/shell/moz/JsValueMoz.class
+ $(JAVAH) -classpath $(CLASSDIR) -o $(OBJDIR)JsValueMoz.h com.google.gwt.dev.shell.moz.JsValueMoz
+
+##
+# Dependency rules for generated headers
+# TODO(jat): autogenerate these and others
+##
+$(OBJDIR)Moz.o: $(OBJDIR)LowLevelMoz.h
+$(OBJDIR)JsValueMoz.o: $(OBJDIR)JsValueMoz.h
+
+##
# Compilation rule for cpp files
##
$(OBJDIR)%.o : $(SRCDIR)%.cpp
@[ -d $(OBJDIR) ] || mkdir -p $(OBJDIR)
- $(CXX) $(CFLAGS) $(INCS) -o $@ $<
+ $(CXX) -c $(CFLAGS) $(INCS) -o $@ $<
+
+%.i : $(SRCDIR)%.cpp
+ $(CXX) -E $(CFLAGS) $(INCS) -o $@ $<
+
+%.I : $(SRCDIR)%.cpp
+ $(CXX) -E -dDI $(CFLAGS) $(INCS) -o $@ $<
##
# Actual output file
@@ -88,7 +131,7 @@
$(OUT): $(OBJS)
@[ -d $(OUTDIR) ] || mkdir -p $(OUTDIR)
$(LD) -shared $(LDFLAGS) $(LIBPATH) -o $@ $^ $(LIBS)
- $(STRIP) --strip-unneeded $@
+# $(STRIP) --strip-unneeded $@
##
# Clean rule
diff --git a/jni/linux/Moz.cpp b/jni/linux/Moz.cpp
index 6a174a6..424218b 100644
--- a/jni/linux/Moz.cpp
+++ b/jni/linux/Moz.cpp
@@ -1,719 +1,296 @@
-// Copyright 2005 Google Inc.
-// All Rights Reserved.
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
// Mozilla-specific hosted-mode methods
+#define DEBUG
+
+#include <cstdio>
+
+//#define JS_GetClass JS_GetClassOld
+
// Mozilla header files
-#include "nsIServiceManagerUtils.h"
-#include "nsComponentManagerUtils.h"
-#include "nsICategoryManager.h"
-#include "nsIScriptNameSpaceManager.h"
-#include "nsIScriptObjectOwner.h"
-#include "nsIScriptGlobalObject.h"
-#include "nsIScriptContext.h"
-#include "nsIDOMWindow.h"
-#include "nsIXPConnect.h"
-#include "nsCOMPtr.h"
-#include "nsAutoPtr.h"
+#include "mozilla-headers.h"
#include <jni.h>
+#include "gwt-jni.h"
+#include "JsRootedValue.h"
+#include "ExternalWrapper.h"
+#include "Tracer.h"
+#include "JsStringWrap.h"
-static JNIEnv* gEnv = 0;
-static jclass gClass = 0;
+// include javah-generated header to make sure we match
+#include "LowLevelMoz.h"
//#define FILETRACE
//#define JAVATRACE
-#if defined(FILETRACE) && defined(JAVATRACE)
-#define TRACE(s) filetrace(s),javatrace(s)
-#elif defined(FILETRACE)
-#define TRACE(s) filetrace(s)
-#elif defined(JAVATRACE)
-#define TRACE(s) javatrace(s)
-#else
-#define TRACE(s) ((void)0)
+
+// TODO(jat) should be in a header
+extern nsCID kGwtExternalCID;
+
+JNIEnv* savedJNIEnv = 0;
+jclass lowLevelMozClass;
+
+static void PrintJSValue(JSContext* cx, jsval val, char* prefix="") {
+ JSType type = JS_TypeOfValue(cx, val);
+ const char* typeString=JS_GetTypeName(cx, type);
+ char buf[256];
+ char* p = buf;
+ p += snprintf(p, sizeof(buf)-(p-buf), "%s%s", prefix, typeString);
+ switch(type) {
+ case JSTYPE_VOID:
+ break;
+ case JSTYPE_BOOLEAN:
+ p += snprintf(p, sizeof(buf)-(p-buf), ": %s",
+ JSVAL_TO_BOOLEAN(val) ? "true" : "false");
+ break;
+ case JSTYPE_NUMBER:
+ if (JSVAL_IS_INT(val)) {
+ p += snprintf(p, sizeof(buf)-(p-buf), ": %d", JSVAL_TO_INT(val));
+ } else {
+ p += snprintf(p, sizeof(buf)-(p-buf), ": %lf",
+ (double)*JSVAL_TO_DOUBLE(val));
+ }
+ break;
+ case JSTYPE_OBJECT: {
+ JSObject* obj = JSVAL_TO_OBJECT(val);
+ if (!JSVAL_IS_OBJECT(val)) break;
+ JSClass* clazz = obj ? JS_GET_CLASS(cx, obj) : 0;
+ p += snprintf(p, sizeof(buf)-(p-buf), " @ %08x, class %s",
+ (unsigned)obj, clazz ? clazz->name : "<null>");
+ break;
+ }
+ case JSTYPE_FUNCTION:
+ case JSTYPE_LIMIT:
+ break;
+ case JSTYPE_STRING: {
+ JsStringWrap str(cx, JSVAL_TO_STRING(val));
+ p += snprintf(p, sizeof(buf)-(p-buf), ": %.*s", str.length(), str.bytes());
+ break;
+ }
+ }
+ Tracer::log("%s", buf);
+}
+
+
+static bool InitGlobals(JNIEnv* env, jclass llClass) {
+ if (savedJNIEnv)
+ return false;
+
+#ifdef FILETRACE
+ Tracer::setFile("gwt-ll.log");
+#endif // FILETRACE
+
+#ifdef JAVATRACE
+ Tracer::setJava(env, llClass);
+#endif // JAVATRACE
+
+#ifdef DEBUG
+ Tracer::setLevel(Tracer::LEVEL_DEBUG);
#endif
-#ifdef FILETRACE
-static FILE* gout = 0;
-static void filetrace(const char* s)
-{
- fprintf(gout, s);
- fprintf(gout, "\n");
- fflush(gout);
-}
-#endif // FILETRACE
-
-#ifdef JAVATRACE
-static jmethodID gTraceMethod = 0;
-static void javatrace(const char* s)
-{
- if (!gEnv->ExceptionCheck())
- {
- jstring out = gEnv->NewStringUTF(s);
- if (!gEnv->ExceptionCheck())
- gEnv->CallStaticVoidMethod(gClass, gTraceMethod, out);
- else
- gEnv->ExceptionClear();
- }
-}
-#endif // JAVATRACE
-
-static bool InitGlobals(JNIEnv* env, jclass llClass)
-{
- if (gEnv)
- return true;
-
-#ifdef FILETRACE
- gout = fopen("gwt-ll.log", "w");
- filetrace("LOG STARTED");
-#endif // FILETRACE
-
- gClass = static_cast<jclass>(env->NewGlobalRef(llClass));
- if (!gClass || env->ExceptionCheck())
- return false;
-
-#ifdef JAVATRACE
- gTraceMethod = env->GetStaticMethodID(gClass, "trace", "(Ljava/lang/String;)V");
- if (!gTraceMethod || env->ExceptionCheck())
- return false;
-#endif // JAVATRACE
-
- gEnv = env;
- return true;
+ savedJNIEnv = env;
+ lowLevelMozClass = static_cast<jclass>(env->NewGlobalRef(llClass));
+ return true;
}
-struct JStringWrap
-{
- JStringWrap(JNIEnv* env, jstring str): env(env), s(str), p(0), jp(0) { }
- ~JStringWrap() { if (p) env->ReleaseStringUTFChars(s, p); if (jp) env->ReleaseStringChars(s, jp); }
- const char* str() { if (!p) p = env->GetStringUTFChars(s, 0); return p; }
- const jchar* jstr() { if (!jp) jp = env->GetStringChars(s, 0); return jp; }
-private:
- JNIEnv* env;
- jstring s;
- const char* p;
- const jchar* jp;
-};
-
-static void hextrace(int val) {
- char buf[20];
- const char* hex = "0123456789ABCDEF";
- for (int i = 7; i >= 0; --i) {
- buf[i] = hex[val & 0xF];
- val >>= 4;
- }
- buf[8] = 0;
- TRACE(buf);
+/*
+ * 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);
}
-static JSBool gwt_invoke(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+/* 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)
{
- TRACE("ENTER gwt_invoke");
+ 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)
- return TRACE("FAIL gwt_invoke: JSTYPE_FUNCTION"), JS_FALSE;
-
- JSObject* funObj = JSVAL_TO_OBJECT(argv[-2]);
- jsval jsCleanupObj;
-
- // Pull the wrapper object out of the funObj's reserved slot
- if (!JS_GetReservedSlot(cx, funObj, 0, &jsCleanupObj))
- return TRACE("FAIL gwt_invoke: JS_GetReservedSlot"), JS_FALSE;
-
- JSObject* cleanupObj = JSVAL_TO_OBJECT(jsCleanupObj);
- if (!cleanupObj)
- return TRACE("FAIL gwt_invoke: cleanupObj"), JS_FALSE;
-
- // Get dispMeth global ref out of the wrapper object
- jobject dispMeth = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, cleanupObj));
- if (!dispMeth)
- return TRACE("FAIL gwt_invoke: dispMeth"), JS_FALSE;
-
- jclass dispClass = gEnv->GetObjectClass(dispMeth);
- if (!dispClass || gEnv->ExceptionCheck())
- return TRACE("FAIL gwt_invoke: GetObjectClass"), JS_FALSE;
-
- jmethodID invokeID = gEnv->GetMethodID(dispClass, "invoke", "(I[I)I");
- if (!invokeID || gEnv->ExceptionCheck())
- return TRACE("FAIL gwt_invoke: GetMethodID"), JS_FALSE;
-
- jintArray jsargs = gEnv->NewIntArray(argc);
- if (!jsargs || gEnv->ExceptionCheck())
- return TRACE("FAIL gwt_invoke: NewIntArray"), JS_FALSE;
-
- gEnv->SetIntArrayRegion(jsargs, 0, argc, (jint*)argv);
- if (gEnv->ExceptionCheck())
- return TRACE("FAIL gwt_invoke: SetIntArrayRegion"), JS_FALSE;
-
- *rval = gEnv->CallIntMethod(dispMeth, invokeID, argv[-1], jsargs);
- if (gEnv->ExceptionCheck())
- return TRACE("FAIL gwt_invoke: java exception is active"), JS_FALSE;
-
- if (JS_IsExceptionPending(cx))
- return TRACE("FAIL gwt_invoke: js exception is active"), JS_FALSE;
-
- TRACE("SUCCESS gwt_invoke");
- return JS_TRUE;
-}
-
-static JSBool getJavaPropertyStats(JSContext *cx, JSObject *obj, jsval id, jclass& dispClass, jobject& dispObj, jstring& jident)
-{
- if (!JSVAL_IS_STRING(id))
- return JS_FALSE;
-
- jident = gEnv->NewString(JS_GetStringChars(JSVAL_TO_STRING(id)), JS_GetStringLength(JSVAL_TO_STRING(id)));
- if (!jident || gEnv->ExceptionCheck())
- return JS_FALSE;
-
- dispObj = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
- if (!dispObj)
- return JS_FALSE;
-
- dispClass = gEnv->GetObjectClass(dispObj);
- if (gEnv->ExceptionCheck())
- return JS_FALSE;
-
- return JS_TRUE;
-}
-
-static JSBool JS_DLL_CALLBACK gwt_nativewrapper_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
-{
- TRACE("ENTER gwt_nativewrapper_setProperty");
-
- jclass dispClass;
- jobject dispObj;
- jstring ident;
- if (!getJavaPropertyStats(cx,obj,id,dispClass,dispObj,ident))
- return JS_FALSE;
-
- jmethodID setFieldMeth = gEnv->GetMethodID(dispClass, "setField", "(Ljava/lang/String;I)V");
- if (!setFieldMeth || gEnv->ExceptionCheck())
- return JS_FALSE;
-
- gEnv->CallVoidMethod(dispObj, setFieldMeth, ident, *vp);
- if (gEnv->ExceptionCheck())
- return JS_FALSE;
-
- TRACE("SUCCESS gwt_nativewrapper_setProperty");
- return JS_TRUE;
-}
-
-static JSBool JS_DLL_CALLBACK gwt_nativewrapper_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
-{
- TRACE("ENTER gwt_nativewrapper_getProperty");
-
- if (*vp != JSVAL_VOID)
- return TRACE("SUCCESS, already defined"), JS_TRUE;
-
- jclass dispClass;
- jobject dispObj;
- jstring ident;
- if (!getJavaPropertyStats(cx,obj,id,dispClass,dispObj,ident))
- return JS_FALSE;
-
- jmethodID getFieldMeth = gEnv->GetMethodID(dispClass, "getField", "(Ljava/lang/String;)I");
- if (!getFieldMeth || gEnv->ExceptionCheck())
- return JS_FALSE;
-
- *vp = gEnv->CallIntMethod(dispObj, getFieldMeth, ident);
- if (gEnv->ExceptionCheck())
- return JS_FALSE;
-
- TRACE("SUCCESS gwt_nativewrapper_getProperty");
- return JS_TRUE;
-}
-
-static void JS_DLL_CALLBACK gwt_nativewrapper_finalize(JSContext *cx, JSObject *obj)
-{
- jobject dispObj = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
- if (dispObj)
- gEnv->DeleteGlobalRef(dispObj);
-}
-
-static JSClass gwt_functionwrapper_class = {
- "gwt_functionwrapper_class", JSCLASS_HAS_PRIVATE,
- JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
- JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, gwt_nativewrapper_finalize,
- JSCLASS_NO_OPTIONAL_MEMBERS
-};
-
-static JSClass gwt_nativewrapper_class = {
- "gwt_nativewrapper_class", JSCLASS_HAS_PRIVATE,
- JS_PropertyStub, JS_PropertyStub, gwt_nativewrapper_getProperty, gwt_nativewrapper_setProperty,
- JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, gwt_nativewrapper_finalize,
- JSCLASS_NO_OPTIONAL_MEMBERS
-};
-
-static JSBool gwtOnLoad(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
- TRACE("ENTER gwtOnLoad");
-
- if (argc < 2)
- return JS_FALSE;
-
- JSObject* scriptWindow = 0;
- if (argv[0] != JSVAL_NULL && argv[0] != JSVAL_VOID) {
- if (!JS_ValueToObject(cx, argv[0], &scriptWindow))
- return JS_FALSE;
- }
-
- JSString* moduleName = 0;
- if (argv[1] != JSVAL_NULL && argv[1] != JSVAL_VOID) {
- moduleName = JS_ValueToString(cx, argv[1]);
- }
-
- nsCOMPtr<nsIScriptGlobalObject> scriptGlobal(0);
- if (scriptWindow) {
- nsCOMPtr<nsIXPConnect> xpConnect = do_GetService(nsIXPConnect::GetCID());
- nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
- xpConnect->GetWrappedNativeOfJSObject(cx, scriptWindow, getter_AddRefs(wrappedNative));
- if (wrappedNative) {
- nsCOMPtr<nsISupports> native;
- wrappedNative->GetNative(getter_AddRefs(native));
- scriptGlobal = do_QueryInterface(native);
- }
- }
-
- jstring jModuleName(0);
- if (moduleName) {
- jModuleName = gEnv->NewString(JS_GetStringChars(moduleName), JS_GetStringLength(moduleName));
- if (!jModuleName || gEnv->ExceptionCheck())
- return JS_FALSE;
- }
-
- jobject externalObject = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
- jclass objClass = gEnv->GetObjectClass(externalObject);
- if (!objClass || gEnv->ExceptionCheck())
- return JS_FALSE;
-
- jmethodID methodID = gEnv->GetMethodID(objClass, "gwtOnLoad", "(ILjava/lang/String;)Z");
- if (!methodID || gEnv->ExceptionCheck())
- return JS_FALSE;
-
- jboolean result = gEnv->CallBooleanMethod(externalObject, methodID, NS_REINTERPRET_CAST(jint, scriptGlobal.get()), jModuleName);
- if (gEnv->ExceptionCheck())
- return JS_FALSE;
-
- *rval = BOOLEAN_TO_JSVAL((result == JNI_FALSE) ? JS_FALSE : JS_TRUE);
- TRACE("SUCCESS gwtOnLoad");
- return JS_TRUE;
-}
-
-class ExternalWrapper : public nsIScriptObjectOwner
-{
-public:
- NS_DECL_ISUPPORTS
- NS_IMETHOD GetScriptObject(nsIScriptContext *aContext, void** aScriptObject);
- NS_IMETHOD SetScriptObject(void* aScriptObject);
- ExternalWrapper(): mScriptObject(0) { }
-private:
- ~ExternalWrapper() { }
- void *mScriptObject;
-};
-
-NS_IMPL_ISUPPORTS1(ExternalWrapper, nsIScriptObjectOwner)
-
-static JSBool JS_DLL_CALLBACK gwt_external_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
-{
+ // 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]);
-static JSBool JS_DLL_CALLBACK gwt_external_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
-{
- TRACE("ENTER gwt_external_getProperty");
+ // 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;
+ }
- if (*vp != JSVAL_VOID)
- return TRACE("SUCCESS, already defined"), JS_TRUE;
+ // 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;
+ }
- if (!JSVAL_IS_STRING(id))
- return TRACE("FAIL 1"), 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;
+ }
- jstring jident = gEnv->NewString(JS_GetStringChars(JSVAL_TO_STRING(id)), JS_GetStringLength(JSVAL_TO_STRING(id)));
- if (!jident || gEnv->ExceptionCheck())
- return TRACE("FAIL 2"), 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;
+ }
- jobject externalObject = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
- jclass objClass = gEnv->GetObjectClass(externalObject);
- if (!objClass || gEnv->ExceptionCheck())
- return TRACE("FAIL 4"), 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));
- jmethodID methodID = gEnv->GetMethodID(objClass, "resolveReference", "(Ljava/lang/String;)I");
- if (!methodID || gEnv->ExceptionCheck())
- return TRACE("FAIL 5"), JS_FALSE;
- int retval = gEnv->CallIntMethod(externalObject, methodID, jident);
- if (gEnv->ExceptionCheck())
- return TRACE("FAIL 6"), JS_FALSE;
- *vp = retval;
+ // 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);
- TRACE("SUCCESS gwt_external_getProperty");
- return JS_TRUE;
-}
+ // 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;
+ }
-static void JS_DLL_CALLBACK gwt_external_finalize(JSContext *cx, JSObject *obj)
-{
- jobject externalObject = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
- if (externalObject)
- gEnv->DeleteGlobalRef(externalObject);
- JS_FinalizeStub(cx,obj);
-}
+ // extract return value
+ *rval = jsReturnVal->getValue();
-static JSClass gwt_external_class = {
- "gwt_external_class", JSCLASS_HAS_PRIVATE,
- JS_PropertyStub, JS_PropertyStub, gwt_external_getProperty, gwt_external_setProperty,
- JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, gwt_external_finalize,
- JSCLASS_NO_OPTIONAL_MEMBERS
-};
+#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
-class nsJSObjectLocker : public nsISupports
-{
-public:
- NS_DECL_ISUPPORTS
- nsJSObjectLocker(JSContext* cx, jsval val): mCx(cx), mVal(val) { if (mCx && mVal) JSVAL_LOCK(mCx,mVal); }
-
- JSContext* const mCx;
- const jsval mVal;
-
- // Major hack; compare other object's vtable ptrs to this one to do crude RTTI
- static nsJSObjectLocker sJSObjectLocker;
-
-private:
- ~nsJSObjectLocker() { if (mCx && mVal) JSVAL_UNLOCK(mCx,mVal); }
-};
-NS_IMPL_ISUPPORTS0(nsJSObjectLocker)
-
-// Major hack; compare other object's vtable ptrs to this one to do crude RTTI
-nsJSObjectLocker nsJSObjectLocker::sJSObjectLocker(0,0);
-
-NS_IMETHODIMP ExternalWrapper::GetScriptObject(nsIScriptContext *aContext, void** aScriptObject)
-{
- TRACE("ENTER ExternalWrapper::GetScriptObject");
-
- if (!aScriptObject)
- return TRACE("FAIL 0"), NS_ERROR_INVALID_POINTER;
-
- if (!mScriptObject)
- {
- *aScriptObject = 0;
-
- nsIScriptGlobalObject* globalObject = aContext->GetGlobalObject();
- nsCOMPtr<nsIDOMWindow> domWindow(do_QueryInterface(globalObject));
- if (!domWindow)
- return TRACE("FAIL 1"), NS_ERROR_UNEXPECTED;
-
- nsCOMPtr<nsIDOMWindow> topWindow;
- domWindow->GetTop(getter_AddRefs(topWindow));
- if (!topWindow)
- return TRACE("FAIL 2"), NS_ERROR_UNEXPECTED;
-
- jmethodID methodID = gEnv->GetStaticMethodID(gClass, "createExternalObjectForDOMWindow", "(I)Lcom/google/gwt/dev/shell/moz/LowLevelMoz$ExternalObject;");
- if (!methodID || gEnv->ExceptionCheck())
- return TRACE("FAIL 3"), NS_ERROR_UNEXPECTED;
-
- jobject externalObject = gEnv->CallStaticObjectMethod(gClass, methodID, NS_REINTERPRET_CAST(jint, topWindow.get()));
- if (!externalObject || gEnv->ExceptionCheck())
- return TRACE("FAIL 4"), NS_ERROR_UNEXPECTED;
- externalObject = gEnv->NewGlobalRef(externalObject);
- if (!externalObject || gEnv->ExceptionCheck())
- return TRACE("FAIL 5"), NS_ERROR_UNEXPECTED;
-
- JSContext* cx = NS_REINTERPRET_CAST(JSContext*,aContext->GetNativeContext());
- if (!cx)
- return TRACE("FAIL 6"), NS_ERROR_UNEXPECTED;
- JSObject* newObj = JS_NewObject(cx, &gwt_external_class, 0, globalObject->GetGlobalJSObject());
- if (!newObj)
- return TRACE("FAIL 7"), NS_ERROR_OUT_OF_MEMORY;
- if (!JS_SetPrivate(cx, newObj, externalObject)) {
- gEnv->DeleteGlobalRef(externalObject);
- return TRACE("FAIL 8"), NS_ERROR_UNEXPECTED;
- }
- if (!JS_DefineFunction(cx, newObj, "gwtOnLoad", gwtOnLoad, 3,
- JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
- return TRACE("FAIL 9"), NS_ERROR_UNEXPECTED;
-
- mScriptObject = newObj;
- }
-
- *aScriptObject = mScriptObject;
- TRACE("SUCCESS ExternalWrapper::GetScriptObject");
- return NS_OK;
-}
-
-NS_IMETHODIMP ExternalWrapper::SetScriptObject(void* aScriptObject)
-{
- mScriptObject = aScriptObject;
- return NS_OK;
-}
-
-class nsRpExternalFactory : public nsIFactory
-{
-public:
- NS_DECL_ISUPPORTS
- NS_DECL_NSIFACTORY
- nsRpExternalFactory() { }
-private:
- ~nsRpExternalFactory() { }
-};
-
-NS_IMPL_ISUPPORTS1(nsRpExternalFactory, nsIFactory)
-
-NS_IMETHODIMP nsRpExternalFactory::CreateInstance(nsISupports *aOuter, const nsIID & aIID, void** aResult)
-{
- TRACE("ENTER nsRpExternalFactory::CreateInstance");
-
- if (!aResult)
- return NS_ERROR_INVALID_POINTER;
-
- *aResult = NULL;
-
- if (aOuter)
- return NS_ERROR_NO_AGGREGATION;
-
- nsISupports* object = new ExternalWrapper();
- if (!object)
- return NS_ERROR_OUT_OF_MEMORY;
-
- nsresult result = object->QueryInterface(aIID, aResult);
- if (!*aResult || NS_FAILED(result))
- delete object;
- else
- TRACE("SUCCESS nsRpExternalFactory::CreateInstance");
- return result;
-}
-
-NS_IMETHODIMP nsRpExternalFactory::LockFactory(PRBool lock)
-{
- return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-#define GWT_EXTERNAL_FACTORY_CID \
-{ 0xF56E23F8, 0x5D06, 0x47F9, \
-{ 0x88, 0x5A, 0xD9, 0xCA, 0xC3, 0x38, 0x41, 0x7F } }
-#define GWT_EXTERNAL_CONTRACTID "@com.google/GWT/external;1"
-
-static NS_DEFINE_CID(kGwtExternalCID, GWT_EXTERNAL_FACTORY_CID);
-
-extern "C" {
-
-/*
- * Class: com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method: _coerceTo31Bits
- * Signature: (II[I)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1coerceTo31Bits
- (JNIEnv* env, jclass, jint scriptObjInt, jint v, jintArray rval)
-{
- nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
- nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
- if (!scriptContext)
- return JNI_FALSE;
- JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
-
- jint r;
- if (!JS_ValueToECMAInt32(cx, v, &r))
- return JNI_FALSE;
- env->SetIntArrayRegion(rval, 0, 1, &r);
- if (env->ExceptionCheck())
- return JNI_FALSE;
- return JNI_TRUE;
+ return returnValue;
}
/*
- * Class: com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method: _coerceToBoolean
- * Signature: (II[Z)Z
+ * 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
*/
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1coerceToBoolean
- (JNIEnv* env, jclass, jint scriptObjInt, jint v, jbooleanArray rval)
+JSBool getJavaPropertyStats(JSContext *cx, JSObject *obj, jsval id,
+ jclass& dispClass, jobject& dispObj, jstring& jident)
{
- nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
- nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
- if (!scriptContext)
- return JNI_FALSE;
- JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
+ Tracer tracer("getJavaPropertyStats");
+ if (!JSVAL_IS_STRING(id)) {
+ tracer.setFail("id is not a string");
+ return JS_FALSE;
+ }
- JSBool r;
- if (!JS_ValueToBoolean(cx, v, &r))
- return JNI_FALSE;
- jboolean jr = (r == JS_FALSE) ? JNI_FALSE : JNI_TRUE;
- env->SetBooleanArrayRegion(rval, 0, 1, &jr);
- if (env->ExceptionCheck())
- return JNI_FALSE;
- return JNI_TRUE;
-}
+ 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;
+ }
-/*
- * Class: com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method: _coerceToDouble
- * Signature: (II[D)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1coerceToDouble
- (JNIEnv* env, jclass, jint scriptObjInt, jint v, jdoubleArray rval)
-{
- nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
- nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
- if (!scriptContext)
- return JNI_FALSE;
- JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
+ dispObj = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
+ if (!dispObj) {
+ tracer.setFail("can't get dispatch object");
+ return JS_FALSE;
+ }
- jdouble r;
- if (!JS_ValueToNumber(cx, v, &r))
- return JNI_FALSE;
- env->SetDoubleArrayRegion(rval, 0, 1, &r);
- if (env->ExceptionCheck())
- return JNI_FALSE;
- return JNI_TRUE;
-}
+ dispClass = savedJNIEnv->GetObjectClass(dispObj);
+ if (savedJNIEnv->ExceptionCheck()) {
+ tracer.setFail("can't get class of dispatch object");
+ return JS_FALSE;
+ }
-/*
- * Class: com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method: _coerceToString
- * Signature: (II[Ljava/lang/String;)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1coerceToString
- (JNIEnv* env, jclass, jint scriptObjInt, jint v, jobjectArray rval)
-{
- jstring jr(0);
- if (v != JSVAL_NULL && v != JSVAL_VOID) {
- nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
- nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
- if (!scriptContext)
- return JNI_FALSE;
- JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
-
- JSString* str = JS_ValueToString(cx, v);
- if (!str)
- return JNI_FALSE;
- jr = env->NewString(JS_GetStringChars(str), JS_GetStringLength(str));
- if (env->ExceptionCheck())
- return JNI_FALSE;
- }
- env->SetObjectArrayElement(rval, 0, jr);
- if (env->ExceptionCheck())
- return JNI_FALSE;
- return JNI_TRUE;
-}
-
-/*
- * Class: com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method: _convert31Bits
- * Signature: (II[I)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1convert31Bits
- (JNIEnv* env, jclass, int scriptObjInt, jint v, jintArray rval)
-{
- jint r = INT_TO_JSVAL(v);
- env->SetIntArrayRegion(rval, 0, 1, &r);
- if (env->ExceptionCheck())
- return JNI_FALSE;
- return JNI_TRUE;
-}
-
-/*
- * Class: com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method: _convertBoolean
- * Signature: (IZ[I)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1convertBoolean
- (JNIEnv* env, jclass, int scriptObjInt, jboolean v, jintArray rval)
-{
- jint r = BOOLEAN_TO_JSVAL((v == JNI_FALSE) ? JS_FALSE : JS_TRUE);
- env->SetIntArrayRegion(rval, 0, 1, &r);
- if (env->ExceptionCheck())
- return JNI_FALSE;
- return JNI_TRUE;
-}
-
-/*
- * Class: com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method: _convertDouble
- * Signature: (ID[I)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1convertDouble
- (JNIEnv* env, jclass, int scriptObjInt, jdouble v, jintArray rval)
-{
- nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
- nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
- if (!scriptContext)
- return JNI_FALSE;
- JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
-
- jsval rv;
- if (!JS_NewDoubleValue(cx, jsdouble(v), &rv))
- return JNI_FALSE;
- jint r = rv;
- env->SetIntArrayRegion(rval, 0, 1, &r);
- if (env->ExceptionCheck())
- return JNI_FALSE;
- return JNI_TRUE;
-}
-
-/*
- * Class: com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method: _convertString
- * Signature: (ILjava/lang/String;[I)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1convertString
- (JNIEnv* env, jclass, int scriptObjInt, jstring v, jintArray rval)
-{
- jint r = 0;
- if (v) {
- JStringWrap jv(env, v);
- if (!jv.jstr())
- return JNI_FALSE;
-
- nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
- nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
- if (!scriptContext)
- return JNI_FALSE;
- JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
-
- JSString* str = JS_NewUCStringCopyZ(cx, jv.jstr());
- if (!str)
- return JNI_FALSE;
-
- r = STRING_TO_JSVAL(str);
- }
-
- env->SetIntArrayRegion(rval, 0, 1, &r);
- if (env->ExceptionCheck())
- return JNI_FALSE;
- return JNI_TRUE;
-}
-
-/*
- * Class: com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method: _executeScript
- * Signature: (ILjava/lang/String;)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1executeScript
- (JNIEnv* env, jclass llClass, jint scriptObject, jstring code)
-{
- JStringWrap jcode(env, code);
- if (!jcode.jstr())
- return JNI_FALSE;
-
- nsIScriptGlobalObject* globalObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObject);
- nsCOMPtr<nsIScriptContext> scriptContext(globalObject->GetContext());
- nsXPIDLString scriptString;
- scriptString = jcode.jstr();
-
- nsXPIDLString aRetValue;
- PRBool aIsUndefined;
- if (NS_FAILED(scriptContext->EvaluateString(scriptString, globalObject->GetGlobalJSObject(),
- 0, __FILE__, __LINE__, 0, aRetValue, &aIsUndefined)))
- return JNI_FALSE;
- return JNI_TRUE;
+ return JS_TRUE;
}
/*
@@ -721,129 +298,143 @@
* Method: _executeScriptWithInfo
* Signature: (ILjava/lang/String;Ljava/lang/String;I)Z
*/
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1executeScriptWithInfo
- (JNIEnv* env, jclass llClass, jint scriptObject, jstring code, jstring file, jint line)
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1executeScriptWithInfo
+ (JNIEnv* env, jclass llClass, jint scriptObject, jstring code,
+ jstring file, jint line)
{
- JStringWrap jcode(env, code);
- if (!jcode.jstr())
- return JNI_FALSE;
+ Tracer tracer("LowLevelMoz._executeScriptWithInfo");
+ JStringWrap jcode(env, code);
+ if (!jcode.jstr()) {
+ tracer.setFail("null code string");
+ return JNI_FALSE;
+ }
+ JStringWrap jfile(env, file);
+ if (!jfile.str()) {
+ tracer.setFail("null file name");
+ return JNI_FALSE;
+ }
+ tracer.log("code=%s, file=%s, line=%d", jcode.str(), jfile.str(), line);
- JStringWrap jfile(env, file);
- if (!jfile.str())
- return JNI_FALSE;
+ nsIScriptGlobalObject* globalObject =
+ NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObject);
+ nsCOMPtr<nsIScriptContext> scriptContext(globalObject->GetContext());
+ nsXPIDLString scriptString;
+ scriptString = jcode.jstr();
- nsIScriptGlobalObject* globalObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObject);
- nsCOMPtr<nsIScriptContext> scriptContext(globalObject->GetContext());
- nsXPIDLString scriptString;
- scriptString = jcode.jstr();
-
- nsXPIDLString aRetValue;
- PRBool aIsUndefined;
- if (NS_FAILED(scriptContext->EvaluateString(scriptString, globalObject->GetGlobalJSObject(),
- 0, jfile.str(), line, 0, aRetValue, &aIsUndefined)))
- return JNI_FALSE;
- return JNI_TRUE;
+ nsXPIDLString aRetValue;
+ PRBool aIsUndefined;
+ if (NS_FAILED(scriptContext->EvaluateString(scriptString,
+ globalObject->GetGlobalJSObject(), 0, jfile.str(), line, 0,
+ aRetValue, &aIsUndefined))) {
+ tracer.setFail("EvaluateString failed");
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
}
/*
* Class: com_google_gwt_dev_shell_moz_LowLevelMoz
* Method: _invoke
- * Signature: (ILjava/lang/String;II[I[I)Z
+ * Signature: (ILjava/lang/String;I[I)I
*/
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1invoke
- (JNIEnv* env, jclass, int scriptObjInt, jstring methodName, jint jsthisval, jint jsargc, jintArray jsargs, jintArray rval)
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1invoke
+ (JNIEnv* env, jclass, int scriptObjInt, jstring methodName, jint jsThisInt,
+ jintArray jsArgsInt, jint jsRetValInt)
{
- TRACE("ENTER Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1invoke");
+ Tracer tracer("LowLevelMoz._invoke");
- JStringWrap methodStr(env,methodName);
- if (!methodStr.str())
- return TRACE("FAIL 1"), JNI_FALSE;
+ JStringWrap methodStr(env, methodName);
+ if (!methodStr.str()) {
+ tracer.setFail("null method name");
+ return JNI_FALSE;
+ }
+ JsRootedValue* jsThisRV = reinterpret_cast<JsRootedValue*>(jsThisInt);
+ jint jsArgc = env->GetArrayLength(jsArgsInt);
+ tracer.log("method=%s, jsthis=%08x, #args=%d", methodStr.str(), jsThisInt,
+ jsArgc);
- nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
- nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
- if (!scriptContext)
- return TRACE("FAIL 2"), JNI_FALSE;
- JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
- JSObject* scriptWindow = (JSObject*)scriptObject->GetGlobalJSObject();
+ 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());
- jsval fval;
- if (!JS_GetProperty(cx, scriptWindow, methodStr.str(), &fval))
- return TRACE("FAIL 3"), JNI_FALSE;
- if (!JS_ValueToFunction(cx, fval))
- return TRACE("FAIL 4"), JNI_FALSE;
+ jsval fval;
+ if (!JS_GetProperty(cx, scriptWindow, methodStr.str(), &fval)) {
+ tracer.setFail("JS_GetProperty(method) failed");
+ return JNI_FALSE;
+ }
+ JSFunction* jsFunction = JS_ValueToFunction(cx, fval);
+ if (!jsFunction) {
+ tracer.setFail("JS_ValueToFunction failed");
+ return JNI_FALSE;
+ }
+
+ // extract arguments in jsval form
+ nsAutoArrayPtr<jint> jsargvals(new jint[jsArgc]);
+ if (!jsargvals) {
+ tracer.setFail("failed to allocate arg array");
+ return JNI_FALSE;
+ }
+ env->GetIntArrayRegion(jsArgsInt, 0, jsArgc, jsargvals);
+ if (env->ExceptionCheck()) {
+ tracer.setFail("copy from Java array failed");
+ return JNI_FALSE;
+ }
+ nsAutoArrayPtr<jsval> jsargs(new jsval[jsArgc]);
+ for (int i = 0; i < jsArgc; ++i) {
+ JsRootedValue* arg = reinterpret_cast<JsRootedValue*>(jsargvals[i]);
+ jsargs[i] = arg->getValue();
+ }
- nsAutoArrayPtr<jint> jsargvals(new jint[jsargc]);
- if (!jsargvals)
- return TRACE("FAIL 5"), JNI_FALSE;
+ jsval jsrval;
+ JSObject* jsThis;
+ if (jsThisRV->isNull()) {
+ jsThis = scriptWindow;
+ } else {
+ jsThis = jsThisRV->getObject();
+ }
+
+ PrintJSValue(cx, OBJECT_TO_JSVAL(jsThis), "jsThis=");
+ for (int i = 0; i < jsArgc; ++i) {
+ char buf[256];
+ snprintf(buf, sizeof(buf), "arg[%d]=", i);
+ PrintJSValue(cx, jsargs[i], buf);
+ }
+ //tracer.log("fval = %08x, args=%08x", fval, jsargs.get());
+ if (!JS_CallFunctionValue(cx, jsThis, fval, jsArgc, jsargs.get(), &jsrval)) {
+ tracer.setFail("JS_CallFunctionValue failed");
+ return JNI_FALSE;
+ }
- env->GetIntArrayRegion(jsargs, 0, jsargc, jsargvals);
- if (env->ExceptionCheck())
- return TRACE("FAIL 6"), JNI_FALSE;
-
- jsval jsrval;
- JSObject* jsthis = (jsthisval == JSVAL_NULL) ? scriptWindow : JSVAL_TO_OBJECT(jsthisval);
- if (!JS_CallFunctionValue(cx, jsthis, fval, jsargc, (jsval*)jsargvals.get(), &jsrval))
- return TRACE("FAIL 7"), JNI_FALSE;
-
- env->SetIntArrayRegion(rval, 0, 1, (jint*)&jsrval);
- if (env->ExceptionCheck())
- return TRACE("FAIL 8"), JNI_FALSE;
-
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1invoke");
- return JNI_TRUE;
+ PrintJSValue(cx, jsrval, "return value=");
+ JsRootedValue* returnVal = reinterpret_cast<JsRootedValue*>(jsRetValInt);
+ returnVal->setValue(jsrval);
+ return JNI_TRUE;
}
-/*
- * Class: com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method: _isWrappedDispatch
- * Signature: (II[Z)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1isWrappedDispatch
- (JNIEnv* env, jclass, jint scriptObjInt, jint jsobjval, jbooleanArray rval)
-{
- TRACE("ENTER Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1isWrappedDispatch");
-
- nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
- nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
- if (!scriptContext)
- return JNI_FALSE;
- JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
-
- jboolean r = JNI_FALSE;
- if (JSVAL_IS_OBJECT(jsobjval))
- {
- JSObject* jsobj = JSVAL_TO_OBJECT(jsobjval);
- if (JS_InstanceOf(cx, jsobj, &gwt_nativewrapper_class, 0))
- r = JNI_TRUE;
- }
-
- env->SetBooleanArrayRegion(rval, 0, 1, &r);
- if (env->ExceptionCheck())
- return JNI_FALSE;
-
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1isWrappedDispatch");
- return JNI_TRUE;
-}
/*
* Class: com_google_gwt_dev_shell_moz_LowLevelMoz
* Method: _raiseJavaScriptException
- * Signature: (II)Z
+ * Signature: (I)Z
*/
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1raiseJavaScriptException
- (JNIEnv* env, jclass, jint scriptObjInt, jint jsarg)
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1raiseJavaScriptException
+ (JNIEnv* env, jclass, jint jscontext)
{
- TRACE("ENTER Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1raiseJavaScriptException");
-
- nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
- nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
- if (!scriptContext)
- return JNI_FALSE;
- JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
- JS_SetPendingException(cx, jsarg);
-
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1raiseJavaScriptException");
- return JNI_TRUE;
+ Tracer tracer("LowLevelMoz._raiseJavaScriptException");
+ JSContext* cx = reinterpret_cast<JSContext*>(jscontext);
+ JS_SetPendingException(cx, JSVAL_NULL);
+ return JNI_TRUE;
}
/*
@@ -851,247 +442,42 @@
* Method: _registerExternalFactoryHandler
* Signature: ()Z
*/
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1registerExternalFactoryHandler
- (JNIEnv* env, jclass llClass)
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1registerExternalFactoryHandler
+ (JNIEnv* env, jclass llClass)
{
- if (!InitGlobals(env, llClass))
- return JNI_FALSE;
+ if (!InitGlobals(env, llClass))
+ return JNI_FALSE;
- TRACE("ENTER Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1registerExternalFactoryHandler");
+ // tracing isn't setup until after InitGlobals is called
+ Tracer tracer("LowLevelMoz._registerExternalFactoryHandler");
- // Register "window.external" as our own class
- if (NS_FAILED(nsComponentManager::RegisterFactory(
- kGwtExternalCID, "externalFactory", GWT_EXTERNAL_CONTRACTID,
- new nsRpExternalFactory(), PR_TRUE)))
- return JNI_FALSE;
+ char buf[256];
+ sprintf(buf, " jniEnv=%08x, llClass=%08x", (unsigned)env, (unsigned)llClass);
+ tracer.log(buf);
+
+ // Register "window.external" as our own class
+ if (NS_FAILED(nsComponentManager::RegisterFactory(
+ kGwtExternalCID, "externalFactory", GWT_EXTERNAL_CONTRACTID,
+ new nsRpExternalFactory(), PR_TRUE))) {
+ tracer.setFail("RegisterFactory failed");
+ return JNI_FALSE;
+ }
- nsCOMPtr<nsICategoryManager> categoryManager = do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
- if (!categoryManager)
- return JNI_FALSE;
+ nsCOMPtr<nsICategoryManager> categoryManager =
+ do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
+ if (!categoryManager) {
+ tracer.setFail("unable to get category manager");
+ return JNI_FALSE;
+ }
- nsXPIDLCString previous;
- if (NS_FAILED(categoryManager->AddCategoryEntry(JAVASCRIPT_GLOBAL_PROPERTY_CATEGORY,
- "external", GWT_EXTERNAL_CONTRACTID,
- PR_TRUE, PR_TRUE, getter_Copies(previous))))
- return JNI_FALSE;
+ nsXPIDLCString previous;
+ if (NS_FAILED(categoryManager->AddCategoryEntry(
+ JAVASCRIPT_GLOBAL_PROPERTY_CATEGORY, "external", GWT_EXTERNAL_CONTRACTID,
+ PR_TRUE, PR_TRUE, getter_Copies(previous)))) {
+ tracer.setFail("AddCategoryEntry failed");
+ return JNI_FALSE;
+ }
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1registerExternalFactoryHandler");
- return JNI_TRUE;
+ return JNI_TRUE;
}
-
-/*
- * Class: com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method: _unwrapDispatch
- * Signature: (II[Lcom/google/gwt/dev/shell/moz/LowLevelMoz/DispatchObject;)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1unwrapDispatch
- (JNIEnv* env, jclass, jint scriptObjInt, jint jsobjval, jobjectArray rval)
-{
- TRACE("ENTER Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1unwrapDispatch");
-
- nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
- nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
- if (!scriptContext)
- return JNI_FALSE;
- JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
-
- if (!JSVAL_IS_OBJECT(jsobjval))
- return JNI_FALSE;
-
- JSObject* jsobj = JSVAL_TO_OBJECT(jsobjval);
- if (!JS_InstanceOf(cx, jsobj, &gwt_nativewrapper_class, 0))
- return JNI_FALSE;
-
- jobject dispObj = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, jsobj));
- if (!dispObj)
- return JNI_FALSE;
-
- env->SetObjectArrayElement(rval, 0, dispObj);
- if (env->ExceptionCheck())
- return JNI_FALSE;
-
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1unwrapDispatch");
- return JNI_TRUE;
-}
-
-/*
- * Class: com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method: _unwrapJSObject
- * Signature: (I[I)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1unwrapJSObject
- (JNIEnv* env, jclass, jint nsISupportsPtr, jintArray rval)
-{
- TRACE("ENTER Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1unwrapJSObject");
-
- // MAJOR HACK: check the vtable ptr against a known "good" as a very crude RTTI
- long *vt1 = NS_REINTERPRET_CAST(long*,NS_STATIC_CAST(nsISupports*, &nsJSObjectLocker::sJSObjectLocker));
- long *vt2 = NS_REINTERPRET_CAST(long*,nsISupportsPtr);
- if (*vt1 != *vt2)
- return JNI_FALSE;
-
- // probably safe
- nsJSObjectLocker* jsObjectLocker = NS_STATIC_CAST(nsJSObjectLocker*, NS_REINTERPRET_CAST(nsISupports*,nsISupportsPtr));
- jsval r = jsObjectLocker->mVal;
- if (!JSVAL_IS_OBJECT(r))
- return JNI_FALSE;
-
- env->SetIntArrayRegion(rval, 0, 1, (jint*)&r);
- if (env->ExceptionCheck())
- return JNI_FALSE;
-
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1unwrapJSObject");
- return JNI_TRUE;
-}
-
-/*
- * Class: com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method: _wrapDispatch
- * Signature: (ILcom/google/gwt/dev/shell/moz/LowLevelMoz/DispatchObject;[I)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1wrapDispatch
- (JNIEnv* env, jclass, jint scriptObjInt, jobject dispObj, jintArray rval)
-{
- TRACE("ENTER Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1wrapDispatch");
-
- nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
- nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
- if (!scriptContext)
- return JNI_FALSE;
-
- JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
- JSObject* scriptWindow = (JSObject*)scriptObject->GetGlobalJSObject();
-
- JSObject* newObj = JS_NewObject(cx, &gwt_nativewrapper_class, 0, scriptWindow);
- if (!newObj)
- return JNI_FALSE;
-
- jobject dispObjRef = env->NewGlobalRef(dispObj);
- if (!dispObjRef || env->ExceptionCheck())
- return JNI_FALSE;
-
- if (!JS_SetPrivate(cx, newObj, dispObjRef))
- {
- env->DeleteGlobalRef(dispObjRef);
- return JNI_FALSE;
- }
-
- // forcibly setup a "toString" method to override the default
- jclass dispClass = env->GetObjectClass(dispObj);
- if (env->ExceptionCheck())
- return JS_FALSE;
-
- jmethodID getFieldMeth = env->GetMethodID(dispClass, "getField", "(Ljava/lang/String;)I");
- if (!getFieldMeth || env->ExceptionCheck())
- return JS_FALSE;
-
- jstring ident = env->NewStringUTF("@java.lang.Object::toString()");
- if (!ident || env->ExceptionCheck())
- return JS_FALSE;
-
- jsval toStringFunc = env->CallIntMethod(dispObj, getFieldMeth, ident);
- if (env->ExceptionCheck())
- return JS_FALSE;
-
- if (!JS_DefineProperty(cx, newObj, "toString", toStringFunc, JS_PropertyStub, JS_PropertyStub, JSPROP_READONLY | JSPROP_PERMANENT))
- return JNI_FALSE;
-
- env->SetIntArrayRegion(rval, 0, 1, (jint*)&newObj);
- if (env->ExceptionCheck())
- return JNI_FALSE;
-
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1wrapDispatch");
- return JNI_TRUE;
-}
-
-/*
- * Class: com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method: _wrapFunction
- * Signature: (ILjava/lang/String;Lcom/google/gwt/dev/shell/moz/LowLevelMoz/DispatchMethod;[I)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1wrapFunction
- (JNIEnv* env, jclass, jint scriptObjInt, jstring name, jobject dispMeth, jintArray rval)
-{
- TRACE("ENTER Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1wrapFunction");
-
- nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
- nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
- if (!scriptContext)
- return JNI_FALSE;
- JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
- JSObject* scriptWindow = (JSObject*)scriptObject->GetGlobalJSObject();
-
- JStringWrap nameStr(env, name);
- if (!nameStr.str())
- return JNI_FALSE;
-
- JSFunction* function = JS_NewFunction(cx, gwt_invoke, 0, JSFUN_LAMBDA, 0, nameStr.str());
- if (!function)
- return JNI_FALSE;
-
- JSObject* funObj = JS_GetFunctionObject(function);
- if (!funObj)
- return JNI_FALSE;
-
- // Create a wrapper object to hold and clean up dispMeth
- JSObject* cleanupObj = JS_NewObject(cx, &gwt_functionwrapper_class, 0, scriptWindow);
- if (!cleanupObj)
- return JNI_FALSE;
-
- jobject dispMethRef = env->NewGlobalRef(dispMeth);
- if (!dispMethRef || env->ExceptionCheck())
- return JNI_FALSE;
-
- // Store our global ref in the wrapper object
- if (!JS_SetPrivate(cx, cleanupObj, dispMethRef))
- {
- env->DeleteGlobalRef(dispMethRef);
- return JNI_FALSE;
- }
-
- // Store the wrapper object in funObj's reserved slot
- if(!JS_SetReservedSlot(cx, funObj, 0, OBJECT_TO_JSVAL(cleanupObj)))
- return JS_FALSE;
-
- env->SetIntArrayRegion(rval, 0, 1, (jint*)&funObj);
- if (env->ExceptionCheck())
- return JNI_FALSE;
-
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1wrapFunction");
- return JNI_TRUE;
-}
-
-/*
- * Class: com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method: _wrapJSObject
- * Signature: (II[I)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1wrapJSObject
- (JNIEnv* env, jclass, jint scriptObjInt, jint jsobjval, jintArray rval)
-{
- TRACE("ENTER Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1wrapJSObject");
-
- if (!JSVAL_IS_OBJECT(jsobjval))
- return JNI_FALSE;
-
- nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
- nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
- if (!scriptContext)
- return JNI_FALSE;
- JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
-
- nsISupports* objLocker = new nsJSObjectLocker(cx, jsobjval);
- if (!objLocker)
- return JNI_FALSE;
-
- jint r = (jint)objLocker;
- env->SetIntArrayRegion(rval, 0, 1, &r);
- if (env->ExceptionCheck())
- return JNI_FALSE;
-
- objLocker->AddRef();
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1wrapJSObject");
- return JNI_TRUE;
-}
-
-} // extern "C"
diff --git a/jni/linux/NativeWrapper.cpp b/jni/linux/NativeWrapper.cpp
new file mode 100644
index 0000000..8d74787
--- /dev/null
+++ b/jni/linux/NativeWrapper.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/*
+ * Defines the JavaScript classes gwt_nativewrapper_class and
+ * gwt_functionwrapper_class, which interface via JNI to Java objects.
+ */
+
+#include <jni.h>
+#include "JsRootedValue.h"
+#include "gwt-jni.h"
+#include "Tracer.h"
+
+extern "C" {
+
+static JSBool JS_DLL_CALLBACK gwt_nativewrapper_getProperty(JSContext *cx,
+ JSObject *obj, jsval id, jsval *vp)
+{
+ Tracer tracer("gwt_nativewrapper_getProperty");
+
+ if (*vp != JSVAL_VOID)
+ return JS_TRUE;
+ jclass dispClass;
+ jobject dispObj;
+ jstring ident;
+ if (!getJavaPropertyStats(cx, obj, id, dispClass, dispObj, ident)) {
+ tracer.setFail("getJavaPropertyStats failed");
+ return JS_FALSE;
+ }
+ JsRootedValue* js_rooted_value = GetFieldAsRootedValue(cx, dispClass,
+ dispObj, ident);
+ if (!js_rooted_value) {
+ tracer.setFail("can't get field");
+ return JS_FALSE;
+ }
+ *vp = js_rooted_value->getValue();
+ return JS_TRUE;
+}
+
+static void JS_DLL_CALLBACK gwt_nativewrapper_finalize(JSContext *cx,
+ JSObject *obj)
+{
+ jobject dispObj = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
+ if (dispObj)
+ savedJNIEnv->DeleteGlobalRef(dispObj);
+}
+
+static JSBool JS_DLL_CALLBACK gwt_nativewrapper_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
+{
+ Tracer tracer("gwt_nativewrapper_setProperty");
+
+ jclass dispClass;
+ jobject dispObj;
+ jstring 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)) {
+ 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,
+ gwt_nativewrapper_setProperty, JS_EnumerateStub, JS_ResolveStub,
+ JS_ConvertStub, gwt_nativewrapper_finalize,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+JSClass gwt_functionwrapper_class = {
+ "gwt_functionwrapper_class", JSCLASS_HAS_PRIVATE,
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+ JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, gwt_nativewrapper_finalize,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+} // extern "C"
diff --git a/jni/linux/Tracer.cpp b/jni/linux/Tracer.cpp
new file mode 100644
index 0000000..706dc97
--- /dev/null
+++ b/jni/linux/Tracer.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include "Tracer.h"
+
+#ifdef ENABLE_TRACING
+
+// initialize static fields
+FILE* Tracer::outfp = 0;
+JNIEnv* Tracer::jniEnv = 0;
+jclass Tracer::traceClass;
+jmethodID Tracer::traceMethod;
+int Tracer::indentation = 0;
+Tracer::LogLevel Tracer::logLevel = Tracer::LEVEL_WARNING;
+
+/*
+ * Sets a JNI environment and Java class to pass trace messages to.
+ *
+ * env - JNI environment to use for trace calls
+ * clazz - Java class, which must provide static void trace(String)
+ */
+bool Tracer::setJava(JNIEnv* env, jclass clazz) {
+ jniEnv = env;
+ if (!env) {
+ return true;
+ }
+ traceClass = static_cast<jclass>(env->NewGlobalRef(clazz));
+ if (!traceClass || env->ExceptionCheck()) {
+ return false;
+ }
+ traceMethod = env->GetStaticMethodID(traceClass, "trace",
+ "(Ljava/lang/String;)V");
+ if (!traceMethod || env->ExceptionCheck()) {
+ return false;
+ }
+
+ jstring msg = jniEnv->NewStringUTF("== Java trace started ==");
+ jniEnv->CallStaticVoidMethod(traceClass, traceMethod, msg);
+ return true;
+}
+
+/*
+ * Throw a HostedModeException and log a failure message.
+ *
+ * Creates a new HostedModeException with the failure message,
+ * and also logs the failure message
+ *
+ * env - JNI environment to throw the exception in
+ * msg - failure message
+ */
+void Tracer::throwHostedModeException(JNIEnv* env, const char* msg) {
+ setFail(msg);
+ jclass exceptionClass
+ = env->FindClass("com/google/gwt/dev/shell/HostedModeException");
+ env->ThrowNew(exceptionClass, fail_msg_);
+}
+
+#endif // ENABLE_TRACING
diff --git a/jni/linux/Tracer.h b/jni/linux/Tracer.h
new file mode 100644
index 0000000..85148b3
--- /dev/null
+++ b/jni/linux/Tracer.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+#ifndef JNI_LINUX_TRACER_H_
+#define JNI_LINUX_TRACER_H_
+
+#include <cstdio>
+#include <cstdarg>
+#include <cstring>
+#include <jni.h>
+
+// comment this out to remove almost all runtime overhead (with usual compiler
+// support) from tracing.
+#define ENABLE_TRACING
+
+/*
+ * Utility class for tracing. This class is intended to be used as follows:
+ *
+ * {
+ * Tracer tracer("method name");
+ * ... do work
+ * if (fail) {
+ * tracer.setFail("failure explanation");
+ * return;
+ * }
+ * if (fail2) {
+ * tracer.throwHostedModeException("failure explanation");
+ * return;
+ * }
+ * return;
+ * }
+ *
+ * The class automatically logs an enter message when it is created, as well
+ * as leave/fail messages when it is destroyed. Logging is performed to a
+ * file or to a Java static member function on a class (or both) -- these
+ * are configured by using static member functions setFile() and setJava().
+ *
+ * This class knows about the Java class
+ * com.google.gwt.dev.shell.HostedModeException
+ * and throws a new instance of that exception if requested.
+ */
+class Tracer {
+public:
+ enum LogLevel {
+ LEVEL_ERROR = 0,
+ LEVEL_WARNING,
+ LEVEL_NOTICE,
+ LEVEL_INFO,
+ LEVEL_DEBUG,
+ LEVEL_DEBUG_V1,
+ LEVEL_DEBUG_V2,
+ };
+protected:
+#ifdef ENABLE_TRACING
+ // static variables that specify where logging is performed. This are
+ // set by calling setFile() and setJava().
+ static FILE* outfp;
+ static JNIEnv* jniEnv;
+ static jclass traceClass;
+ static jmethodID traceMethod;
+ static int indentation;
+ static LogLevel logLevel;
+
+ // method is set when the instance is created.
+ const char* method_;
+ // fail_msg is set to indicate a failure has occurred.
+ const char* fail_msg_;
+ // level of this trace object
+ LogLevel log_level_;
+#endif
+
+public:
+ /*
+ * Set the logging level.
+ */
+ static void setLevel(LogLevel level) {
+#ifdef ENABLE_TRACING
+ logLevel = level;
+#endif
+ }
+
+protected:
+ /*
+ * Log a message (with supplied prefix) to the configured file.
+ * Only called if a file was specified and successfully opened for writing.
+ */
+ static void logFile(const char* msg) {
+#ifdef ENABLE_TRACING
+ for (int i = 0; i < indentation; ++i) {
+ putc(' ', outfp);
+ }
+ fputs(msg, outfp);
+ putc('\n', outfp);
+ fflush(outfp);
+#else
+ (void)msg; // avoid unused warning
+#endif
+ }
+
+ /*
+ * Log a message (with supplied prefix) to the configured Java class.
+ * Only called if a file was specified and successfully accessed.
+ *
+ * Call static void trace(String msg) on the configured class.
+ */
+ static void logJava(const char* msg) {
+#ifdef ENABLE_TRACING
+ // TODO(jat): fixed buffer size
+ char buf[512];
+ for (int i = 0; (i < indentation) && (i < int(sizeof(buf))); ++i) {
+ buf[i] = ' ';
+ }
+ strncpy(buf + indentation, msg, sizeof(buf) - indentation);
+ buf[sizeof(buf) - 1] = 0; // ensure null termination
+ jstring str = jniEnv->NewStringUTF(buf);
+ jniEnv->CallStaticVoidMethod(traceClass, traceMethod, str);
+#else
+ (void)msg; // avoid unused warning
+#endif
+ }
+
+ /*
+ * Log a message to a file and/or class with the default logging level.
+ *
+ * If the preprocessor symbol DISABLE_TRACING has been defined, this is
+ * completely removed from the code path.
+ */
+ void logPrefix(const char* prefix) {
+#ifdef ENABLE_TRACING
+ logPrefix(prefix, log_level_);
+#else
+ (void)prefix; // avoid unused warning
+#endif
+ }
+
+ /*
+ * Log a message to a file and/or class.
+ *
+ * If the preprocessor symbol DISABLE_TRACING has been defined, this is
+ * completely removed from the code path.
+ */
+ void logPrefix(const char* prefix, LogLevel level) {
+#ifdef ENABLE_TRACING
+ if (level>logLevel) return;
+ log("%-5.5s %s%s%s", prefix, method_, fail_msg_ ? ": " : "",
+ fail_msg_ ? fail_msg_ : "");
+#endif
+ }
+
+public:
+ /*
+ * Create an instance with the specified method name and no failure
+ * message. Log an ENTER message.
+ */
+ Tracer(const char* method, LogLevel log_level = LEVEL_ERROR)
+#ifdef ENABLE_TRACING
+ : method_(method), fail_msg_(0), log_level_(log_level) {
+ log("ENTER %s", method);
+ indentation++;
+#else
+ { (void)method; (void)log_level; // avoid unused warnings
+#endif
+ }
+
+ /*
+ * Create an instance with the specified method name and no failure
+ * message. Log an ENTER message and the this pointer.
+ */
+ Tracer(const char* method, const void* objThis,
+ LogLevel log_level = LEVEL_ERROR)
+#ifdef ENABLE_TRACING
+ : method_(method), fail_msg_(0), log_level_(log_level) {
+ log("ENTER %s(this=%08x)", method, unsigned(objThis));
+ indentation++;
+#else
+ { (void)method; (void)objThis; (void)log_level; // avoid unused warnings
+#endif
+ }
+
+ /*
+ * Destroy the instance and log a fail or leave message.
+ */
+ ~Tracer() {
+#ifdef ENABLE_TRACING
+ --indentation;
+ if(fail_msg_) {
+ logPrefix("*FAIL", LEVEL_ERROR);
+ } else {
+ logPrefix("LEAVE");
+ }
+#endif
+ }
+
+ /*
+ * Specify a filename to receive logging output. Close any previously
+ * opened file. If a null filename is passed, disable logging to a
+ * file.
+ *
+ * filename - the file path to receive logging output. This file is
+ * truncated if it already exists.
+ *
+ * Returns false on failure.
+ */
+ static bool setFile(const char* filename) {
+#ifdef ENABLE_TRACING
+ if (outfp) {
+ fclose(outfp);
+ outfp = 0;
+ }
+ if (!filename) {
+ return true;
+ }
+ outfp = fopen(filename, "w");
+ if (!outfp) {
+ return false;
+ }
+ fprintf(outfp, "== started logging ==\n");
+ fflush(outfp);
+#else
+ (void)filename; // avoid unused warning
+#endif
+ return true;
+ }
+
+ /*
+ * Specify a Java class to receive logging output. The supplied class
+ * must have a static void trace(String) member function which is called
+ * for output. Logging to a Java class is disabled if the supplied JNI
+ * environment is null.
+ *
+ * env - JNI environment
+ * clazz - the Java class to receive logging output
+ *
+ * Returns false on failure.
+ */
+ static bool setJava(JNIEnv* env, jclass clazz)
+#ifdef ENABLE_TRACING
+ ;
+#else
+ // inline a null body if we aren't debugging; avoid unused warnings
+ { (void)env; (void)clazz; return true; }
+#endif
+
+ /*
+ * Set a failure message, overwriting any previously specified failure
+ * message. Passing a null string will remove any previous failure
+ * notification.
+ */
+ void setFail(const char* fail_msg) {
+#ifdef ENABLE_TRACING
+ fail_msg_ = fail_msg;
+#else
+ (void)fail_msg; // avoid unused warning
+#endif
+ }
+
+ /*
+ * Throw a Java HostedModeException as well as set a failure message to
+ * be logged.
+ *
+ * env - JNI environment to throw exception into
+ * fail_msg - failure message
+ */
+ void throwHostedModeException(JNIEnv* env, const char* fail_msg);
+
+ /*
+ * Log an arbitrary message.
+ */
+ static void log(const char* format, ...) {
+#ifdef ENABLE_TRACING
+ va_list args;
+ va_start(args, format);
+ char msg[512]; // TODO(jat): fixed size buffer
+ vsnprintf(msg, sizeof(msg), format, args);
+ msg[sizeof(msg) - 1] = 0; // ensure null termination
+ if(outfp) logFile(msg);
+ if(jniEnv) logJava(msg);
+ va_end(args);
+#else
+ (void)format; // avoid unused warning
+#endif
+ }
+};
+
+#endif /* JNI_LINUX_TRACER_H_ */
diff --git a/jni/linux/gwt-jni.h b/jni/linux/gwt-jni.h
new file mode 100644
index 0000000..e0a627a
--- /dev/null
+++ b/jni/linux/gwt-jni.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+#ifndef JNI_LINUX_GWT_JNI_H_
+#define JNI_LINUX_GWT_JNI_H_
+
+#include <jni.h>
+#include "JsRootedValue.h"
+#include "JStringWrap.h"
+
+extern JNIEnv* savedJNIEnv;
+extern jclass lowLevelMozClass;
+
+// JavaScript class objects
+extern JSClass gwt_nativewrapper_class;
+extern JSClass gwt_functionwrapper_class;
+
+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);
+
+#endif /*JNI_LINUX_GWT_JNI_H_*/
diff --git a/jni/linux/mozilla-headers.h b/jni/linux/mozilla-headers.h
new file mode 100644
index 0000000..b115794
--- /dev/null
+++ b/jni/linux/mozilla-headers.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2007 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+// include all of the necessary Mozilla headers.
+#include "mozilla-config.h"
+#include "nsIServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsICategoryManager.h"
+#include "nsIScriptNameSpaceManager.h"
+#include "nsIScriptObjectOwner.h"
+#include "nsIScriptGlobalObject.h"
+#include "nsIScriptContext.h"
+#include "nsIDOMWindow.h"
+#include "nsIXPConnect.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
diff --git a/jni/linux/prebuilt/libgwt-ll.so b/jni/linux/prebuilt/libgwt-ll.so
index ee03c5e..596f2f1 100755
--- a/jni/linux/prebuilt/libgwt-ll.so
+++ b/jni/linux/prebuilt/libgwt-ll.so
Binary files differ
diff --git a/jni/mac/Makefile b/jni/mac/Makefile
index 1f59692..2787428 100644
--- a/jni/mac/Makefile
+++ b/jni/mac/Makefile
@@ -75,6 +75,8 @@
##
all: $(GWT_LL_LIB) $(GWT_WEBKIT_LIB) $(SWT_LIBS)
+staging: all
+ cp libgwt-*.jnilib ../../build/staging/gwt-mac-0.0.0/
##
# Copy WebKit binary frameworks locally.
##
diff --git a/jni/mac/gwt-webkit.cpp b/jni/mac/gwt-webkit.cpp
index ce5a797..86b61d4 100644
--- a/jni/mac/gwt-webkit.cpp
+++ b/jni/mac/gwt-webkit.cpp
@@ -28,6 +28,38 @@
using namespace KJS;
+static void PrintJSValue(JSValue* val, char* prefix="") {
+ static const char* typeStrings[]={
+ "unspecified",
+ "number",
+ "boolean",
+ "undefined",
+ "null",
+ "string",
+ "object",
+ "getter/setter",
+ };
+ JSType type = val->type();
+ const char* typeString=typeStrings[type];
+ char buf[256];
+ char* p = buf;
+ p += snprintf(p, sizeof(buf)-(p-buf), "%s%s: ", prefix, typeString);
+ if (val->isNumber()) {
+ p += snprintf(p, sizeof(buf)-(p-buf), "%lf", val->getNumber());
+ } else if(val->isString()) {
+ CString str(val->getString().UTF8String());
+ p += snprintf(p, sizeof(buf)-(p-buf), "%.*s", str.size(), str.c_str());
+ } else if(val->isObject()) {
+ const JSObject* obj = val->getObject();
+ const ClassInfo* cinfo = obj->classInfo();
+ const char* cname = cinfo ? cinfo->className : "js object";
+ p += snprintf(p, sizeof(buf)-(p-buf), "%s @ %08x", cname, unsigned(obj));
+ } else if(val->isBoolean()) {
+ p += snprintf(p, sizeof(buf)-(p-buf), "%s", val->getBoolean() ? "true" : "false");
+ }
+ TRACE(buf);
+}
+
/*
* Class: com_google_gwt_dev_shell_mac_LowLevelSaf
* Method: isNull
@@ -35,13 +67,13 @@
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_isNull
(JNIEnv *env, jclass, jint jsval) {
- TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__isNull");
+ TRACE("ENTER LowLevelSaf__isNull");
JSValue* val = (JSValue*)jsval;
if (!val)
return JNI_FALSE;
- TRACE("SUCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__isNull");
+ TRACE("SUCCESS LowLevelSaf__isNull");
return val->isNull();
}
@@ -50,14 +82,15 @@
* Method: isUndefined
* Signature: (I)Z
*/
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_isUndefined
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_mac_LowLevelSaf_isUndefined
(JNIEnv *env, jclass, jint jsval) {
- TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__isUndefined");
+ TRACE("ENTER LowLevelSaf__isUndefined");
JSValue* val = (JSValue*)jsval;
if (!val)
return JNI_FALSE;
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__isUndefined");
+ TRACE("SUCCESS LowLevelSaf__isUndefined");
return val->isUndefined();
}
@@ -66,13 +99,47 @@
* Method: jsNull
* Signature: ()I
*/
-JNIEXPORT jint JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_jsNull
+extern "C" JNIEXPORT jint JNICALL
+Java_com_google_gwt_dev_shell_mac_LowLevelSaf_jsNull
(JNIEnv *, jclass) {
return (jint)jsNull();
}
/*
* Class: com_google_gwt_dev_shell_mac_LowLevelSaf
+ * Method: getTypeString
+ * Signature: (I)Ljava/lang/String;
+ */
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_google_gwt_dev_shell_mac_LowLevelSaf_getTypeString
+ (JNIEnv *env, jclass, jint jsval) {
+ static const char* typeStrings[]={
+ "unspecified",
+ "number",
+ "boolean",
+ "undefined",
+ "null",
+ "string",
+ "object",
+ "getter/setter",
+ };
+ JSValue* val = (JSValue*)jsval;
+ if (!val)
+ return 0;
+ JSType type = val->type();
+ const char* typeString=typeStrings[type];
+ if (type == ObjectType) {
+ if (val->isObject(&DispWrapper::info)) {
+ typeString = "Java object";
+ } else {
+ typeString = "JS object";
+ }
+ }
+ return env->NewStringUTF(typeString);
+}
+
+/*
+ * Class: com_google_gwt_dev_shell_mac_LowLevelSaf
* Method: jsUndefined
* Signature: ()I
*/
@@ -88,7 +155,7 @@
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1coerceToBoolean
(JNIEnv * env, jclass, jint execState, jint jsval, jbooleanArray rval) {
- TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1coerceToBoolean");
+ TRACE("ENTER LowLevelSaf__1coerceToBoolean");
if (!execState || !jsval)
return JNI_FALSE;
@@ -98,7 +165,7 @@
if (env->ExceptionCheck())
return JNI_FALSE;
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1coerceToBoolean");
+ TRACE("SUCCESS LowLevelSaf__1coerceToBoolean");
return JNI_TRUE;
}
@@ -109,7 +176,7 @@
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1coerceToDouble
(JNIEnv *env, jclass, jint execState, jint jsval, jdoubleArray rval) {
- TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1coerceToDouble");
+ TRACE("ENTER LowLevelSaf__1coerceToDouble");
if (!execState || !jsval)
return JNI_FALSE;
@@ -119,7 +186,7 @@
if (env->ExceptionCheck())
return JNI_FALSE;
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1coerceToDouble");
+ TRACE("SUCCESS LowLevelSaf__1coerceToDouble");
return JNI_TRUE;
}
@@ -130,7 +197,7 @@
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1coerceToString
(JNIEnv *env, jclass, jint execState, jint jsval, jobjectArray rval) {
- TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1coerceToString");
+ TRACE("ENTER LowLevelSaf__1coerceToString");
JSValue *val = (JSValue*)jsval;
if (!execState || !val)
@@ -152,7 +219,7 @@
if (env->ExceptionCheck())
return JNI_FALSE;
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1coerceToString");
+ TRACE("SUCCESS LowLevelSaf__1coerceToString");
return JNI_TRUE;
}
@@ -163,7 +230,10 @@
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1convertBoolean
(JNIEnv *env, jclass, jboolean jval, jintArray rval) {
- TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1convertBoolean");
+ TRACE("ENTER LowLevelSaf__1convertBoolean");
+ char buf[256];
+ snprintf(buf, sizeof(buf), " val=%s", jval ? "true" : "false");
+ TRACE(buf);
JSValue *jsval = (jval == JNI_FALSE) ? jsBoolean(false) : jsBoolean(true);
if (!jsval)
@@ -173,7 +243,7 @@
if (env->ExceptionCheck())
return JNI_FALSE;
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1convertBoolean");
+ TRACE("SUCCESS LowLevelSaf__1convertBoolean");
return JNI_TRUE;
}
@@ -184,7 +254,10 @@
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1convertDouble
(JNIEnv *env, jclass, jdouble jval, jintArray rval) {
- TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1convertDouble");
+ TRACE("ENTER LowLevelSaf__1convertDouble");
+ char buf[256];
+ snprintf(buf, sizeof(buf), " val=%lf", jval);
+ TRACE(buf);
JSValue *jsval = jsNumber(jval);
if (!jsval)
@@ -194,7 +267,7 @@
if (env->ExceptionCheck())
return JNI_FALSE;
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1convertDouble");
+ TRACE("SUCCESS LowLevelSaf__1convertDouble");
return JNI_TRUE;
}
@@ -205,11 +278,14 @@
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1convertString
(JNIEnv *env, jclass, jstring jval, jintArray rval) {
- TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1convertString");
+ TRACE("ENTER LowLevelSaf__1convertString");
JStringWrap jstr(env, jval);
if (!jstr.jstr())
return JNI_FALSE;
+ char buf[256];
+ snprintf(buf, sizeof(buf), " val=%s", jstr.str());
+ TRACE(buf);
JSValue *jsval = jsString(UString((const UChar*)jstr.jstr(), jstr.length()));
/*
@@ -222,11 +298,14 @@
if (!jsval)
return JNI_FALSE;
+ snprintf(buf, sizeof(buf), " return={%08x} ", unsigned(jsval));
+ PrintJSValue(jsval, buf);
+
env->SetIntArrayRegion(rval,0,1,(const jint*)&jsval);
if (env->ExceptionCheck())
return JNI_FALSE;
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1convertString");
+ TRACE("SUCCESS LowLevelSaf__1convertString");
return JNI_TRUE;
}
@@ -237,20 +316,23 @@
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1executeScript
(JNIEnv* env, jclass, jint execState, jstring code) {
- TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1executeScript");
+ TRACE("ENTER LowLevelSaf__1executeScript");
if (!execState || !code)
return JNI_FALSE;
JStringWrap jcode(env, code);
if (!jcode.jstr())
return JNI_FALSE;
-
+ char buf[1024];
+ snprintf(buf, sizeof(buf), " code=%s", jcode.str());
+ TRACE(buf);
+
Interpreter* interp = ((ExecState*)execState)->dynamicInterpreter();
if (!interp)
return JNI_FALSE;
interp->evaluate(UString(), 0, (const UChar*)jcode.jstr(), jcode.length());
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1executeScript");
+ TRACE("SUCCESS LowLevelSaf__1executeScript");
return JNI_TRUE;
}
@@ -261,7 +343,7 @@
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1executeScriptWithInfo
(JNIEnv* env, jclass, jint execState, jstring code, jstring file, jint line) {
- TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1executeScriptWithInfo");
+ TRACE("ENTER LowLevelSaf__1executeScriptWithInfo");
if (!execState || !code || !file)
return JNI_FALSE;
@@ -272,13 +354,17 @@
JStringWrap jfile(env, file);
if (!jcode.jstr())
return JNI_FALSE;
-
+
+ char buf[1024];
+ snprintf(buf, sizeof(buf), " code=%s, file=%s, line=%d", jcode.str(), jfile.str(), line);
+ TRACE(buf);
+
Interpreter* interp = ((ExecState*)execState)->dynamicInterpreter();
if (!interp)
return JNI_FALSE;
interp->evaluate(UString((const UChar*)jfile.jstr(), jfile.length()), line, (const UChar*)jcode.jstr(), jcode.length());
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1executeScriptWithInfo");
+ TRACE("SUCCESS LowLevelSaf__1executeScriptWithInfo");
return JNI_TRUE;
}
@@ -309,7 +395,7 @@
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1getGlobalExecState
(JNIEnv *env, jclass, jint scriptObject, jintArray rval) {
- TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1getGlobalExecState");
+ TRACE("ENTER LowLevelSaf__1getGlobalExecState");
if (!scriptObject || !((JSValue*)scriptObject)->isObject())
return JNI_FALSE;
@@ -322,7 +408,7 @@
env->SetIntArrayRegion(rval, 0, 1, (jint*)&execState);
if (env->ExceptionCheck())
return JNI_FALSE;
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1getGlobalExecState");
+ TRACE("SUCCESS LowLevelSaf__1getGlobalExecState");
return JNI_TRUE;
}
@@ -352,7 +438,7 @@
}
#ifdef FILETRACE
- gout = fopen("gwt-ll.log", "w");
+ gout = fopen("/tmp/gwt-ll.log", "w");
filetrace("LOG STARTED");
#endif // FILETRACE
@@ -372,18 +458,22 @@
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1invoke
(JNIEnv* env, jclass, jint jsexecState, jint jsScriptObject, jstring method, jint jsthis, jint argc, jintArray argv, jintArray rval) {
- TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1invoke");
+ TRACE("ENTER LowLevelSaf__1invoke");
if (!jsexecState || !jsScriptObject || !method || !rval)
return JNI_FALSE;
-
+ JStringWrap jmethod(env, method);
+ char buf[256];
+ snprintf(buf, sizeof(buf), "sciptObject=%08x, method=%s, argc=%d",
+ jsScriptObject, jmethod.str(), argc);
+ TRACE(buf);
+ PrintJSValue((JSValue*)jsthis, " jsthis=");
ExecState* execState = (ExecState*)jsexecState;
JSObject* scriptObj = (JSObject*)jsScriptObject;
if (!scriptObj->isObject())
return JNI_FALSE;
- JStringWrap jmethod(env, method);
if (!jmethod.jstr())
return JNI_FALSE;
@@ -408,7 +498,9 @@
env->GetIntArrayRegion(argv, i, 1, &argi);
if (env->ExceptionCheck())
return JNI_FALSE;
-
+ snprintf(buf, sizeof(buf), " arg[%d]={%08x} ", i, argi);
+ TRACE(buf);
+ PrintJSValue((JSValue*)argi, buf);
if (argi) {
args.append((JSValue*)argi);
} else {
@@ -417,16 +509,68 @@
}
JSValue* result = func->call(execState, thisObj, args);
- env->SetIntArrayRegion(rval, 0, 1, (jint*)&result);
- if (env->ExceptionCheck())
- return JNI_FALSE;
+ gcProtectNullTolerant(result);
+ PrintJSValue(result, " return=");
+ env->SetIntArrayRegion(rval, 0, 1, (jint*)&result);
+ if (env->ExceptionCheck())
+ return JNI_FALSE;
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1invoke");
+ TRACE("SUCCESS LowLevelSaf__1invoke");
return JNI_TRUE;
}
/*
* Class: com_google_gwt_dev_shell_mac_LowLevelSaf
+ * Method: isBoolean
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_mac_LowLevelSaf_isBoolean
+ (JNIEnv *, jclass, jint jsval) {
+ if (!jsval)
+ return JNI_FALSE;
+ return ((JSValue*)jsval)->isBoolean() ? JNI_TRUE : JNI_FALSE;
+}
+
+/*
+ * Class: com_google_gwt_dev_shell_mac_LowLevelSaf
+ * Method: isNumber
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_mac_LowLevelSaf_isNumber
+ (JNIEnv *, jclass, jint jsval) {
+ if (!jsval)
+ return JNI_FALSE;
+ return ((JSValue*)jsval)->isNumber() ? JNI_TRUE : JNI_FALSE;
+}
+
+/*
+ * Class: com_google_gwt_dev_shell_mac_LowLevelSaf
+ * Method: _isString
+ * Signature: (I)Z
+ *
+ * Must return true for JavaScript String objects as well as string primitives.
+ */
+JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1isString
+ (JNIEnv *, jclass, jint jsval) {
+ if (!jsval)
+ return JNI_FALSE;
+ JSValue* jsValue = reinterpret_cast<JSValue*>(jsval);
+ if(jsValue->isString()) return JNI_TRUE;
+ if(jsValue->isObject()) {
+ const JSObject* obj = jsValue->getObject();
+ const ClassInfo* cinfo = obj->classInfo();
+ if (cinfo && !strcmp(cinfo->className, "String")) {
+ return JNI_TRUE;
+ }
+ }
+ return JNI_FALSE;
+}
+
+/*
+ * Class: com_google_gwt_dev_shell_mac_LowLevelSaf
* Method: _isObject
* Signature: (I)Z
*/
@@ -439,24 +583,12 @@
/*
* Class: com_google_gwt_dev_shell_mac_LowLevelSaf
- * Method: _isString
- * Signature: (I)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1isString
- (JNIEnv *, jclass, jint jsval) {
- if (!jsval)
- return JNI_FALSE;
- return ((JSValue*)jsval)->isString() ? JNI_TRUE : JNI_FALSE;
-}
-
-/*
- * Class: com_google_gwt_dev_shell_mac_LowLevelSaf
* Method: _isWrappedDispatch
* Signature: (I[Z)Z
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1isWrappedDispatch
(JNIEnv* env, jclass, jint jsval, jbooleanArray rval) {
- TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1isWrappedDispatch");
+ TRACE("ENTER LowLevelSaf__1isWrappedDispatch");
if (!jsval)
return JNI_FALSE;
@@ -467,7 +599,7 @@
if (env->ExceptionCheck())
return JNI_FALSE;
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1isWrappedDispatch");
+ TRACE("SUCCESS LowLevelSaf__1isWrappedDispatch");
return JNI_TRUE;
}
@@ -498,13 +630,13 @@
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1raiseJavaScriptException
(JNIEnv *env, jclass, jint execState, jint jsval) {
- TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1raiseJavaScriptException");
+ TRACE("ENTER LowLevelSaf__1raiseJavaScriptException");
if (!execState || !jsval)
return JNI_FALSE;
((ExecState*)execState)->setException((JSValue*)jsval);
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1raiseJavaScriptException");
+ TRACE("SUCCESS LowLevelSaf__1raiseJavaScriptException");
return JNI_TRUE;
}
@@ -515,7 +647,7 @@
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1unwrapDispatch
(JNIEnv* env, jclass, jint jsval, jobjectArray rval) {
- TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1unwrapDispatch");
+ TRACE("ENTER LowLevelSaf__1unwrapDispatch");
if (!jsval)
return JNI_FALSE;
@@ -528,7 +660,7 @@
if (env->ExceptionCheck())
return JNI_FALSE;
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1unwrapDispatch");
+ TRACE("SUCCESS LowLevelSaf__1unwrapDispatch");
return JNI_TRUE;
}
@@ -539,7 +671,7 @@
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1wrapDispatch
(JNIEnv* env, jclass, jobject dispObj, jintArray rval) {
- TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1wrapDispatch");
+ TRACE("ENTER LowLevelSaf__1wrapDispatch");
jobject dispObjRef = env->NewGlobalRef(dispObj);
if (!dispObjRef || env->ExceptionCheck())
@@ -553,11 +685,15 @@
* out in favor of better memory mgmt scheme.
*/
gcProtectNullTolerant(wrapper);
+ char buf[256];
+ snprintf(buf, sizeof(buf), " return={%08x} ", unsigned(wrapper));
+ PrintJSValue((JSValue*)wrapper, buf);
+
env->SetIntArrayRegion(rval, 0, 1, (jint*)&wrapper);
if (env->ExceptionCheck())
return JNI_FALSE;
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1wrapDispatch");
+ TRACE("SUCCESS LowLevelSaf__1wrapDispatch");
return JNI_TRUE;
}
@@ -568,7 +704,7 @@
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1wrapFunction
(JNIEnv* env, jclass, jstring name, jobject dispMeth, jintArray rval) {
- TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1wrapFunction");
+ TRACE("ENTER LowLevelSaf__1wrapFunction");
jobject dispMethRef = env->NewGlobalRef(dispMeth);
if (!dispMethRef || env->ExceptionCheck())
@@ -586,11 +722,14 @@
* out in favor of better memory mgmt scheme.
*/
gcProtectNullTolerant(wrapper);
+ char buf[256];
+ snprintf(buf, sizeof(buf), " return={%08x} ", unsigned(wrapper));
+ PrintJSValue((JSValue*)wrapper, buf);
env->SetIntArrayRegion(rval, 0, 1, (jint*)&wrapper);
if (env->ExceptionCheck())
return JNI_FALSE;
- TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1wrapFunction");
+ TRACE("SUCCESS LowLevelSaf__1wrapFunction");
return JNI_TRUE;
}
diff --git a/jni/mac/gwt-webkit.h b/jni/mac/gwt-webkit.h
index 3ba982b..1cf9cb8 100644
--- a/jni/mac/gwt-webkit.h
+++ b/jni/mac/gwt-webkit.h
@@ -30,6 +30,7 @@
//#define FILETRACE
//#define JAVATRACE
+
#if defined(FILETRACE) && defined(JAVATRACE)
#define TRACE(s) filetrace(s),javatrace(s)
#elif defined(FILETRACE)
diff --git a/jni/mac/prebuilt/libgwt-webkit.jnilib b/jni/mac/prebuilt/libgwt-webkit.jnilib
index 0fdd5b4..328bcb3 100755
--- a/jni/mac/prebuilt/libgwt-webkit.jnilib
+++ b/jni/mac/prebuilt/libgwt-webkit.jnilib
Binary files differ