Add back IE proxy handling for CheckForUpdates. Also remove branch-info.txt inadvertently committed to trunk. Patch by: scottb Review by: jat git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@6390 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/branch-info.txt b/branch-info.txt deleted file mode 100644 index bf03fda..0000000 --- a/branch-info.txt +++ /dev/null
@@ -1,21 +0,0 @@ -Created from trunk@6101 -======================== -svn copy -r6226 \ - https://google-web-toolkit.googlecode.com/svn/branches/farewellSwt \ - https://google-web-toolkit.googlecode.com/svn/changes/jat/noswt-merge6200 - -Merges -====== -svn merge https://google-web-toolkit.googlecode.com/svn/trunk \ - -r6101:6142 -svn merge --ignore-ancestry -r6142:6200 \ - https://google-web-toolkit.googlecode.com/svn/trunk - (via changes/jat/noswt-merge6200) -svn merge https://google-web-toolkit.googlecode.com/svn/trunk \ - -r6200:6239 . -svn merge https://google-web-toolkit.googlecode.com/svn/trunk \ - -r6239:6249 . -svn merge https://google-web-toolkit.googlecode.com/svn/trunk \ - --ignore-ancestry -r6249:6270 . -svn merge --ignore-ancestry -r6280:6359 \ - https://google-web-toolkit.googlecode.com/svn/changes/jat/abstractui
diff --git a/build.xml b/build.xml index e468a51..9c97067 100755 --- a/build.xml +++ b/build.xml
@@ -38,6 +38,7 @@ <call-subproject subproject="dev" subtarget="build" /> <call-subproject subproject="user" subtarget="build" /> <call-subproject subproject="servlet" subtarget="build" /> + <call-subproject subproject="jni" subtarget="build" /> </target> <target name="dist" depends="build, doc" description="[action] Make all the distributions"> @@ -68,6 +69,10 @@ <gwt.ant dir="servlet" /> </target> + <target name="jni" description="[subdir] Builds (or runs ${target} if set) jni for all platforms"> + <gwt.ant dir="jni" /> + </target> + <target name="doc" description="[subdir] Builds (or runs ${target} if set) the doc"> <call-subproject subproject="user" subtarget="build" /> <gwt.ant dir="doc" /> @@ -87,6 +92,7 @@ <call-subproject subproject="user" subtarget="build"/> <call-subproject subproject="servlet" subtarget="build"/> <call-subproject subproject="tools" subtarget="build"/> + <call-subproject subproject="jni" subtarget="build"/> <call-subproject subproject="samples" subtarget="build"/> </target>
diff --git a/dev/core/src/com/google/gwt/dev/shell/CheckForUpdates.java b/dev/core/src/com/google/gwt/dev/shell/CheckForUpdates.java index 30c699a..ac9bf00 100644 --- a/dev/core/src/com/google/gwt/dev/shell/CheckForUpdates.java +++ b/dev/core/src/com/google/gwt/dev/shell/CheckForUpdates.java
@@ -18,6 +18,7 @@ import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.TreeLogger.HelpInfo; import com.google.gwt.dev.About; +import com.google.gwt.dev.shell.ie.CheckForUpdatesIE6; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -34,7 +35,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; -import java.lang.reflect.Constructor; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; @@ -190,15 +190,6 @@ // The real URL that should be used. private static final String QUERY_URL = "http://tools.google.com/webtoolkit/currentversion.xml"; - /** - * All of these classes must extend CheckForUpdates. Note that currently only - * IE has a custom implementation (to handle proxies) and that CheckForUpdates - * must be the last one in the list. - */ - private static final String[] updaterClassNames = new String[] { - "com.google.gwt.dev.shell.ie.CheckForUpdatesIE6", - "com.google.gwt.dev.shell.CheckForUpdates"}; - public static FutureTask<UpdateResult> checkForUpdatesInBackgroundThread( final TreeLogger logger, final long minCheckMillis) { final String entryPoint = computeEntryPoint(); @@ -243,25 +234,12 @@ public static CheckForUpdates createUpdateChecker(TreeLogger logger, String entryPoint) { - try { - for (int i = 0; i < updaterClassNames.length; i++) { - try { - Class<? extends CheckForUpdates> clazz = Class.forName( - updaterClassNames[i]).asSubclass(CheckForUpdates.class); - Constructor<? extends CheckForUpdates> ctor = clazz.getDeclaredConstructor(new Class[] { - TreeLogger.class, String.class}); - CheckForUpdates checker = ctor.newInstance(new Object[] { - logger, entryPoint}); - return checker; - } catch (Exception e) { - // Other exceptions can occur besides ClassNotFoundException, - // so ignore them all so we can find a functional updater. - } - } - } catch (Throwable e) { - // silently ignore any errors + // Windows has a custom implementation to handle proxies. + if (System.getProperty("os.name").toLowerCase().contains("win")) { + return new CheckForUpdatesIE6(logger, entryPoint); + } else { + return new CheckForUpdates(logger, entryPoint); } - return null; } public static void logUpdateAvailable(TreeLogger logger, @@ -329,16 +307,6 @@ * about an update if available. * * @param logger TreeLogger to use - */ - public CheckForUpdates(TreeLogger logger) { - this(logger, null); - } - - /** - * Create an update checker which will poll a server URL and log a message - * about an update if available. - * - * @param logger TreeLogger to use * @param entryPoint the name of the main entry point used for this execution */ public CheckForUpdates(TreeLogger logger, String entryPoint) {
diff --git a/dev/core/src/com/google/gwt/dev/shell/LowLevel.java b/dev/core/src/com/google/gwt/dev/shell/LowLevel.java deleted file mode 100644 index cab33b6..0000000 --- a/dev/core/src/com/google/gwt/dev/shell/LowLevel.java +++ /dev/null
@@ -1,155 +0,0 @@ -/* - * 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. - */ -package com.google.gwt.dev.shell; - -import java.lang.reflect.Field; - -/** - * Various cross-platform low-level helper methods. - */ -public class LowLevel { - - public static final String PACKAGE_PATH = LowLevel.class.getPackage().getName().replace( - '.', '/').concat("/"); - - private static boolean sInitialized = false; - - /** - * Clobbers a field on an object to which we do not have access. - */ - public static void clobberFieldObjectValue(Class<?> victimClass, - Object victimObject, String fieldName, Object value) { - Throwable rethrow = null; - try { - Field victimField = victimClass.getDeclaredField(fieldName); - victimField.setAccessible(true); - victimField.set(victimObject, value); - return; - } catch (IllegalArgumentException e) { - rethrow = e; - } catch (SecurityException e) { - rethrow = e; - } catch (IllegalAccessException e) { - rethrow = e; - } catch (NoSuchFieldException e) { - rethrow = e; - } - throw new RuntimeException("Unable to clobber field '" + fieldName - + "' from class " + victimClass.getName(), rethrow); - } - - /** - * Clobbers a field on an object to which we do not have access. - */ - public static void clobberFieldObjectValue(Object victim, String fieldName, - Object value) { - if (victim != null) { - clobberFieldObjectValue(victim.getClass(), victim, fieldName, value); - } else { - throw new NullPointerException("victim must not be null"); - } - } - - /** - * Deletes a global ref on the specified object, invalidating its handle. - */ - public static void deleteGlobalRefInt(int globalRef) { - _deleteGlobalRefInt(globalRef); - } - - /** - * Gets an environment variable. This is to replace the deprecated - * {@link System#getenv(java.lang.String)}. - */ - public static String getEnv(String key) { - return _getEnv(key); - } - - public static synchronized void init() { - if (!sInitialized) { - // TODO(jat): load native code for IE proxy handling? - sInitialized = true; - } - } - - /** - * Creates a global ref on the specified object, returning its int handle. - */ - public static int newGlobalRefInt(Object o) { - return _newGlobalRefInt(o); - } - - /** - * Converts a global ref handle from {@link #newGlobalRefInt(Object)} into an - * Object. Use at your own risk. - */ - public static Object objFromGlobalRefInt(int globalRef) { - return _objFromGlobalRefInt(globalRef); - } - - /** - * Snatches a field from an object to which we do not have access. - */ - @SuppressWarnings("unchecked") - public static <T> T snatchFieldObjectValue(Class<?> victimClass, - Object victimObject, String fieldName) { - Throwable rethrow = null; - try { - Field victimField = victimClass.getDeclaredField(fieldName); - victimField.setAccessible(true); - return (T) victimField.get(victimObject); - } catch (IllegalArgumentException e) { - rethrow = e; - } catch (SecurityException e) { - rethrow = e; - } catch (IllegalAccessException e) { - rethrow = e; - } catch (NoSuchFieldException e) { - rethrow = e; - } - throw new RuntimeException("Unable to snatch field '" + fieldName - + "' from class " + victimClass.getName(), rethrow); - } - - /** - * Snatches a field from an object to which we do not have access. - */ - public static Object snatchFieldObjectValue(Object victim, String fieldName) { - if (victim != null) { - return snatchFieldObjectValue(victim.getClass(), victim, fieldName); - } else { - throw new NullPointerException("victim must not be null"); - } - } - - // CHECKSTYLE_NAMING_OFF - private static native void _deleteGlobalRefInt(int globalRef); - - private static native String _getEnv(String key); - - private static native int _newGlobalRefInt(Object o); - - private static native Object _objFromGlobalRefInt(int globalRef); - - // CHECKSTYLE_NAMING_ON - - /** - * This class is not instantiable. - */ - private LowLevel() { - } - -}
diff --git a/dev/core/src/com/google/gwt/dev/shell/ie/CheckForUpdatesIE6.java b/dev/core/src/com/google/gwt/dev/shell/ie/CheckForUpdatesIE6.java new file mode 100644 index 0000000..6e400ba --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/shell/ie/CheckForUpdatesIE6.java
@@ -0,0 +1,40 @@ +/* + * 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. + */ +package com.google.gwt.dev.shell.ie; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.dev.shell.CheckForUpdates; + +/** + * IE6 implementation of the update checker. + */ +public class CheckForUpdatesIE6 extends CheckForUpdates { + + public CheckForUpdatesIE6(TreeLogger logger, String entryPoint) { + super(logger, entryPoint); + } + + @Override + protected byte[] doHttpGet(TreeLogger branch, String userAgent, String url) { + if (LowLevelIE6.init()) { + return LowLevelIE6.httpGet(branch, userAgent, url, + System.getProperty(PROPERTY_DEBUG_HTTP_GET) != null); + } else { + return super.doHttpGet(branch, userAgent, url); + } + } + +}
diff --git a/dev/core/src/com/google/gwt/dev/shell/ie/LowLevelIE6.java b/dev/core/src/com/google/gwt/dev/shell/ie/LowLevelIE6.java new file mode 100644 index 0000000..dd4b740 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/shell/ie/LowLevelIE6.java
@@ -0,0 +1,89 @@ +/* + * 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. + */ +package com.google.gwt.dev.shell.ie; + +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.util.tools.Utility; + +import java.io.File; +import java.io.IOException; + +/** + * Various low-level helper methods for dealing with COM and such. + */ +class LowLevelIE6 { + + private static boolean sInitialized = false; + + /** + * Does an HTTP GET that works with Windows proxy settings. Set the system + * property <code>gwt.debugLowLevelHttpGet</code> to print failure status + * codes to stderr. + * + * @param userAgent the user-agent to specify for the GET + * @param url the absolute URL to GET + * @return the bytes of the full response (including headers), or + * <code>null</code> if there's a problem + */ + public static byte[] httpGet(TreeLogger branch, String userAgent, String url, + boolean debugFlag) { + if (!init()) { + return null; + } + byte[][] out = new byte[1][]; + int status = _httpGet(userAgent, url, out); + if (status == 0) { + return out[0]; + } else { + if (debugFlag) { + branch.log(TreeLogger.ERROR, "GET failed with status " + status + + " for " + url); + } + return null; + } + } + + public static synchronized boolean init() { + if (!sInitialized) { + File lib = new File(Utility.getInstallPath(), "gwt-ll.dll"); + if (!lib.exists()) { + return false; + } + try { + // Try to make canonical. + System.load(lib.getCanonicalPath()); + } catch (IOException e) { + return false; + } catch (UnsatisfiedLinkError e) { + return false; + } + sInitialized = true; + } + return true; + } + + // CHECKSTYLE_OFF + // out must be an array of size 1 to receive the array answer + private static native int _httpGet(String userAgent, String url, byte[][] out); + + // CHECKSTYLE_ON + + /** + * Not instantiable. + */ + private LowLevelIE6() { + } +}
diff --git a/distro-source/build.xml b/distro-source/build.xml index 2a83261..9edb2e8 100755 --- a/distro-source/build.xml +++ b/distro-source/build.xml
@@ -26,6 +26,9 @@ <zipfileset file="${gwt.build.lib}/gwt-soyc-vis.jar" prefix="${project.distname}" /> <zipfileset file="${gwt.build.lib}/gwt-api-checker.jar" prefix="${project.distname}" /> + <!-- jni libs--> + <zipfileset dir="${gwt.build.jni}/windows" prefix="${project.distname}" /> + <!-- raw files --> <zipfileset dir="${dist.resources}" prefix="${project.distname}" /> <zipfileset dir="src" prefix="${project.distname}" />
diff --git a/jni/build.xml b/jni/build.xml new file mode 100755 index 0000000..fc25ce0 --- /dev/null +++ b/jni/build.xml
@@ -0,0 +1,9 @@ +<project name="jni" default="build" basedir="."> + <property name="gwt.root" location=".." /> + <property name="project.tail" value="jni" /> + <import file="${gwt.root}/common.ant.xml" /> + + <target name="build" description="Build each platforms"> + <gwt.ant dir="windows" target="build"/> + </target> +</project>
diff --git a/jni/core/gwt-ll.cpp b/jni/core/gwt-ll.cpp new file mode 100644 index 0000000..4076aea --- /dev/null +++ b/jni/core/gwt-ll.cpp
@@ -0,0 +1,66 @@ +// Copyright 2005-2006 Google Inc. +// All Rights Reserved. + +// A slim and portable set of JNI methods for GWT hosted mode + +#include <jni.h> +#include <stdlib.h> + +extern "C" { + +/* + * Class: com_google_gwt_dev_shell_LowLevel + * Method: newGlobalRefInt + * Signature: (Ljava/lang/Object;)I + */ +JNIEXPORT jint JNICALL Java_com_google_gwt_dev_shell_LowLevel__1newGlobalRefInt(JNIEnv* env, jclass, jobject o) { + return reinterpret_cast<int>(env->NewGlobalRef(o)); +} +/* + * Class: com_google_gwt_dev_shell_LowLevel + * Method: objFromGlobalRefInt + * Signature: (I)Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_com_google_gwt_dev_shell_LowLevel__1objFromGlobalRefInt(JNIEnv* env, jclass, jint globalRef) { + return reinterpret_cast<jobject>(globalRef); +} + +/* + * Class: com_google_gwt_dev_shell_LowLevel + * Method: deleteGlobalRefInt + * Signature: (I)I + */ +JNIEXPORT void JNICALL Java_com_google_gwt_dev_shell_LowLevel__1deleteGlobalRefInt(JNIEnv* env, jclass, jint globalRef) { + env->DeleteGlobalRef(reinterpret_cast<jobject>(globalRef)); +} + +/* + * Class: com_google_gwt_dev_shell_LowLevel + * Method: getEnv + * Signature: (Ljava/lang/String;)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_com_google_gwt_dev_shell_LowLevel__1getEnv(JNIEnv* env, jclass, jstring key) { + if (!key) { + // null -> null + return 0; + } + + const char* keyPtr = env->GetStringUTFChars(key, 0); + if (!keyPtr) { + // There will be a pending OutOfMemoryException on this thread, so leave immediately and let it be thrown. + return 0; + } + + const char* valuePtr = getenv(keyPtr); + env->ReleaseStringUTFChars(key, keyPtr); + + if (!valuePtr) { + // no match found + return 0; + } + + // return the result + return env->NewStringUTF(valuePtr); +} + +} // extern "C"
diff --git a/jni/linux/ExternalWrapper.cpp b/jni/linux/ExternalWrapper.cpp new file mode 100644 index 0000000..3b2854e --- /dev/null +++ b/jni/linux/ExternalWrapper.cpp
@@ -0,0 +1,412 @@ +/* + * 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" + +#ifdef ENABLE_TRACING +extern void PrintJSValue(JSContext* cx, jsval val, char* prefix=""); +#else +// Include a null version just to keep from cluttering up call sites. +static inline void PrintJSValue(JSContext* cx, jsval val, char* prefix="") { } +#endif + +// 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"); + tracer.log("context=%08x", unsigned(cx)); + JsRootedValue::ContextManager context(cx); + JsRootedValue::ensureRuntime(cx); + if (argc < 3) { + tracer.setFail("less than 3 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]); + } + + JSString* version = 0; + if (argv[2] != JSVAL_NULL && argv[2] != JSVAL_VOID) { + version = JS_ValueToString(cx, argv[2]); + } + + 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; + } + tracer.log("module name=%s", JS_GetStringBytes(moduleName)); + } else { + tracer.log("null module name"); + } + + jstring jVersion(0); + if (version) { + jVersion = savedJNIEnv->NewString(JS_GetStringChars(version), + JS_GetStringLength(version)); + if (!jVersion || savedJNIEnv->ExceptionCheck()) { + tracer.setFail("can't get module name in Java string"); + return JS_FALSE; + } + tracer.log("version=%s", JS_GetStringBytes(version)); + } else { + tracer.log("null version"); + } + + 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;Ljava/lang/String;)Z"); + if (!methodID || savedJNIEnv->ExceptionCheck()) { + tracer.setFail("can't get gwtOnLoad method"); + return JS_FALSE; + } + + tracer.log("scriptGlobal=%08x", unsigned(scriptGlobal.get())); + + jboolean result = savedJNIEnv->CallBooleanMethod(externalObject, methodID, + NS_REINTERPRET_CAST(jint, scriptGlobal.get()), jModuleName, jVersion); + 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; +} + +/* + * definition of JavaScript initModule method which maps to the Java + * initModule method. + */ +static JSBool initModule(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + Tracer tracer("initModule"); + tracer.log("context=%08x", unsigned(cx)); + JsRootedValue::ContextManager context(cx); + JsRootedValue::ensureRuntime(cx); + if (argc < 1) { + tracer.setFail("less than 1 args"); + return JS_FALSE; + } + + JSString* moduleName = 0; + if (argv[0] != JSVAL_NULL && argv[0] != JSVAL_VOID) { + moduleName = JS_ValueToString(cx, argv[0]); + } + + 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; + } + tracer.log("module name=%s", JS_GetStringBytes(moduleName)); + } else { + tracer.log("null module name"); + } + + 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, "initModule", + "(Ljava/lang/String;)Z"); + if (!methodID || savedJNIEnv->ExceptionCheck()) { + tracer.setFail("can't get initModule method"); + return JS_FALSE; + } + + jboolean result = savedJNIEnv->CallBooleanMethod(externalObject, methodID, + jModuleName); + if (savedJNIEnv->ExceptionCheck()) { + tracer.setFail("LowLevelMoz.ExternalObject.initModule() 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"); + JsRootedValue::ContextManager context(cx); + if (*vp != JSVAL_VOID) { + // we setup the gwtOnLoad function as a property in GetScriptObject and + // do not maintain a copy of it anywhere. So, if there is a cached + // value for a property we must return it. As we never redefine any + // property values, this is safe. If we were going to keep this code + // around and add Toby's profiling code we would need to revisit this. + 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) +{ + // We don't need to push a context if all we do is DeleteGlobalRef + Tracer tracer("gwt_external_finalize", obj); + jobject externalObject = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj)); + if (externalObject) { + 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 (!jsWindowExternalObject) { + JSContext* cx = NS_REINTERPRET_CAST(JSContext*, + aContext->GetNativeContext()); + if (!cx) { + tracer.setFail("can't get JSContext"); + return NS_ERROR_UNEXPECTED; + } + JsRootedValue::ContextManager context(cx); + *aScriptObject = 0; + + nsIScriptGlobalObject* globalObject = aContext->GetGlobalObject(); + 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; + } + 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; + } + if (!JS_DefineFunction(cx, newObj, "initModule", initModule, 1, + JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) { + tracer.setFail("can't define initModule function on JavaScript object"); + return NS_ERROR_UNEXPECTED; + } + jsWindowExternalObject = newObj; + } + + *aScriptObject = jsWindowExternalObject; + return NS_OK; +} + +NS_IMETHODIMP ExternalWrapper::SetScriptObject(void* aScriptObject) +{ + jsWindowExternalObject = 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..0168083 --- /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(): jsWindowExternalObject(0) { } +private: + ~ExternalWrapper() { } + void *jsWindowExternalObject; +}; + +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..9d2ebcc --- /dev/null +++ b/jni/linux/JStringWrap.h
@@ -0,0 +1,50 @@ +/* + * 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; + } + int length() { + return env->GetStringLength(s); + } + 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..29b95ca --- /dev/null +++ b/jni/linux/JsRootedValue.cpp
@@ -0,0 +1,44 @@ +/* + * 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" + +// Initialize static value used to hold the JavaScript String class. +JSClass* JsRootedValue::stringClass = 0; + +// Initialize static reference to the sole JSRuntime value in Gecko. +JSRuntime* JsRootedValue::runtime = 0; + +// Static stack of JavaScript execution contexts. +std::stack<JSContext*> JsRootedValue::contextStack; + +/* + * Actually get the stringClass pointer from JavaScript. + */ +void JsRootedValue::fetchStringClass() const { + Tracer tracer("JsRootedValue::fetchStringClass"); + JSContext* cx = currentContext(); + jsval val = JS_GetEmptyStringValue(cx); + JSObject* obj; + // on error, leave stringClass null + if (!JS_ValueToObject(cx, val, &obj)) return; + if (!obj) { + tracer.log("ensureStringClass: null object"); + return; + } + stringClass = JS_GET_CLASS(cx, 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..65c25af --- /dev/null +++ b/jni/linux/JsRootedValue.h
@@ -0,0 +1,428 @@ +/* + * 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" +#include <stack> + +extern "C" 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): 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 runtime + static JSRuntime* runtime; + + // stack of Javascript contexts + static std::stack<JSContext*> contextStack; + + // 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(); + } + + /* + * Helper for the various constructors + */ + void constructorHelper(const char* ctorDesc) { + Tracer tracer(ctorDesc, this); + if (!JS_AddNamedRootRT(runtime, &value_, ctorDesc)) { + tracer.log("JS_AddNamedRootRT failed"); + // TODO(jat): handle errors + } + } + + /* + * Push a new JavaScript execution context. + */ + static void pushContext(JSContext* context) { + Tracer tracer("JsRootedValue::pushContext"); + tracer.log("pushed context=%08x", unsigned(context)); + contextStack.push(context); + } + + /* + * Pop a JavaScript execution context from the context stack. + */ + static void popContext() { + Tracer tracer("JsRootedValue::popContext"); + JSContext* context = currentContext(); + contextStack.pop(); + tracer.log("popped context=%08x", unsigned(context)); + } + +public: + /* + * This is a helper class used to push/pop JSContext values automatically, + * using RAII. Simply create a ContextManager object on the stack and it + * will push the context at creation time and pop it at destruction time, + * so you don't have to worry about covering all the exit paths from a + * function. + */ + class ContextManager { + public: + explicit ContextManager(JSContext* context) { + JsRootedValue::pushContext(context); + } + ~ContextManager() { + JsRootedValue::popContext(); + } + }; + + /* + * This is a helper class to manage short-lived roots on the stack, using + * RAII to free the roots when no longer needed. + */ + class Temp { + private: + void* ptr_; + public: + explicit Temp(void* ptr) : ptr_(ptr) { + JS_AddNamedRootRT(runtime, ptr_, "temporary root"); + } + ~Temp() { + JS_RemoveRootRT(runtime, ptr_); + } + }; + + /* + * Copy constructor - make another rooted value that refers to the same + * JavaScript object (or has the same value if a primitive) + */ + JsRootedValue(const JsRootedValue& rooted_value) : value_(rooted_value.value_) + { + constructorHelper("JsRootedValue copy ctor"); + } + + /* + * Create a value with a given jsval value + */ + JsRootedValue(jsval value) : value_(value) + { + constructorHelper("JsRootedValue jsval ctor"); + } + + /* + * Create a void value + */ + JsRootedValue() : value_(JSVAL_VOID) { + constructorHelper("JsRootedValue void ctor"); + } + + /* + * Destroy this object. + */ + ~JsRootedValue() { + Tracer tracer("~JsRootedValue", this); + // ignore error since currently it is not possible to fail + JS_RemoveRootRT(runtime, &value_); + } + + /* + * Save a pointer to the JSRuntime if we don't have it yet + */ + static void ensureRuntime(JSContext* context) { + if(!runtime) runtime = JS_GetRuntime(context); + } + + /* + * Return the current JavaScript execution context. + */ + static JSContext* currentContext() { + Tracer tracer("JsRootedValue::currentContext"); + if (contextStack.empty()) { + // TODO(jat): better error handling? + fprintf(stderr, "JsRootedValue::currentContext - context stack empty\n"); + ::abort(); + } + JSContext* context = contextStack.top(); + tracer.log("context=%08x", unsigned(context)); + return context; + } + + /* + * Return the underlying JS object + */ + jsval getValue() const { return value_; } + + /* + * Sets the value of the underlying JS object. + * + * 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; + // ignore return value -- if it fails, value will remain 0.0 + JS_ValueToNumber(currentContext(), 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(currentContext(), 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) { + // check if it fits in 31 bits (ie, top two bits are equal). + // if not, store it as a double + if ((val & 0x80000000) != ((val << 1) & 0x80000000)) { + return setDouble(val); + } else { + 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(currentContext(), 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(currentContext(), + reinterpret_cast<const jschar*>(utf16)); + return setValue(STRING_TO_JSVAL(str)); + } + + /* + * Sets the underlying value, defined by a counted array of UTF16 chars. + * + * Returns false on failure. + */ + bool setString(const wchar_t* utf16, size_t len) { + JSString* str = JS_NewUCStringCopyN(currentContext(), + reinterpret_cast<const jschar*>(utf16), len); + 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(currentContext(), 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..195306c --- /dev/null +++ b/jni/linux/JsValueMoz.cpp
@@ -0,0 +1,993 @@ +/* + * 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. + */ +static JsRootedValue* GetFieldAsRootedValue(JSContext* cx, jclass clazz, + jobject obj, jstring fieldName) +{ + Tracer tracer("GetFieldAsRootedValue"); + JsRootedValue::ContextManager context(cx); + jmethodID getFieldMeth = savedJNIEnv->GetMethodID(clazz, "getField", + "(Ljava/lang/String;I)V"); + if (!getFieldMeth || savedJNIEnv->ExceptionCheck()) { + return 0; + } + + JsRootedValue* jsRootedValue = new JsRootedValue(); + savedJNIEnv->CallVoidMethod(obj, getFieldMeth, fieldName, + reinterpret_cast<jint>(jsRootedValue)); + if (savedJNIEnv->ExceptionCheck()) { + delete jsRootedValue; + return 0; + } + return jsRootedValue; +} + +/* + * Sets the value of a field on a Java object. + * + * context - JavaScript context + * clazz - class of obj + * obj - Java object to store into field + * fieldName - name of field on Java object to store into + * jsRootedValue - the value to store in the field + * + * returns true on success, false on failure + */ +static bool SetFieldFromRootedValue(JSContext* cx, jclass clazz, + jobject obj, jstring fieldName, JsRootedValue* jsRootedValue) +{ + Tracer tracer("SetFieldAsRootedValue"); + JsRootedValue::ContextManager context(cx); + jmethodID getFieldMeth = savedJNIEnv->GetMethodID(clazz, "setField", + "(Ljava/lang/String;I)V"); + if (!getFieldMeth || savedJNIEnv->ExceptionCheck()) { + return false; + } + + savedJNIEnv->CallVoidMethod(obj, getFieldMeth, fieldName, + reinterpret_cast<jint>(jsRootedValue)); + if (savedJNIEnv->ExceptionCheck()) { + return false; + } + + return true; +} + +/* + * 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); +} + +/* + * Types of jsvals. + */ +enum JsValueType { + JSVAL_TYPE_VOID=0, + JSVAL_TYPE_NULL, + JSVAL_TYPE_BOOLEAN, + JSVAL_TYPE_NUMBER, + JSVAL_TYPE_STRING, + JSVAL_TYPE_OBJECT, + JSVAL_TYPE_UNKNOWN, +}; + +/* + * Names of jsval types -- must match the order of the enum above. + */ +static const char* JsValueTypeStrings[]={ + "undefined", + "null", + "boolean", + "number", + "string", + "object", + "unknown", +}; + +/* + * Return the type of a jsval. + */ +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; + } +} + +/* + * Called from JavaScript to call a Java method that has previously been + * wrapped. See _setWrappedFunction for use. + */ +static JSBool invokeJavaMethod(JSContext *cx, JSObject *obj, uintN argc, + jsval *argv, jsval *rval) +{ + Tracer tracer("invokeJavaMethod"); + JsRootedValue::ContextManager context(cx); + + // I kid you not; this is how XPConnect gets their function object so they can + // multiplex dispatch the call from a common site. See XPCDispObject.cpp(466) + // + // I now have a secondary confirmation that this trick is legit. + // brandon@mozilla.org writes: + // + // argv[-2] is part of the JS API, unabstracted. Just as argv[0] is the + // first argument (if argc != 0), argv[-1] is the |this| parameter (equal + // to OBJECT_TO_JSVAL(obj) in a native method with the standard |obj| + // second formal parameter name), and argv[-2] is the callee object, tagged + // as a jsval. + if (JS_TypeOfValue(cx, argv[-2]) != JSTYPE_FUNCTION) { + tracer.setFail("not a function type"); + return JS_FALSE; + } + JSObject* funObj = JSVAL_TO_OBJECT(argv[-2]); + + // Pull the wrapper object out of the funObj's reserved slot + jsval jsCleanupObj; + if (!JS_GetReservedSlot(cx, funObj, 0, &jsCleanupObj)) { + tracer.setFail("JS_GetReservedSlot failed"); + return JS_FALSE; + } + JSObject* cleanupObj = JSVAL_TO_OBJECT(jsCleanupObj); + if (!cleanupObj) { + tracer.setFail("cleanupObj is null"); + return JS_FALSE; + } + + // Get DispatchMethod instance out of the wrapper object + jobject dispMeth = + NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, cleanupObj)); + if (!dispMeth) { + tracer.setFail("dispMeth is null"); + return JS_FALSE; + } + jclass dispClass = savedJNIEnv->GetObjectClass(dispMeth); + if (!dispClass || savedJNIEnv->ExceptionCheck()) { + tracer.setFail("GetObjectClass returns null"); + return JS_FALSE; + } + + // lookup the invoke method on the dispatch object + jmethodID invokeID = + savedJNIEnv->GetMethodID(dispClass, "invoke", "(I[II)V"); + if (!invokeID || savedJNIEnv->ExceptionCheck()) { + tracer.setFail("GetMethodID failed"); + return JS_FALSE; + } + + // create an array of integers to hold the JsRootedValue pointers passed + // to the invoke method + jintArray args = savedJNIEnv->NewIntArray(argc); + if (!args || savedJNIEnv->ExceptionCheck()) { + tracer.setFail("NewIntArray failed"); + return JS_FALSE; + } + + // these arguments are already rooted by the JS interpreter, but we + // can't easily take advantage of that without complicating the JsRootedValue + // interface. + + // argv[-1] is OBJECT_TO_JSVAL(this) + JsRootedValue* jsThis = new JsRootedValue(argv[-1]); + tracer.log("jsthis=%08x, RV=%08x", unsigned(argv[-1]), unsigned(jsThis)); + + // create JsRootedValues for arguments + JsRootedValue *jsArgs[argc]; + for (uintN i = 0; i < argc; ++i) { + jsArgs[i] = new JsRootedValue(argv[i]); + } + savedJNIEnv->SetIntArrayRegion(args, 0, argc, + reinterpret_cast<jint*>(jsArgs)); + if (savedJNIEnv->ExceptionCheck()) { + tracer.setFail("SetIntArrayRegion failed"); + return JS_FALSE; + } + + // slot for return value + JsRootedValue* jsReturnVal = new JsRootedValue(); + + // TODO(jat): small window here where invocation may fail before Java + // takes ownership of the JsRootedValue objects. One solution would be + // to reference-count them between Java and C++ (so the reference count + // would always be 0, 1, or 2). Also setField has a similar problem. + // I plan to fix this when switching away from Java holding pointers to + // C++ objects as part of the fix for 64-bit support (which we could + // accomplish inefficiently by changing int to long everywhere, but there + // are other 64-bit issues to resolve and we need to reduce the number of + // roots the JS interpreter has to search. + + // call Java method + savedJNIEnv->CallVoidMethod(dispMeth, invokeID, reinterpret_cast<int>(jsThis), + args, reinterpret_cast<int>(jsReturnVal)); + + JSBool returnValue = JS_TRUE; + + if (savedJNIEnv->ExceptionCheck()) { + tracer.log("dispMeth=%08x", unsigned(dispMeth)); + tracer.setFail("java exception is active:"); + jobject exception = savedJNIEnv->ExceptionOccurred(); + if (exception) { + fprintf(stderr, "Exception occurred in MethodDispatch.invoke:\n"); + savedJNIEnv->ExceptionDescribe(); + savedJNIEnv->DeleteLocalRef(exception); + } + returnValue = JS_FALSE; + } else if (JS_IsExceptionPending(cx)) { + tracer.setFail("js exception is active"); + returnValue = JS_FALSE; + } + + // extract return value + *rval = jsReturnVal->getValue(); + +#if 0 + // NOTE: C++ objects are not cleaned up here because Java now owns them. + // TODO(jat): if reference-counted, they *do* need to be Released here. + + // free JsRootedValues + for (uintN i = 0; i < argc; ++i) { + delete jsArgs[i]; + } + delete jsThis; + delete jsReturnVal; +#endif + + return returnValue; +} + +/* + * Class: com_google_gwt_dev_shell_moz_JsValueMoz + * Method: _createJsRootedValue() + * Signature: (I)I + */ +extern "C" JNIEXPORT jint JNICALL +Java_com_google_gwt_dev_shell_moz_JsValueMoz__1createJsRootedValue + (JNIEnv* jniEnv, jclass, jint jsval) +{ + Tracer tracer("JsValueMoz._createJsRootedValue"); + JsRootedValue* jsRootedValue = new JsRootedValue(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() + */ +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(); +} + +/** + * Class: com_google_gwt_dev_shell_moz_JsValueMoz + * Method: _getJsval() + * Signature: (I)I + */ +extern "C" JNIEXPORT jint JNICALL +Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getJsval + (JNIEnv* jniEnv, jclass, jint jsRootedValueInt) +{ + JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> + (jsRootedValueInt); + Tracer tracer("JsValueMoz._getObjectPointer", jsRootedValue); + int val = jsRootedValue->getValue(); + tracer.log("value=%d", val); + return val; +} + +/** + * 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* cx = JsRootedValue::currentContext(); + JsValueType valueType = GetValueType(val); + const char* typeString = 0; + char buf[256]; + if(valueType == JSVAL_TYPE_OBJECT) { + JSObject* jsObject = JSVAL_TO_OBJECT(val); + JSClass* objClass = JS_GET_CLASS(cx, jsObject); + if (JS_InstanceOf(cx, 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); + JSObject* jsObject = jsRootedValue->getObject(); + if(!jsObject) { + tracer.throwHostedModeException(jniEnv, "Javascript value not an object"); + return 0; + } + JSContext* cx = JsRootedValue::currentContext(); + if(!JS_InstanceOf(cx, jsObject, &gwt_nativewrapper_class, 0)) { + tracer.throwHostedModeException(jniEnv, + "Javascript object not a Java object"); + return 0; + } + jobject javaObject + = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, 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::currentContext(), 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::currentContext(), 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->setValue(jsOtherRootedValue->getValue()); +} + +/* + * Set the Javascript value to a specific jsval. + * + * Class: com_google_gwt_dev_shell_moz_JsValueMoz + * Method: _setJsval() + * Signature: (II)V + */ +extern "C" JNIEXPORT void JNICALL +Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setJsval + (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jint jsval) +{ + JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*> + (jsRootedValueInt); + Tracer tracer("JsValueMoz._setJsval", jsRootedValue); + jsRootedValue->setValue(jsval); +} + +/* + * Set the JavaScript value to be null. + * + * Class: com_google_gwt_dev_shell_moz_JsValueMoz + * 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), + strVal.length())) { + 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* cx = JsRootedValue::currentContext(); + JSObject* scriptWindow = JS_GetGlobalObject(cx); + JSObject* newObj = JS_NewObject(cx, &gwt_nativewrapper_class, 0, + scriptWindow); + if (!newObj) { + tracer.throwHostedModeException(jniEnv, + "Unable to allocate JS object to wrap Java object"); + return; + } + // Save in output value so it won't get GCed. + jsRootedValue->setObject(newObj); + tracer.log("jsobject=%08x", unsigned(newObj)); + + // This is collected when the gwt_nativewrapper_class destructor runs. + jobject dispObjRef = jniEnv->NewGlobalRef(obj); + if (!dispObjRef || jniEnv->ExceptionCheck()) { + tracer.throwHostedModeException(jniEnv, + "Unable to allocate global reference for JS wrapper"); + return; + } + if (!JS_SetPrivate(cx, 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()) { + tracer.throwHostedModeException(jniEnv, "Can't get object class"); + return; + } + jmethodID getFieldMeth = jniEnv->GetMethodID(dispClass, "getField", + "(Ljava/lang/String;I)V"); + if (!getFieldMeth || jniEnv->ExceptionCheck()) { + tracer.throwHostedModeException(jniEnv, "Can't get getField method"); + return; + } + jstring ident = jniEnv->NewStringUTF("@java.lang.Object::toString()"); + if (!ident || jniEnv->ExceptionCheck()) { + tracer.throwHostedModeException(jniEnv, + "Can't create Java string for toString method name"); + return; + } + // allocate a new root to hold the result of the getField call + JsRootedValue* toStringFunc = new JsRootedValue(); + jniEnv->CallVoidMethod(obj, getFieldMeth, ident, + NS_REINTERPRET_CAST(jint, toStringFunc)); + if (toStringFunc->isUndefined() || jniEnv->ExceptionCheck()) { + tracer.throwHostedModeException(jniEnv, "getField(toString) failed"); + return; + } + if (!JS_DefineProperty(cx, newObj, "toString", toStringFunc->getValue(), + JS_PropertyStub, JS_PropertyStub, JSPROP_READONLY | JSPROP_PERMANENT)) { + tracer.throwHostedModeException(jniEnv, "Can't define JS toString method"); + return; + } +} + +/* + * 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* cx = JsRootedValue::currentContext(); + JSObject* scriptWindow = JS_GetGlobalObject(cx); + 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(cx, 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; + } + // Save in output value so it won't get GCed. + jsRootedValue->setObject(funObj); + + // Create a cleanup object to hold and clean up dispMeth + JSObject* cleanupObj = JS_NewObject(cx, &gwt_functionwrapper_class, 0, + scriptWindow); + if (!cleanupObj) { + tracer.throwHostedModeException(jniEnv, "JS_NewObject failed"); + return; + } + tracer.log("funObj=%08x, cleanupObj=%08x", unsigned(funObj), + unsigned(cleanupObj)); + // Store the cleanup object in funObj's reserved slot; now GC protected. + if(!JS_SetReservedSlot(cx, funObj, 0, OBJECT_TO_JSVAL(cleanupObj))) { + tracer.throwHostedModeException(jniEnv, "JS_SetReservedSlot failed"); + return; + } + jobject dispMethRef = jniEnv->NewGlobalRef(dispatchMethod); + if (!dispMethRef || jniEnv->ExceptionCheck()) { + tracer.throwHostedModeException(jniEnv, + "NewGlobalRef(dispatchMethod) failed"); + return; + } + // Store our global ref in the wrapper object + if (!JS_SetPrivate(cx, cleanupObj, dispMethRef)) { + jniEnv->DeleteGlobalRef(dispMethRef); + tracer.throwHostedModeException(jniEnv, "JS_SetPrivate(cleanupObj) failed"); + return; + } +} + +/* + * 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::currentContext(); + + // 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/LowLevelMoz.cpp b/jni/linux/LowLevelMoz.cpp new file mode 100644 index 0000000..9c3aba2 --- /dev/null +++ b/jni/linux/LowLevelMoz.cpp
@@ -0,0 +1,380 @@ +/* + * 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 to log debug-level output rather than just warnings. +#define DEBUG + +#include <cstdio> +#include <cstdarg> +#include <cwchar> + +// Mozilla header files +#include "mozilla-headers.h" + +#include <jni.h> +#include "gwt-jni.h" +#include "JsRootedValue.h" +#include "ExternalWrapper.h" +#include "Tracer.h" +#include "JsStringWrap.h" + +/* + * Debug definitions -- define FILETRACE to have debug output written to + * a file named gwt-ll.log, or JAVATRACE to have debug output passed to the + * Java LowLevelMoz.trace method. + */ +#ifdef ENABLE_TRACING +#define FILETRACE +//#define JAVATRACE +#endif + +// include javah-generated header to make sure we match +#include "LowLevelMoz.h" + +JNIEnv* savedJNIEnv = 0; +jclass lowLevelMozClass; + +// Only include debugging code if we are tracing somewhere. +#ifdef ENABLE_TRACING + +/* + * Template so vsnprintf/vswprintf can be used interchangeably in the + * append_sprintf template below. + * buf - pointer to the start of the output buffer + * len - maximum number of characters to write into the buffer + * (including the null terminator) + * fmt - printf-style format string + * args - stdarg-style variable arguments list + * Returns the number of characters written (excluding the null terminator) + * or -1 if an error occurred. + * + * Note that %lc and %ls are only legal in the wchar_t implementation. + */ +template<class charT> +int safe_vsprintf(charT* buf, size_t len, const charT* fmt, va_list args); + +// specialization for char that maps to vsnprintf +template<> +inline int safe_vsprintf<char>(char* buf, size_t len, const char* fmt, + va_list args) { + return ::vsnprintf(buf, len, fmt, args); +} + +// specialization for wchar_t that maps to vswprintf +template<> +inline int safe_vsprintf<wchar_t>(wchar_t* buf, size_t len, const wchar_t* fmt, + va_list args) { + return ::vswprintf(buf, len, fmt, args); +} + +/* + * Safely append to a string buffer, updating the output pointer and always + * reserving the last character of the buffer for a null terminator. + * bufStart - pointer to the start of the output buffer + * bufEnd - pointer just past the end of the output buffer + * fmt - format string + * additional arguments as passed to *printf + * Returns the number of characters actually written, not including the null + * terminator. Nothing is written, including the null terminator, if the + * buffer start points beyond the output buffer. + * + * Templated to work with any character type that has a safe_vsprintf + * implementation. + */ +template<class charT> +static int append_sprintf(charT* bufStart, const charT* bufEnd, + const charT* fmt, ...) { + va_list args; + va_start(args, fmt); // initialize variable arguments list + // compute space left in buffer: -1 for null terminator + int maxlen = bufEnd - bufStart - 1; + if (maxlen <= 0) return 0; + int n = safe_vsprintf(bufStart, maxlen, fmt, args); + va_end(args); + if (n > maxlen) { + n = maxlen; + } + bufStart[n] = 0; + return n; +} + +/* + * Log a given jsval with a prefix. + * cx - JSContext for the JS execution context to use + * val - jsval to print + * prefix - string to print before the value, defaults to empty string + * + * TODO(jat): this whole printf-style logging needs to be replaced, but we + * run into library version issues if we use C++ iostreams so we would need + * to implement our own equivalent. Given that this code is all likely to + * be rewritten for out-of-process hosted mode, it seems unlikely to be worth + * the effort until that is completed. + */ +void PrintJSValue(JSContext* cx, jsval val, char* prefix="") { + JSType type = JS_TypeOfValue(cx, val); + const char* typeString=JS_GetTypeName(cx, type); + static const int BUF_SIZE = 256; + char buf[BUF_SIZE]; + const char *bufEnd = buf + BUF_SIZE; + char* p = buf; + p += append_sprintf(p, bufEnd, "%s%s", prefix, typeString); + switch(type) { + case JSTYPE_VOID: + break; + case JSTYPE_BOOLEAN: + p += append_sprintf(p, bufEnd, ": %s", + JSVAL_TO_BOOLEAN(val) ? "true" : "false"); + break; + case JSTYPE_NUMBER: + if (JSVAL_IS_INT(val)) { + p += append_sprintf(p, bufEnd, ": %d", JSVAL_TO_INT(val)); + } else { + p += append_sprintf(p, bufEnd, ": %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 += append_sprintf(p, bufEnd, " @ %08x, class %s", + (unsigned)obj, clazz ? clazz->name : "<null>"); + break; + } + case JSTYPE_FUNCTION: + case JSTYPE_LIMIT: + break; + case JSTYPE_STRING: { + /* + * TODO(jat): support JS strings with international characters + */ + JsStringWrap str(cx, JSVAL_TO_STRING(val)); + p += append_sprintf(p, bufEnd, ": %.*s", str.length(), str.bytes()); + break; + } + } + Tracer::log("%s", buf); +} +#else +// Include a null version just to keep from cluttering up call sites. +static inline void PrintJSValue(JSContext* cx, jsval val, char* prefix="") { } +#endif + + +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 + + savedJNIEnv = env; + lowLevelMozClass = static_cast<jclass>(env->NewGlobalRef(llClass)); + return true; +} + +/* + * Class: com_google_gwt_dev_shell_moz_LowLevelMoz + * Method: _executeScriptWithInfo + * Signature: (ILjava/lang/String;Ljava/lang/String;I)Z + */ +extern "C" JNIEXPORT jboolean JNICALL +Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1executeScriptWithInfo + (JNIEnv* env, jclass llClass, jint scriptObjectInt, jstring code, + jstring file, jint line) +{ + 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); + JSContext* cx = JsRootedValue::currentContext(); + nsCOMPtr<nsIScriptContext> scriptContext(GetScriptContextFromJSContext(cx)); + + nsIScriptGlobalObject* scriptObject = + NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjectInt); + JSObject* scriptWindow = + reinterpret_cast<JSObject*>(scriptObject->GetGlobalJSObject()); + nsXPIDLString scriptString; + scriptString = jcode.jstr(); + + nsXPIDLString aRetValue; + PRBool aIsUndefined; + if (NS_FAILED(scriptContext->EvaluateString(scriptString, scriptWindow, 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;I[I)I + */ +extern "C" JNIEXPORT jboolean JNICALL +Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1invoke + (JNIEnv* env, jclass, jint scriptObjInt, jstring methodName, jint jsThisInt, + jintArray jsArgsInt, jint jsRetValInt) +{ + Tracer tracer("LowLevelMoz._invoke"); + + 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); + JSContext* cx = JsRootedValue::currentContext(); + nsIScriptGlobalObject* scriptObject = + NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt); + JSObject* scriptWindow + = reinterpret_cast<JSObject*>(scriptObject->GetGlobalJSObject()); + + 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(); + } + + 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; + } + + 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: _raiseJavaScriptException + * Signature: ()Z + */ +extern "C" JNIEXPORT jboolean JNICALL +Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1raiseJavaScriptException + (JNIEnv* env, jclass) +{ + Tracer tracer("LowLevelMoz._raiseJavaScriptException"); + JS_SetPendingException(JsRootedValue::currentContext(), JSVAL_NULL); + return JNI_TRUE; +} + +/* + * Class: com_google_gwt_dev_shell_moz_LowLevelMoz + * Method: _registerExternalFactoryHandler + * Signature: ()Z + */ +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; + + // tracing isn't setup until after InitGlobals is called + Tracer tracer("LowLevelMoz._registerExternalFactoryHandler"); + + 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) { + 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)))) { + tracer.setFail("AddCategoryEntry failed"); + return JNI_FALSE; + } + + return JNI_TRUE; +}
diff --git a/jni/linux/Makefile b/jni/linux/Makefile new file mode 100644 index 0000000..8b3a8cf --- /dev/null +++ b/jni/linux/Makefile
@@ -0,0 +1,153 @@ +# 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. + +## +# Target settings +## +#JAVA_HOME +#JAVA_HOME=/usr/lib/j2sdk1.5-sun + +# set GWT_TOOLS only if it is not already defined +GWT_TOOLS ?= ../../../tools + +GWT_ROOT = ../../ +OBJDIR := $(GWT_ROOT)build/out/jni/linux/ +OUTDIR := $(GWT_ROOT)build/jni/linux/ +OUT := $(OUTDIR)libgwt-ll.so +STAGING := $(GWT_ROOT)build/staging/gwt-linux-0.0.0/ + +## +# The location to get .class files from for javah +## +CLASSDIR := $(GWT_ROOT)build/out/dev/linux/bin + +# use this instead if you want to use eclipse to build the class files during +# development +#CLASSDIR := $(GWT_ROOT)eclipse/dev/linux/bin + +## +# Tools +## +CXX := g++ +AR := ar +STRIP := strip +LD := $(CXX) +JAVAH := $(JAVA_HOME)/bin/javah + +## +# List of source, object, and dependency paths plus the path to them +## +SRCDIRS := ./:../core/ +VPATH := .:../core +SRCS := gwt-ll.cpp LowLevelMoz.cpp JsValueMoz.cpp Tracer.cpp \ + ExternalWrapper.cpp NativeWrapper.cpp JsRootedValue.cpp +OBJS := $(addprefix $(OBJDIR),$(SRCS:.cpp=.o)) +DEPS := $(addprefix $(OBJDIR),$(SRCS:.cpp=.d)) + +MOZDIR = $(GWT_TOOLS)/sdk/mozilla-1.7.12 +MOZINC = $(MOZDIR)/include +MOZLIB = $(MOZDIR)/lib + +## +# Include path configuration +## +SYSINCS := \ + $(JAVA_HOME)/include \ + $(JAVA_HOME)/include/linux \ + $(MOZINC) $(MOZINC)/extra + +INCS := $(OBJDIR) +INCS := $(addprefix -I ,$(INCS)) $(addprefix -isystem ,$(SYSINCS)) + +## +# Libraries and library path +## +LIBS = xpcomglue_s +LIBPATH = -L$(MOZLIB) +LIBS := $(addprefix -l,$(LIBS)) + +# for notes on auto-dependency generation, see +# http://make.paulandlesley.org/autodep.html +# -MP obviates the need for sed hackery +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 +#------------------------------------------------------------------------------- + +## +# default rule +## +all: $(OUT) + +# install into prebuilt directory +install: $(OUT) + cp $(OUT) prebuilt/ + +## +# Include the dependency rules +## +-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)LowLevelMoz.o: $(OBJDIR)LowLevelMoz.h $(JAVA_HOME)/include/jni.h +$(OBJDIR)JsValueMoz.o: $(OBJDIR)JsValueMoz.h $(JAVA_HOME)/include/jni.h + +## +# Compilation rule for cpp files +## +$(OBJDIR)%.o : $(SRCDIR)%.cpp + @[ -d $(OBJDIR) ] || mkdir -p $(OBJDIR) + $(CXX) -c $(CFLAGS) $(INCS) -o $@ $< + +# generate preprocessed output files upon request for debugging +%.i : $(SRCDIR)%.cpp + $(CXX) -E $(CFLAGS) $(INCS) -o $@ $< + +%.I : $(SRCDIR)%.cpp + $(CXX) -E -dDI $(CFLAGS) $(INCS) -o $@ $< + +## +# Actual output file +## +$(OUT): $(OBJS) $(MOZLIB)/libxpcomglue_s.a + @[ -d $(OUTDIR) ] || mkdir -p $(OUTDIR) + $(LD) -shared $(LDFLAGS) $(LIBPATH) -o $@ $^ $(LIBS) + $(STRIP) --strip-unneeded $@ + +## +# copy to staging area for hosted-mode development +## +staging: $(OUT) + @[ -d $(STAGING) ] || mkdir -p $(STAGING) + cp $(OUT) $(STAGING) + +## +# Clean rule +## +clean: + @-rm -rf $(OBJDIR) $(OUT)
diff --git a/jni/linux/NativeWrapper.cpp b/jni/linux/NativeWrapper.cpp new file mode 100644 index 0000000..38958d0 --- /dev/null +++ b/jni/linux/NativeWrapper.cpp
@@ -0,0 +1,211 @@ +/* + * 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" + +/* + * Helper function to get reference Java attributes from Javascript. + * + * cx - JSContext pointer + * obj - JavaScript object which is a wrapped Java object + * id - property name, as a jsval string + * dispClass - output parameter of DispatchMethod subclass + * dispObj - output parameter of Java object + * jident - output parameter of property name as a Java string + */ +static JSBool getJavaPropertyStats(JSContext *cx, JSObject *obj, jsval id, + jclass& dispClass, jobject& dispObj, jstring& jident) +{ + Tracer tracer("getJavaPropertyStats"); + if (!JSVAL_IS_STRING(id)) { + tracer.setFail("id is not a string"); + return JS_FALSE; + } + + jident = savedJNIEnv->NewString(JS_GetStringChars(JSVAL_TO_STRING(id)), + JS_GetStringLength(JSVAL_TO_STRING(id))); + if (!jident || savedJNIEnv->ExceptionCheck()) { + tracer.setFail("unable to create Java string"); + return JS_FALSE; + } + + dispObj = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj)); + if (!dispObj) { + tracer.setFail("can't get dispatch object"); + return JS_FALSE; + } + + dispClass = savedJNIEnv->GetObjectClass(dispObj); + if (savedJNIEnv->ExceptionCheck()) { + tracer.setFail("can't get class of dispatch object"); + return JS_FALSE; + } + + return JS_TRUE; +} + +/* + * Returns the value of a field on a Java object. + * + * context - JavaScript context + * clazz - class of obj + * obj - Java object to retreive field from + * fieldName - name of field on Java object to retrieve + * + * Returns null on failure. Caller is responsible for deleting + * returned JsRootedValue when done with it. + */ +static JsRootedValue* GetFieldAsRootedValue(JSContext* cx, jclass clazz, + jobject obj, jstring fieldName) +{ + Tracer tracer("GetFieldAsRootedValue"); + JsRootedValue::ContextManager context(cx); + jmethodID getFieldMeth = savedJNIEnv->GetMethodID(clazz, "getField", + "(Ljava/lang/String;I)V"); + if (!getFieldMeth || savedJNIEnv->ExceptionCheck()) { + return 0; + } + + JsRootedValue* jsRootedValue = new JsRootedValue(); + savedJNIEnv->CallVoidMethod(obj, getFieldMeth, fieldName, + reinterpret_cast<jint>(jsRootedValue)); + if (savedJNIEnv->ExceptionCheck()) { + delete jsRootedValue; + return 0; + } + return jsRootedValue; +} + +/* + * Sets the value of a field on a Java object. + * + * context - JavaScript context + * clazz - class of obj + * obj - Java object to store into field + * fieldName - name of field on Java object to store into + * jsRootedValue - the value to store in the field + * + * returns true on success, false on failure + */ +static bool SetFieldFromRootedValue(JSContext* cx, jclass clazz, + jobject obj, jstring fieldName, JsRootedValue* jsRootedValue) +{ + Tracer tracer("SetFieldAsRootedValue"); + JsRootedValue::ContextManager context(cx); + jmethodID getFieldMeth = savedJNIEnv->GetMethodID(clazz, "setField", + "(Ljava/lang/String;I)V"); + if (!getFieldMeth || savedJNIEnv->ExceptionCheck()) { + return false; + } + + savedJNIEnv->CallVoidMethod(obj, getFieldMeth, fieldName, + reinterpret_cast<jint>(jsRootedValue)); + if (savedJNIEnv->ExceptionCheck()) { + return false; + } + + return true; +} + +static JSBool JS_DLL_CALLBACK gwt_nativewrapper_getProperty(JSContext *cx, + JSObject *obj, jsval id, jsval *vp) +{ + Tracer tracer("gwt_nativewrapper_getProperty"); + tracer.log("context=%08x, obj=%08x", unsigned(cx), unsigned(obj)); + JsRootedValue::ContextManager context(cx); + + 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) +{ + Tracer tracer("gwt_nativewrapper_finalize"); + jobject dispObj = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj)); + if (dispObj) { + // Remove this pairing from the global map. + jmethodID removeMethod = savedJNIEnv->GetStaticMethodID(lowLevelMozClass, "removeJsvalForObject", + "(Ljava/lang/Object;)V"); + if (!removeMethod || savedJNIEnv->ExceptionCheck()) { + tracer.setFail("Cannot GetMethodID for removeJsvalForObject"); + return; + } + savedJNIEnv->CallStaticVoidMethod(lowLevelMozClass, removeMethod, dispObj); + if (savedJNIEnv->ExceptionCheck()) { + tracer.setFail("Exception calling removeJsvalForObject"); + return; + } + savedJNIEnv->DeleteGlobalRef(dispObj); + } +} + +static JSBool JS_DLL_CALLBACK gwt_nativewrapper_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + Tracer tracer("gwt_nativewrapper_setProperty"); + tracer.log("context=%08x", unsigned(cx)); + JsRootedValue::ContextManager context(cx); + jclass dispClass; + jobject dispObj; + jstring ident; + if (!getJavaPropertyStats(cx, obj, id, dispClass, dispObj, ident)) { + tracer.setFail("getJavaPropertyStats failed"); + return JS_FALSE; + } + JsRootedValue* js_rooted_value = new JsRootedValue(*vp); + if (!SetFieldFromRootedValue(cx, dispClass, dispObj, ident, + js_rooted_value)) { + tracer.setFail("can't set field"); + return JS_FALSE; + } + return JS_TRUE; +} + +JSClass gwt_nativewrapper_class = { + "gwt_nativewrapper_class", JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, gwt_nativewrapper_getProperty, + 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 +};
diff --git a/jni/linux/Tracer.cpp b/jni/linux/Tracer.cpp new file mode 100644 index 0000000..e5f3189 --- /dev/null +++ b/jni/linux/Tracer.cpp
@@ -0,0 +1,73 @@ +/* + * 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; +} + +#endif // ENABLE_TRACING + +/* + * 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) { +#ifdef ENABLE_TRACING + setFail(msg); +#endif // ENABLE_TRACING + jclass exceptionClass + = env->FindClass("com/google/gwt/dev/shell/HostedModeException"); + env->ThrowNew(exceptionClass, msg); +}
diff --git a/jni/linux/Tracer.h b/jni/linux/Tracer.h new file mode 100644 index 0000000..ef9fd7d --- /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/build.xml b/jni/linux/build.xml new file mode 100755 index 0000000..5f0db6b --- /dev/null +++ b/jni/linux/build.xml
@@ -0,0 +1,18 @@ +<project name="jni-linux" default="build" basedir="."> + <property name="gwt.root" location="../.." /> + <property name="project.tail" value="jni/linux" /> + <import file="${gwt.root}/common.ant.xml" /> + + <target name="build" description="Builds a JNI lib"> + <mkdir dir="${project.jni}" /> + <!-- TODO: Actually build this from source! --> + <copy todir="${project.jni}"> + <fileset dir="prebuilt" /> + </copy> + </target> + + <target name="clean" description="Cleans this project's intermediate and output files"> + <delete dir="${project.build}" failonerror="false" /> + <delete dir="${project.jni}" failonerror="false" /> + </target> +</project>
diff --git a/jni/linux/gwt-jni.h b/jni/linux/gwt-jni.h new file mode 100644 index 0000000..c477f1c --- /dev/null +++ b/jni/linux/gwt-jni.h
@@ -0,0 +1,32 @@ +/* + * 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; + +extern nsCID kGwtExternalCID; + +// JavaScript class objects +extern JSClass gwt_nativewrapper_class; +extern JSClass gwt_functionwrapper_class; + +#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..6259b41 --- /dev/null +++ b/jni/linux/mozilla-headers.h
@@ -0,0 +1,30 @@ +/* + * 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 "nsIFactory.h" +#include "nsCOMPtr.h" +#include "nsAutoPtr.h"
diff --git a/jni/linux/prebuilt/libgwt-ll.so b/jni/linux/prebuilt/libgwt-ll.so new file mode 100755 index 0000000..97ead651 --- /dev/null +++ b/jni/linux/prebuilt/libgwt-ll.so Binary files differ
diff --git a/jni/mac/JStringWrap.h b/jni/mac/JStringWrap.h new file mode 100644 index 0000000..e17981f --- /dev/null +++ b/jni/mac/JStringWrap.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 JSTRINGWRAP_H +#define JSTRINGWRAP_H + +#include <jni.h> + +/* + * Wrap a Java String and automatically clean up temporary storage allocated + * for accessing its contents. + */ +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; } + jsize length() { return env->GetStringLength(s); } +private: + JNIEnv* env; + jstring s; + const char* p; + const jchar* jp; +}; + +#endif
diff --git a/jni/mac/Makefile b/jni/mac/Makefile new file mode 100644 index 0000000..867b511 --- /dev/null +++ b/jni/mac/Makefile
@@ -0,0 +1,89 @@ +# Copyright 2008 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. + +CC = g++ +JAVAH = javah + +## +# Try a GWT_TOOLS default if it isn't set +## +GWT_TOOLS ?= ../../../tools +GWT_ROOT ?= ../.. + +## +# The location to get .class files for javah +## +CLASSDIR := $(GWT_ROOT)/build/out/dev/mac/bin + +## +# External SWT products. +## +SWT_ORIGINAL_SRC = $(GWT_TOOLS)/lib/eclipse/org.eclipse.swt.carbon-macosx-3.2.1.src.zip +SWT_PATCH_DIR = ./swt-build +SWT_LIBS=$(SWT_PATCH_DIR)/libswt-webkit-carbon-3235.jnilib \ + $(SWT_PATCH_DIR)/libswt-agl-carbon-3235.jnilib \ + $(SWT_PATCH_DIR)/libswt-carbon-3235.jnilib \ + $(SWT_PATCH_DIR)/libswt-pi-carbon-3235.jnilib + +GWT_LL_LIB = libgwt-ll.jnilib +GWT_LL_OBJS = gwt-ll.o java-dispatch.o gwt-webkit.o trace.o +GWT_LL_JNI = gwt-webkit.h + +TARGETS = $(GWT_LL_LIB) $(SWT_LIBS) +ARCHS = -arch i386 -arch ppc +CFLAGS = -I/System/Library/Frameworks/JavaScriptCore.framework/Headers -I/System/Library/Frameworks/JavaVM.framework/Headers +LFLAGS = -bundle -framework JavaScriptCore + +ALL : $(TARGETS) + +## +# Unpack and patch SWT source. +## +$(SWT_PATCH_DIR): $(SWT_ORIGINAL_SRC) ./org.eclipse.swt/webkit.c + unzip $(SWT_ORIGINAL_SRC) -d $(SWT_PATCH_DIR) + cp ./org.eclipse.swt/webkit.c $(SWT_PATCH_DIR) + +## +# Build SWT. +## +$(SWT_LIBS):$(SWT_PATCH_DIR) + make -C $(SWT_PATCH_DIR) -f make_macosx.mak + +## +# Build core gwt-ll object. +## +gwt-ll.o : ../core/gwt-ll.cpp + $(CC) -c -Wall -o $@ $(ARCHS) $(CFLAGS) $< + +## +# General coff target. +## +%.o : %.cpp + $(CC) -c -Wall -o $@ $(ARCHS) $(CFLAGS) $< + +## +# Generate JNI Header +## +gwt-webkit.h : $(CLASSDIR)/com/google/gwt/dev/shell/mac/LowLevelSaf.class + $(JAVAH) -classpath $(CLASSDIR) -o gwt-webkit.h com.google.gwt.dev.shell.mac.LowLevelSaf + +## +# Build gwt-ll library. +## +$(GWT_LL_LIB) : $(GWT_LL_JNI) $(GWT_LL_OBJS) + $(CC) -Wall -o $@ $(ARCHS) $(LFLAGS) $(GWT_LL_OBJS) + +clean: + rm -f $(GWT_LL_JNI) $(GWT_LL_OBJS) $(TARGETS) + rm -rf $(SWT_PATCH_DIR)
diff --git a/jni/mac/build.xml b/jni/mac/build.xml new file mode 100755 index 0000000..66b6580 --- /dev/null +++ b/jni/mac/build.xml
@@ -0,0 +1,18 @@ +<project name="jni-mac" default="build" basedir="."> + <property name="gwt.root" location="../.." /> + <property name="project.tail" value="jni/mac" /> + <import file="${gwt.root}/common.ant.xml" /> + + <target name="build" description="Builds a JNI lib"> + <mkdir dir="${project.jni}" /> + <!-- TODO: Actually build this from source! --> + <copy todir="${project.jni}"> + <fileset dir="prebuilt" /> + </copy> + </target> + + <target name="clean" description="Cleans this project's intermediate and output files"> + <delete dir="${project.build}" failonerror="false" /> + <delete dir="${project.jni}" failonerror="false" /> + </target> +</project>
diff --git a/jni/mac/gwt-webkit.cpp b/jni/mac/gwt-webkit.cpp new file mode 100644 index 0000000..477b312 --- /dev/null +++ b/jni/mac/gwt-webkit.cpp
@@ -0,0 +1,886 @@ +/* + * Copyright 2008 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 <iostream> +#include <JavaScriptCore/JavaScriptCore.h> +#include "gwt-webkit.h" +#include "JStringWrap.h" +#include "java-dispatch.h" +#include "trace.h" + + +// http://unixjunkie.blogspot.com/2006/07/access-argc-and-argv-from-anywhere.html +extern "C" int *_NSGetArgc(void); +extern "C" char ***_NSGetArgv(void); + +/* + * + */ +JSContextRef ToJSContextRef(jint context) { + return reinterpret_cast<JSContextRef>(context); +} + +/* + * + */ +JSValueRef ToJSValueRef(jint value) { + return reinterpret_cast<JSValueRef>(value); +} + +/* + * + */ +JSObjectRef ToJSObjectRef(JSContextRef jsContext, jint object, + JSValueRef* jsException) { + JSValueRef jsValue = reinterpret_cast<JSValueRef>(object); + if (!jsValue || !JSValueIsObject(jsContext, jsValue)) { + return NULL; + } + return JSValueToObject(jsContext, jsValue, jsException); +} + +/* + * + */ +JSObjectRef ToJSObjectRef(JSContextRef jsContext, JSValueRef jsValue, + JSValueRef* jsException) { + if (!jsValue || !JSValueIsObject(jsContext, jsValue)) { + return NULL; + } + return JSValueToObject(jsContext, jsValue, jsException); +} + +/* + * + */ +JSObjectRef GetStringConstructor(JSContextRef jsContext, + JSValueRef* jsException) { + // This could only be cached relative to jsContext. + JSStringRef script = JSStringCreateWithUTF8CString("(String)"); + JSValueRef ctorVal = JSEvaluateScript(jsContext, script, NULL, NULL, 0, jsException); + JSStringRelease(script); + return ToJSObjectRef(jsContext, ctorVal, jsException); +} + +/* + * + */ +bool IsObjectOfStringConstructor(JSContextRef jsContext, JSValueRef jsValue, + JSValueRef* jsException) { + JSObjectRef jsObject = ToJSObjectRef(jsContext, jsValue, jsException); + if (!jsObject) { + return false; + } + JSObjectRef stringCtor = GetStringConstructor(jsContext, jsException); + if (!stringCtor) { + return false; + } + return JSValueIsInstanceOfConstructor(jsContext, jsObject, stringCtor, + jsException); +} + +#if 0 // For debugging purposes only. +void PrintJSString(JSStringRef jsString) +{ + size_t length = JSStringGetMaximumUTF8CStringSize(jsString); + char* buffer = new char[length]; + JSStringGetUTF8CString(jsString, buffer, length); + std::cerr << "JSString: " << buffer << std::endl; + delete[] buffer; +} + +void PrintJSValue(JSContextRef jsContext, JSValueRef jsValue) +{ + JSValueRef jsException = NULL; + JSStringRef jsResult = JSValueToStringCopy(jsContext, jsValue, + &jsException); + if (!jsException && jsValue) { + PrintJSString(jsResult); + } else { + std::cerr << "Could not convert the value to string." << std::endl; + } +} +#endif + +/* + * + */ +JSStringRef ToJSStringRef(JNIEnv* env, jstring jstr) { + if (!jstr) { + return NULL; + } + + JStringWrap jstrw(env, jstr); + if (!jstrw.jstr()) { + return NULL; + } + + return JSStringCreateWithCharacters(static_cast<const JSChar*>(jstrw.jstr()), + static_cast<size_t>(jstrw.length())); +} + +/* + * + */ +JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_isJsNull + (JNIEnv *env, jclass klass, jint context, jint value) { + TR_ENTER(); + JSContextRef jsContext = ToJSContextRef(context); + JSValueRef jsValue = ToJSValueRef(value); + if (!jsContext || !jsValue) { + TR_FAIL(); + return JNI_FALSE; + } + TR_LEAVE(); + return static_cast<jboolean>(JSValueIsNull(jsContext, jsValue)); +} + +/* + * + */ +JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_isJsUndefined + (JNIEnv *env, jclass klass, jint context, jint value) { + TR_ENTER(); + JSContextRef jsContext = ToJSContextRef(context); + JSValueRef jsValue = ToJSValueRef(value); + if (!jsContext || !jsValue) { + TR_FAIL(); + return JNI_FALSE; + } + TR_LEAVE(); + return static_cast<jboolean>(JSValueIsUndefined(jsContext, jsValue)); +} + +/* + * + */ +JNIEXPORT jint JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_getJsUndefined + (JNIEnv *env, jclass klass, jint context) { + TR_ENTER(); + JSContextRef jsContext = ToJSContextRef(context); + if (!jsContext) { + return static_cast<jint>(NULL); + } + + JSValueRef jsUndefined = JSValueMakeUndefined(jsContext); + JSValueProtectChecked(jsContext, jsUndefined); + TR_LEAVE(); + return reinterpret_cast<jint>(jsUndefined); +} + +/* + * + */ +JNIEXPORT jint JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_getJsNull + (JNIEnv *env, jclass klass, jint context) { + TR_ENTER(); + JSContextRef jsContext = ToJSContextRef(context); + if (!jsContext) { + return static_cast<jint>(NULL); + } + JSValueRef jsNull = JSValueMakeNull(jsContext); + JSValueProtectChecked(jsContext, jsNull); + TR_LEAVE(); + return reinterpret_cast<jint>(jsNull); +} + +/* + * + */ +JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_isJsBoolean + (JNIEnv *env, jclass klass, jint context, jint value) { + TR_ENTER(); + JSContextRef jsContext = ToJSContextRef(context); + JSValueRef jsValue = ToJSValueRef(value); + if (!jsContext || !jsValue) { + TR_FAIL(); + return JNI_FALSE; + } + TR_LEAVE(); + return static_cast<jboolean>(JSValueIsBoolean(jsContext, jsValue)); +} + +/* + * + */ +JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_isJsNumber + (JNIEnv *env, jclass klass, jint context, jint value) { + TR_ENTER(); + JSContextRef jsContext = ToJSContextRef(context); + JSValueRef jsValue = ToJSValueRef(value); + if (!jsContext || !jsValue) { + TR_FAIL(); + return JNI_FALSE; + } + TR_LEAVE(); + return static_cast<jboolean>(JSValueIsNumber(jsContext, jsValue)); +} + +/* + * + */ +JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_toJsBooleanImpl + (JNIEnv *env, jclass klass, jint context, jboolean jValue, jintArray rval) { + TR_ENTER(); + JSContextRef jsContext = ToJSContextRef(context); + if (!jsContext) { + TR_FAIL(); + return JNI_FALSE; + } + + JSValueRef jsValue = JSValueMakeBoolean(jsContext, static_cast<bool>(jValue)); + + env->SetIntArrayRegion(rval, 0, 1, reinterpret_cast<const jint*>(&jsValue)); + if (env->ExceptionCheck()) { + TR_FAIL(); + return JNI_FALSE; + } + JSValueProtectChecked(jsContext, jsValue); + + TR_LEAVE(); + return JNI_TRUE; +} + +/* + * + */ +JNIEXPORT void JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_gcUnprotect + (JNIEnv *env, jclass klass, jint context, jint value) { + TR_ENTER(); + JSContextRef jsContext = ToJSContextRef(context); + JSValueRef jsValue = ToJSValueRef(value); + if (!jsContext || !jsValue) { + return; + } + JSValueUnprotectChecked(jsContext, jsValue); + TR_LEAVE(); +} + +/* + * + */ +JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_toJsNumberImpl + (JNIEnv *env, jclass klass, jint context, jdouble jValue, jintArray rval) { + TR_ENTER(); + JSContextRef jsContext = ToJSContextRef(context); + if (!jsContext) { + TR_FAIL(); + return JNI_FALSE; + } + + JSValueRef jsValue = JSValueMakeNumber(jsContext, static_cast<jdouble>(jValue)); + + env->SetIntArrayRegion(rval, 0, 1, reinterpret_cast<const jint*>(&jsValue)); + if (env->ExceptionCheck()) { + TR_FAIL(); + return JNI_FALSE; + } + + JSValueProtectChecked(jsContext, jsValue); + + TR_LEAVE(); + return JNI_TRUE; +} + +/* + * + */ +JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_executeScriptWithInfoImpl + (JNIEnv *env, jclass klass, jint context, jstring jScript, jstring jUrl, + jint jLine, jintArray rval) { + TR_ENTER(); + JSValueRef jsException = NULL; + + JSContextRef jsContext = ToJSContextRef(context); + if (!jsContext) { + TR_FAIL(); + return JNI_FALSE; + } + + JSStringRef jsScript = ToJSStringRef(env, jScript); + if (!jsScript) { + TR_FAIL(); + return JNI_FALSE; + } + + JSStringRef jsUrl = ToJSStringRef(env, jUrl); + + // Evaluate will set this to global object. + JSValueRef jsResult = JSEvaluateScript(jsContext, jsScript, NULL, jsUrl, + static_cast<int>(jLine), &jsException); + if (jsException) { + TR_FAIL(); + return JNI_FALSE; + } + + JSStringRelease(jsScript); + if (jsUrl) { + JSStringRelease(jsUrl); + } + + env->SetIntArrayRegion(rval, 0, 1, reinterpret_cast<const jint*>(&jsResult)); + if (env->ExceptionCheck()) { + TR_FAIL(); + return JNI_FALSE; + } + + JSValueProtectChecked(jsContext, jsResult); + + TR_LEAVE(); + return JNI_TRUE; +} + +/* + * + */ +JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_toJsStringImpl + (JNIEnv *env, jclass klass, jint context, jstring jValue, jintArray rval) { + TR_ENTER(); + JSContextRef jsContext = ToJSContextRef(context); + if (!jsContext) { + TR_FAIL(); + return JNI_FALSE; + } + + JSStringRef jsString = ToJSStringRef(env, jValue); + if (!jsString) { + TR_FAIL(); + return JNI_FALSE; + } + + JSValueRef jsValue = JSValueMakeString(jsContext, jsString); + JSStringRelease(jsString); + + env->SetIntArrayRegion(rval, 0, 1, reinterpret_cast<const jint*>(&jsValue)); + if (env->ExceptionCheck()) { + TR_FAIL(); + return JNI_FALSE; + } + + JSValueProtectChecked(jsContext, jsValue); + + TR_LEAVE(); + return JNI_TRUE; +} + +/* + * + */ +JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_isJsStringImpl + (JNIEnv *env, jclass klass, jint context, jint value, jbooleanArray rval) { + TR_ENTER(); + JSContextRef jsContext = ToJSContextRef(context); + JSValueRef jsValue = ToJSValueRef(value); + if (!jsContext || !jsValue) { + TR_FAIL(); + return JNI_FALSE; + } + + bool isString = JSValueIsString(jsContext, jsValue); + if (!isString) { + JSValueRef jsException = NULL; + isString = IsObjectOfStringConstructor(jsContext, jsValue, &jsException); + if (jsException) { + TR_FAIL(); + return JNI_FALSE; + } + } + + jboolean jIsString = static_cast<jboolean>(isString); + env->SetBooleanArrayRegion(rval, 0, 1, &jIsString); + if (env->ExceptionCheck()) { + TR_FAIL(); + return JNI_FALSE; + } + + TR_LEAVE(); + return JNI_TRUE; +} + +/* + * + */ +JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_invokeImpl + (JNIEnv *env, jclass klass, jint context, jint scriptValue, + jstring jMethodName, jint thisVal, jintArray jArgs, jint jArgsLength, + jintArray rval) { + TR_ENTER(); + + JSContextRef jsContext = ToJSContextRef(context); + if (!jsContext) { + TR_FAIL(); + return JNI_FALSE; + } + + JSObjectRef jsScriptObj = ToJSObjectRef(jsContext, scriptValue, NULL); + if (!jsScriptObj) { + TR_FAIL(); + return JNI_FALSE; + } + + JSValueRef jsThisVal = ToJSValueRef(thisVal); + JSObjectRef jsThisObj = NULL; + // If thisVal is null, jsNull, or jsUndefined use the script object + // as this. + if (!jsThisVal || JSValueIsNull(jsContext, jsThisVal) + || JSValueIsUndefined(jsContext, jsThisVal)) { + jsThisObj = jsScriptObj; + } else { + // If we are given a value, ensure that it is an object. + jsThisObj = ToJSObjectRef(jsContext, jsThisVal, NULL); + if (!jsThisObj) { + TR_FAIL(); + return JNI_FALSE; + } + } + + JSStringRef jsMethodName = ToJSStringRef(env, jMethodName); + if (!jsMethodName) { + TR_FAIL(); + return JNI_FALSE; + } + + JSObjectRef jsMethod = ToJSObjectRef(jsContext, JSObjectGetProperty(jsContext, + jsScriptObj, jsMethodName, NULL), NULL); + if (!jsMethod || !JSObjectIsFunction(jsContext, jsMethod)) { + TR_FAIL(); + return JNI_FALSE; + } + + JSStringRelease(jsMethodName); + + // NOTE (knorton): Fix for 64-bit. + JSValueRef* jsArgs = new JSValueRef[static_cast<size_t>(jArgsLength)]; + env->GetIntArrayRegion(jArgs, 0, jArgsLength, + reinterpret_cast<jint*>(jsArgs)); + if (env->ExceptionCheck()) { + TR_FAIL(); + delete[] jsArgs; + return JNI_FALSE; + } + + JSValueRef jsException = NULL; + JSValueRef jsResult = JSObjectCallAsFunction(jsContext, jsMethod, jsThisObj, + static_cast<size_t>(jArgsLength), jsArgs, &jsException); + if (jsException) { + TR_FAIL(); + delete[] jsArgs; + return JNI_FALSE; + } + delete[] jsArgs; + + env->SetIntArrayRegion(rval, 0, 1, reinterpret_cast<const jint*>(&jsResult)); + if (env->ExceptionCheck()) { + TR_FAIL(); + return JNI_FALSE; + } + + JSValueProtectChecked(jsContext, jsResult); + + TR_LEAVE(); + return JNI_TRUE; +} + +/* + * + */ +JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_isJsObject + (JNIEnv *env, jclass klass, jint context, jint value) { + TR_ENTER(); + JSContextRef jsContext = ToJSContextRef(context); + JSValueRef jsValue = ToJSValueRef(value); + if (!jsContext || !jsValue) { + TR_FAIL(); + return JNI_FALSE; + } + + TR_LEAVE(); + return static_cast<jboolean>(JSValueIsObject(jsContext, jsValue)); +} + +JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_toBooleanImpl + (JNIEnv *env, jclass klass, jint context, jint value, jbooleanArray rval) { + TR_ENTER(); + JSContextRef jsContext = ToJSContextRef(context); + JSValueRef jsValue = ToJSValueRef(value); + if (!jsContext || !jsValue) { + TR_FAIL(); + return JNI_FALSE; + } + + jboolean jResult = static_cast<jboolean>(JSValueToBoolean(jsContext, jsValue)); + env->SetBooleanArrayRegion(rval, 0, 1, &jResult); + if (env->ExceptionCheck()) { + TR_FAIL(); + return JNI_FALSE; + } + + TR_LEAVE(); + return JNI_TRUE; +} + +/* + * + */ +JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_toDoubleImpl + (JNIEnv *env, jclass klass, jint context, jint value, jdoubleArray rval) { + TR_ENTER(); + JSContextRef jsContext = ToJSContextRef(context); + JSValueRef jsValue = ToJSValueRef(value); + if (!jsContext || !jsValue) { + TR_FAIL(); + return JNI_FALSE; + } + + JSValueRef jsException = NULL; + double result = JSValueToNumber(jsContext, jsValue, &jsException); + if (jsException) { + TR_FAIL(); + return JNI_FALSE; + } + + env->SetDoubleArrayRegion(rval, 0, 1, static_cast<jdouble*>(&result)); + if (env->ExceptionCheck()) { + TR_FAIL(); + return JNI_FALSE; + } + + TR_LEAVE(); + return JNI_TRUE; +} + +/* + * + */ +JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_toStringImpl + (JNIEnv *env, jclass klass, jint context, jint value, jobjectArray rval) { + TR_ENTER(); + JSValueRef jsException = NULL; + JSContextRef jsContext = ToJSContextRef(context); + JSValueRef jsValue = ToJSValueRef(value); + if (!jsContext || !jsValue) { + TR_FAIL(); + return JNI_FALSE; + } + + jstring jResult = NULL; + // Convert all objects to their string representation, EXCEPT + // null and undefined which will be returned as a true NULL. + if (!JSValueIsNull(jsContext, jsValue) && + !JSValueIsUndefined(jsContext, jsValue)) { + JSStringRef jsResult = JSValueToStringCopy(jsContext, jsValue, &jsException); + if (jsException) { + TR_FAIL(); + return JNI_FALSE; + } + + jResult = env->NewString( + static_cast<const jchar*>(JSStringGetCharactersPtr(jsResult)), + static_cast<jsize>(JSStringGetLength(jsResult))); + if (env->ExceptionCheck()) { + TR_FAIL(); + return JNI_FALSE; + } + + JSStringRelease(jsResult); + } + + env->SetObjectArrayElement(rval, 0, jResult); + if (env->ExceptionCheck()) { + TR_FAIL(); + return JNI_FALSE; + } + + TR_LEAVE(); + return JNI_TRUE; +} + +/* + * + */ +JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_wrapDispatchObjectImpl + (JNIEnv *env, jclass klass, jint context, jobject dispatch, jintArray rval) { + TR_ENTER(); + JSContextRef jsContext = ToJSContextRef(context); + if (!jsContext) { + TR_FAIL(); + return JNI_FALSE; + } + + JSObjectRef jsDispatch = gwt::DispatchObjectCreate(jsContext, dispatch); + if (!jsDispatch || env->ExceptionCheck()) { + TR_FAIL(); + return JNI_FALSE; + } + + env->SetIntArrayRegion(rval, 0, 1, reinterpret_cast<jint*>(&jsDispatch)); + if (env->ExceptionCheck()) { + TR_FAIL(); + return JNI_FALSE; + } + + JSValueProtectChecked(jsContext, jsDispatch); + + TR_LEAVE(); + return JNI_TRUE; +} + +/* + * + */ +JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_unwrapDispatchObjectImpl + (JNIEnv *env, jclass klass, jint context, jint value, jobjectArray rval) { + TR_ENTER(); + JSContextRef jsContext = ToJSContextRef(context); + JSValueRef jsValue = ToJSValueRef(value); + if (!jsContext || !jsValue) { + TR_FAIL(); + return JNI_FALSE; + } + + if (!JSValueIsObjectOfClass(jsContext, jsValue, gwt::GetDispatchObjectClass())) { + TR_FAIL(); + return JNI_FALSE; + } + + JSObjectRef jsObject = ToJSObjectRef(jsContext, jsValue, NULL); + if (!jsObject) { + TR_FAIL(); + return JNI_FALSE; + } + + env->SetObjectArrayElement(rval, 0, reinterpret_cast<jobject>(JSObjectGetPrivate(jsObject))); + if (env->ExceptionCheck()) { + TR_FAIL(); + return JNI_FALSE; + } + + TR_LEAVE(); + return JNI_TRUE; +} + +/* + * + */ +JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_initImpl + (JNIEnv *env, jclass klass, jclass dispatchObjectClass, + jclass dispatchMethodClass, jclass lowLevelSafClass) { + TR_ENTER(); + TR_LEAVE(); + return static_cast<jboolean>(gwt::Initialize(env, dispatchObjectClass, dispatchMethodClass, lowLevelSafClass)); +} + +/* + * + */ +JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_wrapDispatchMethodImpl + (JNIEnv *env, jclass klass, jint context, jstring name, jobject jDispatch, + jintArray rval) { + TR_ENTER(); + JSContextRef jsContext = ToJSContextRef(context); + if (!jsContext) { + TR_FAIL(); + return JNI_FALSE; + } + + JStringWrap nameWrap(env, name); + std::string nameStr(nameWrap.str()); + JSObjectRef jsDispatch = gwt::DispatchMethodCreate(jsContext, nameStr, + jDispatch); + if (!jsDispatch || env->ExceptionCheck()) { + TR_FAIL(); + return JNI_FALSE; + } + + env->SetIntArrayRegion(rval, 0, 1, reinterpret_cast<jint*>(&jsDispatch)); + if (env->ExceptionCheck()) { + TR_FAIL(); + return JNI_FALSE; + } + + JSValueProtectChecked(jsContext, jsDispatch); + + TR_LEAVE(); + return JNI_TRUE; +} + +/* + * + */ +JNIEXPORT jstring JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_getTypeString + (JNIEnv *env, jclass klass, jint context, jint value) { + TR_ENTER(); + JSContextRef jsContext = ToJSContextRef(context); + JSValueRef jsValue = ToJSValueRef(value); + if (!jsContext || !jsValue) { + return NULL; + } + + switch (JSValueGetType(jsContext, jsValue)) { + case kJSTypeUndefined: + return env->NewStringUTF("undefined"); + case kJSTypeNull: + return env->NewStringUTF("null"); + case kJSTypeBoolean: + return env->NewStringUTF("boolean"); + case kJSTypeNumber: + return env->NewStringUTF("number"); + case kJSTypeString: + return env->NewStringUTF("string"); + case kJSTypeObject: + return (JSValueIsObjectOfClass(jsContext, jsValue, gwt::GetDispatchObjectClass())) + ? env->NewStringUTF("Java object") : env->NewStringUTF("JavaScript object"); + default: + return env->NewStringUTF("unknown"); + } +} + +/* + * + */ +JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_isDispatchObjectImpl + (JNIEnv *env, jclass klass, jint context, jint value, jbooleanArray rval) { + TR_ENTER(); + JSContextRef jsContext = ToJSContextRef(context); + JSValueRef jsValue = ToJSValueRef(value); + if (!jsContext || !jsValue) { + TR_FAIL(); + return JNI_FALSE; + } + + jboolean jIsDispatchObject = static_cast<jboolean>(JSValueIsObjectOfClass( + jsContext, jsValue, gwt::GetDispatchObjectClass())); + env->SetBooleanArrayRegion(rval, 0, 1, &jIsDispatchObject); + if (env->ExceptionCheck()) { + TR_FAIL(); + return JNI_FALSE; + } + + TR_LEAVE(); + return JNI_TRUE; +} + +/* + * + */ +JNIEXPORT jint JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_getArgc + (JNIEnv *env, jclass klass) { + return *_NSGetArgc(); +} + +/* + * + */ +JNIEXPORT jstring JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_getArgv + (JNIEnv *env, jclass klass, jint index) { + int argc = *_NSGetArgc(); + if (index < 0 || index >= argc) { + return 0; + } + char **argv = *_NSGetArgv(); + return env->NewStringUTF(argv[index]); +} + +/* + * + */ +JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_getGlobalJsObjectImpl + (JNIEnv *env, jclass klass, jint context, jintArray rval) { + TR_ENTER(); + + JSContextRef jsContext = ToJSContextRef(context); + if (!jsContext) { + TR_FAIL(); + return JNI_FALSE; + } + + JSObjectRef jsGlobalObject = JSContextGetGlobalObject(jsContext); + env->SetIntArrayRegion(rval, 0, 1, reinterpret_cast<jint*>(&jsGlobalObject)); + if (env->ExceptionCheck()) { + TR_FAIL(); + return JNI_FALSE; + } + + JSValueProtectChecked(jsContext, jsGlobalObject); + + TR_LEAVE(); + return JNI_TRUE; +} + +/* + * + */ +JNIEXPORT void JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_gcProtect + (JNIEnv *env, jclass klass, jint context, jint value) { + TR_ENTER(); + + JSContextRef jsContext = ToJSContextRef(context); + JSValueRef jsValue = ToJSValueRef(value); + if (!jsContext || !jsValue) { + return; + } + + JSValueProtectChecked(jsContext, jsValue); + TR_LEAVE(); +} + +/* + * + */ +JNIEXPORT void JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_retainJsGlobalContext + (JNIEnv *env, jclass klass, jint context) { + TR_ENTER(); + JSGlobalContextRef jsContext = reinterpret_cast<JSGlobalContextRef>(context); + if (!jsContext) { + TR_FAIL(); + return; + } + JSGlobalContextRetain(jsContext); + TR_LEAVE(); +} + +/* + * + */ +JNIEXPORT void JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_releaseJsGlobalContext + (JNIEnv *env, jclass klass, jint context) { + TR_ENTER(); + JSGlobalContextRef jsContext = reinterpret_cast<JSGlobalContextRef>(context); + if (!jsContext) { + TR_FAIL(); + return; + } + JSGlobalContextRelease(jsContext); + TR_LEAVE(); +} + +/* + * + */ +JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_isGcProtected + (JNIEnv *env, jclass klass, jint value) { + JSValueRef jsValue = ToJSValueRef(value); + TR_ENTER(); + TR_LEAVE(); + return static_cast<jboolean>(JSValueIsProtected(jsValue)); +} + +JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_isJsValueProtectionCheckingEnabledImpl + (JNIEnv *env, jclass klass) { + TR_ENTER(); + TR_LEAVE(); + return static_cast<jboolean>(JSValueProtectCheckingIsEnabled()); +}
diff --git a/jni/mac/java-dispatch.cpp b/jni/mac/java-dispatch.cpp new file mode 100644 index 0000000..fafab11 --- /dev/null +++ b/jni/mac/java-dispatch.cpp
@@ -0,0 +1,524 @@ +/* + * Copyright 2008 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 <string> +#include <sstream> +#include "jni.h" +#include "java-dispatch.h" +#include "trace.h" +#include "JStringWrap.h" + +namespace gwt { + + /* + * Declarations for private functions. + */ + JSClassRef DispatchObjectClassCreate(); + + JSClassRef DispatchMethodClassCreate(); + + JSValueRef DispatchObjectGetProperty(JSContextRef, JSObjectRef, JSStringRef, + JSValueRef*); + + JSValueRef DispatchObjectToString(JSContextRef, JSObjectRef, JSObjectRef, + size_t, const JSValueRef*, JSValueRef*); + + bool DispatchObjectSetProperty(JSContextRef, JSObjectRef, JSStringRef, + JSValueRef, JSValueRef*); + + void DispatchObjectFinalize(JSObjectRef); + + JSValueRef DispatchMethodCallAsFunction(JSContextRef, JSObjectRef, + JSObjectRef, size_t, + const JSValueRef*, JSValueRef*); + + JSValueRef DispatchMethodGetToString(JSContextRef, JSObjectRef, JSStringRef, + JSValueRef*); + + JSValueRef DispatchMethodToString(JSContextRef, JSObjectRef, JSObjectRef, + size_t, const JSValueRef*, JSValueRef*); + + void DispatchMethodFinalize(JSObjectRef); + + /* + * Call this when an underlying Java Object should be freed. + */ + void ReleaseJavaObject(jobject jObject); + + + /* + * The class definition stuct for DispatchObjects. + */ + static JSClassDefinition _dispatchObjectClassDef = { 0, + kJSClassAttributeNone, "DispatchObject", 0, 0, 0, 0, + DispatchObjectFinalize, 0, DispatchObjectGetProperty, + DispatchObjectSetProperty, 0, 0, 0, 0, 0, 0 }; + + /* + * The class definition structs for DispatchMethods. + */ + static JSStaticValue _dispatchMethodStaticValues[] = { + { "toString", DispatchMethodGetToString, 0, kJSPropertyAttributeNone }, + { 0, 0, 0, 0 } + }; + static JSClassDefinition _dispatchMethodClassDef = { 0, + kJSClassAttributeNoAutomaticPrototype, "DispatchMethod", 0, + _dispatchMethodStaticValues, 0, 0, DispatchMethodFinalize, 0, 0, 0, 0, + 0, DispatchMethodCallAsFunction, 0, 0, 0 }; + + /* + * The classes used to create DispatchObjects and DispatchMethods. + */ + static JSClassRef _dispatchObjectClass = DispatchObjectClassCreate(); + static JSClassRef _dispatchMethodClass = DispatchMethodClassCreate(); + + /* + * Java class and method references needed to do delegation. + */ + + /* + * The main JVM, used by foreign threads to attach. + */ + static JavaVM* _javaVM; + + /* + * Only valid for the main thread! WebKit can finalized on a foreign thread. + */ + static JNIEnv* _javaEnv; + + static jclass _javaDispatchObjectClass; + static jclass _javaDispatchMethodClass; + static jclass _lowLevelSafClass; + static jmethodID _javaDispatchObjectSetFieldMethod; + static jmethodID _javaDispatchObjectGetFieldMethod; + static jmethodID _javaDispatchMethodInvokeMethod; + static jmethodID _javaDispatchObjectToStringMethod; + static jmethodID _lowLevelSafReleaseObject; + + /* + * Structure to hold DispatchMethod private data. + * + * NOTE: utf8Name is defensively copied. + */ + class DispatchMethodData { + public: + DispatchMethodData(jobject jObject, std::string& utf8Name) + : _jObject(jObject), _utf8Name(utf8Name) { } + ~DispatchMethodData() { + ReleaseJavaObject(_jObject); + } + jobject _jObject; + std::string _utf8Name; + }; + +/* + * The following takes the prototype from the Function constructor, this allows + * us to easily support call and apply on our objects that support CallAsFunction. + * + * NOTE: The return value is not protected. + */ +JSValueRef GetFunctionPrototype(JSContextRef jsContext, JSValueRef* exception) { + TR_ENTER(); + JSObjectRef globalObject = JSContextGetGlobalObject(jsContext); + JSStringRef fnPropName= JSStringCreateWithUTF8CString("Function"); + JSValueRef fnCtorValue = JSObjectGetProperty(jsContext, globalObject, + fnPropName, exception); + JSStringRelease(fnPropName); + if (!fnCtorValue) { + return JSValueMakeUndefined(jsContext); + } + + JSObjectRef fnCtorObject = JSValueToObject(jsContext, fnCtorValue, exception); + if (!fnCtorObject) { + return JSValueMakeUndefined(jsContext); + } + + JSStringRef protoPropName = JSStringCreateWithUTF8CString("prototype"); + JSValueRef fnPrototype = JSObjectGetProperty(jsContext, fnCtorObject, + protoPropName, exception); + JSStringRelease(protoPropName); + if (!fnPrototype) { + return JSValueMakeUndefined(jsContext); + } + + TR_LEAVE(); + return fnPrototype; +} + +/* + * + */ +JSClassRef GetDispatchObjectClass() { + TR_ENTER(); + TR_LEAVE(); + return _dispatchObjectClass; +} + +/* + * + */ +JSClassRef GetDispatchMethodClass() { + TR_ENTER(); + TR_LEAVE(); + return _dispatchMethodClass; +} + +/* + * + */ +JSClassRef DispatchObjectClassCreate() { + TR_ENTER(); + JSClassRef dispClass = JSClassCreate(&_dispatchObjectClassDef); + JSClassRetain(dispClass); + TR_LEAVE(); + return dispClass; +} + +/* + * + */ +JSClassRef DispatchMethodClassCreate() { + TR_ENTER(); + JSClassRef dispClass = JSClassCreate(&_dispatchMethodClassDef); + JSClassRetain(dispClass); + TR_LEAVE(); + return dispClass; +} + +/* + * NOTE: The object returned from this function is not protected. + */ +JSObjectRef DispatchObjectCreate(JSContextRef jsContext, jobject jObject) { + TR_ENTER(); + JSObjectRef dispInst = JSObjectMake(jsContext, _dispatchObjectClass, + _javaEnv->NewGlobalRef(jObject)); + TR_LEAVE(); + return dispInst; +} + +/* + * NOTE: The object returned from this function is not protected. + */ +JSObjectRef DispatchMethodCreate(JSContextRef jsContext, std::string& name, + jobject jObject) { + TR_ENTER(); + + JSObjectRef dispInst = JSObjectMake(jsContext, _dispatchMethodClass, + new DispatchMethodData(_javaEnv->NewGlobalRef(jObject), name)); + + // This could only be cached relative to jsContext. + JSValueRef fnProtoValue = GetFunctionPrototype(jsContext, NULL); + JSObjectSetPrototype(jsContext, dispInst, fnProtoValue); + TR_LEAVE(); + return dispInst; +} + +/* + * NOTE: The value returned from this function is not protected, but all + * JSValues that are passed into Java are protected before the invocation. + */ +JSValueRef DispatchObjectGetProperty(JSContextRef jsContext, + JSObjectRef jsObject, JSStringRef jsPropertyName, + JSValueRef* jsException) { + TR_ENTER(); + + // If you call toString on a DispatchObject, you should get the results + // of the java object's toString invcation. + if (JSStringIsEqualToUTF8CString(jsPropertyName, "toString")) { + JSObjectRef jsFunction = JSObjectMakeFunctionWithCallback(jsContext, + jsPropertyName, DispatchObjectToString); + return jsFunction; + } + + // The class check is omitted because it should not be possible to tear off + // a getter. + jobject jObject = reinterpret_cast<jobject>(JSObjectGetPrivate(jsObject)); + + jstring jPropertyName = _javaEnv->NewString( + static_cast<const jchar*>(JSStringGetCharactersPtr(jsPropertyName)), + static_cast<jsize>(JSStringGetLength(jsPropertyName))); + if (!jObject || !jPropertyName || _javaEnv->ExceptionCheck()) { + TR_FAIL(); + _javaEnv->ExceptionClear(); + return JSValueMakeUndefined(jsContext); + } + + JSValueRef jsResult = reinterpret_cast<JSValueRef>( + _javaEnv->CallIntMethod(jObject, _javaDispatchObjectGetFieldMethod, + reinterpret_cast<jint>(jsContext), + jPropertyName)); + if (!jsResult || _javaEnv->ExceptionCheck()) { + TR_FAIL(); + _javaEnv->ExceptionClear(); + return JSValueMakeUndefined(jsContext); + } + + // Java left us an extra reference to eat. + JSValueUnprotectChecked(jsContext, jsResult); + TR_LEAVE(); + return jsResult; +} + +/* + * + */ +bool DispatchObjectSetProperty(JSContextRef jsContext, JSObjectRef jsObject, + JSStringRef jsPropertyName, JSValueRef jsValue, JSValueRef* jsException) { + TR_ENTER(); + + // The class check is omitted because it should not be possible to tear off + // a getter. + jobject jObject = reinterpret_cast<jobject>(JSObjectGetPrivate(jsObject)); + + jstring jPropertyName = _javaEnv->NewString( + static_cast<const jchar*>(JSStringGetCharactersPtr(jsPropertyName)), + static_cast<jsize>(JSStringGetLength(jsPropertyName))); + if (!jObject || !jPropertyName || _javaEnv->ExceptionCheck()) { + _javaEnv->ExceptionClear(); + return false; + } + + JSValueProtectChecked(jsContext, jsValue); + + _javaEnv->CallIntMethod(jObject, _javaDispatchObjectSetFieldMethod, + reinterpret_cast<jint>(jsContext), jPropertyName, + reinterpret_cast<jint>(jsValue)); + if (_javaEnv->ExceptionCheck()) { + _javaEnv->ExceptionClear(); + return false; + } + + TR_LEAVE(); + return true; +} + +/* + * + */ +void DispatchObjectFinalize(JSObjectRef jsObject) { + TR_ENTER(); + jobject jObject = reinterpret_cast<jobject>(JSObjectGetPrivate(jsObject)); + ReleaseJavaObject(jObject); + TR_LEAVE(); +} + +/* + * + */ +void DispatchMethodFinalize(JSObjectRef jsObject) { + TR_ENTER(); + DispatchMethodData* data = reinterpret_cast<DispatchMethodData*>( + JSObjectGetPrivate(jsObject)); + delete data; + TR_LEAVE(); +} + +/* + * NOTE: The value returned from this function is not protected. + */ +JSValueRef DispatchObjectToString(JSContextRef jsContext, JSObjectRef, + JSObjectRef jsThis, size_t, const JSValueRef*, JSValueRef*) { + TR_ENTER(); + + // This function cannot be torn off and applied to any JSValue. If this does + // not reference a DispatchObject, return undefined. + if (!JSValueIsObjectOfClass(jsContext, jsThis, GetDispatchObjectClass())) { + return JSValueMakeUndefined(jsContext); + } + + jobject jObject = reinterpret_cast<jobject>(JSObjectGetPrivate(jsThis)); + jstring jResult = reinterpret_cast<jstring>( + _javaEnv->CallObjectMethod(jObject, _javaDispatchObjectToStringMethod)); + if (_javaEnv->ExceptionCheck()) { + return JSValueMakeUndefined(jsContext); + } else if (!jResult) { + return JSValueMakeNull(jsContext); + } else { + JStringWrap result(_javaEnv, jResult); + JSStringRef resultString = JSStringCreateWithCharacters( + static_cast<const JSChar*>(result.jstr()), + static_cast<size_t>(result.length())); + JSValueRef jsResultString = JSValueMakeString(jsContext, resultString); + JSStringRelease(resultString); + return jsResultString; + } + TR_LEAVE(); +} + +/* + * + */ +JSValueRef DispatchMethodCallAsFunction(JSContextRef jsContext, + JSObjectRef jsFunction, JSObjectRef jsThis, size_t argumentCount, + const JSValueRef arguments[], JSValueRef* exception) { + TR_ENTER(); + + // We don't need to check the class here because we take the private + // data from jsFunction and not jsThis. + + DispatchMethodData* data = reinterpret_cast<DispatchMethodData*>( + JSObjectGetPrivate(jsFunction)); + jobject jObject = data->_jObject; + + jintArray jArguments = _javaEnv->NewIntArray(argumentCount); + if (!jArguments || _javaEnv->ExceptionCheck()) { + return JSValueMakeUndefined(jsContext); + } + + // This single element int array will be passed into the java call to allow the + // called java method to raise an exception. We will check for a non-null value + // after the call is dispatched. + jintArray jException = _javaEnv->NewIntArray(1); + if (!jException || _javaEnv->ExceptionCheck()) { + return JNI_FALSE; + } + + for (size_t i = 0; i < argumentCount; ++i) { + JSValueRef arg = arguments[i]; + // Java will take ownership of the arguments. + JSValueProtectChecked(jsContext, arg); + _javaEnv->SetIntArrayRegion(jArguments, i, 1, reinterpret_cast<jint*>(&arg)); + if (_javaEnv->ExceptionCheck()) { + return JSValueMakeUndefined(jsContext); + } + } + + // Java will take ownership of this. + JSValueProtectChecked(jsContext, jsThis); + + JSValueRef jsResult = reinterpret_cast<JSValueRef>(_javaEnv->CallIntMethod(jObject, + _javaDispatchMethodInvokeMethod, reinterpret_cast<jint>(jsContext), + reinterpret_cast<jint>(jsThis), jArguments, jException)); + if (_javaEnv->ExceptionCheck()) { + return JSValueMakeUndefined(jsContext); + } + + JSValueRef jsException = NULL; + _javaEnv->GetIntArrayRegion(jException, 0, 1, reinterpret_cast<jint*>(&jsException)); + if (!_javaEnv->ExceptionCheck() && jsException) { + // If the java dispatch set an exception, then we pass it back to our caller. + if (exception) { + *exception = jsException; + } + // Java left us an extra reference to eat. + JSValueUnprotectChecked(jsContext, jsException); + } + + // Java left us an extra reference to eat. + JSValueUnprotectChecked(jsContext, jsResult); + TR_LEAVE(); + return jsResult; +} + +/* + * NOTE: The object returned from this function is not protected. + */ +JSValueRef DispatchMethodToString(JSContextRef jsContext, JSObjectRef, + JSObjectRef thisObject, size_t, const JSValueRef*, JSValueRef*) { + TR_ENTER(); + + // This function cannot be torn off and applied to any JSValue. If this does + // not reference a DispatchMethod, return undefined. + if (!JSValueIsObjectOfClass(jsContext, thisObject, GetDispatchMethodClass())) { + return JSValueMakeUndefined(jsContext); + } + + std::ostringstream ss; + DispatchMethodData* data = reinterpret_cast<DispatchMethodData*>( + JSObjectGetPrivate(thisObject)); + ss << "function " << data->_utf8Name << "() {\n [native code]\n}\n"; + JSStringRef stringRep = JSStringCreateWithUTF8CString(ss.str().c_str()); + JSValueRef jsStringRep = JSValueMakeString(jsContext, stringRep); + JSStringRelease(stringRep); + TR_LEAVE(); + return jsStringRep; +} + +/* + * NOTE: The object returned from this function is not protected. + */ +JSValueRef DispatchMethodGetToString(JSContextRef jsContext, + JSObjectRef jsObject, JSStringRef jsPropertyName, JSValueRef* jsException) { + TR_ENTER(); + JSObjectRef toStringFn = JSObjectMakeFunctionWithCallback(jsContext, + jsPropertyName, DispatchMethodToString); + TR_LEAVE(); + return toStringFn; +} + +/* + * + */ +bool Initialize(JNIEnv* javaEnv, jclass javaDispatchObjectClass, + jclass javaDispatchMethodClass, jclass lowLevelSafClass) { + TR_ENTER(); + if (!javaEnv || !javaDispatchObjectClass || !javaDispatchMethodClass + || !lowLevelSafClass) { + return false; + } + + _javaVM = 0; + javaEnv->GetJavaVM(&_javaVM); + + _javaEnv = javaEnv; + _javaDispatchObjectClass = static_cast<jclass>( + javaEnv->NewGlobalRef(javaDispatchObjectClass)); + _javaDispatchMethodClass = static_cast<jclass>( + javaEnv->NewGlobalRef(javaDispatchMethodClass)); + _lowLevelSafClass = static_cast<jclass>( + javaEnv->NewGlobalRef(lowLevelSafClass)); + _javaDispatchObjectSetFieldMethod = javaEnv->GetMethodID( + javaDispatchObjectClass, "setField", "(ILjava/lang/String;I)V"); + _javaDispatchObjectGetFieldMethod = javaEnv->GetMethodID( + javaDispatchObjectClass, "getField", "(ILjava/lang/String;)I"); + _javaDispatchMethodInvokeMethod = javaEnv->GetMethodID( + javaDispatchMethodClass, "invoke", "(II[I[I)I"); + _javaDispatchObjectToStringMethod = javaEnv->GetMethodID( + javaDispatchObjectClass, "toString", "()Ljava/lang/String;"); + _lowLevelSafReleaseObject = javaEnv->GetStaticMethodID( + lowLevelSafClass, "releaseObject", "(Ljava/lang/Object;)V"); + + if (!_javaVM + || !_javaDispatchObjectSetFieldMethod || !_javaDispatchObjectGetFieldMethod + || !_javaDispatchMethodInvokeMethod || !_javaDispatchObjectToStringMethod + || !_lowLevelSafReleaseObject || javaEnv->ExceptionCheck()) { + return false; + } + + TR_LEAVE(); + return true; +} + +void ReleaseJavaObject(jobject jObject) { + // Tricky: this call may be on a foreign thread. + JNIEnv* javaEnv = 0; + if ((_javaVM->AttachCurrentThreadAsDaemon(reinterpret_cast<void**>(&javaEnv), + NULL) < 0) || !javaEnv) { + TR_FAIL(); + return; + } + + // Tell the Java code we're done with this object. + javaEnv->CallStaticVoidMethod(_lowLevelSafClass, _lowLevelSafReleaseObject, + jObject); + if (javaEnv->ExceptionCheck()) { + TR_FAIL(); + return; + } + javaEnv->DeleteGlobalRef(jObject); +} + +} // namespace gwt
diff --git a/jni/mac/java-dispatch.h b/jni/mac/java-dispatch.h new file mode 100644 index 0000000..f8fa80b --- /dev/null +++ b/jni/mac/java-dispatch.h
@@ -0,0 +1,65 @@ +/* + * Copyright 2008 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 JAVA_DISPATCHER_H__ +#define JAVA_DISPATCHER_H__ +#include <iostream> +#include <JavaScriptCore/JavaScriptCore.h> +#include <jni.h> + +namespace gwt { + +/* + * Initializes static members needed by DispatchObject, DispatchMethod, + * and LowLevelSaf. This should be called before before calling anything + * else. + */ +bool Initialize(JNIEnv*, jclass, jclass, jclass); + +/* + * Returns a shared reference to the DispatchObject class + */ +JSClassRef GetDispatchObjectClass(); + +/* + * Constructs a new DispatchObject. + * + * jContext - the JavaScript context + * jObject - the java instance of DispatchObject to which + * this instance will dispatch calls + */ +JSObjectRef DispatchObjectCreate(JSContextRef jContext, jobject jObject); + +/* + * Returns a shared reference to the DispatchMethod class + */ +JSClassRef GetDispatchMethodClass(); + +/* + * Constructs a new DispatchMethod object. + * + * jsContext - the JavaScript context + * name - the name of the method (used in toString) + * jObject - the java instance of DispatchMethod to which this object will + * delegate calls. + */ +JSObjectRef DispatchMethodCreate(JSContextRef jsContext, std::string& name, + jobject jObject); + + +} // namespace gwt + +#endif // JAVA_DISPATCHER_H__
diff --git a/jni/mac/org.eclipse.swt/webkit.c b/jni/mac/org.eclipse.swt/webkit.c new file mode 100644 index 0000000..3b704ec --- /dev/null +++ b/jni/mac/org.eclipse.swt/webkit.c
@@ -0,0 +1,250 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +// Modified by Google +#include <Carbon/Carbon.h> +#include <WebKit/WebKit.h> +#include <WebKit/HIWebView.h> + +#include "jni.h" + +JNIEXPORT void JNICALL Java_org_eclipse_swt_browser_WebKit_WebInitForCarbon(JNIEnv *env, jclass zz) { + WebInitForCarbon(); +} + +JNIEXPORT jint JNICALL Java_org_eclipse_swt_browser_WebKit_HIWebViewCreate(JNIEnv *env, jclass zz, jintArray outView) { + jint *a = (*env)->GetIntArrayElements(env, outView, NULL); + jint status = (jint) HIWebViewCreate((HIViewRef *)a); + (*env)->ReleaseIntArrayElements(env, outView, a, 0); + return status; +} + +JNIEXPORT jint JNICALL Java_org_eclipse_swt_browser_WebKit_HIWebViewGetWebView(JNIEnv *env, jclass zz, jint viewRef) { + return (jint) HIWebViewGetWebView((HIViewRef)viewRef); +} + +JNIEXPORT jint JNICALL Java_org_eclipse_swt_browser_WebKit_objc_1msgSend__II(JNIEnv *env, jclass zz, jint obj, jint sel) { + return (jint) objc_msgSend((void *)obj, (void *)sel); +} + +JNIEXPORT jint JNICALL Java_org_eclipse_swt_browser_WebKit_objc_1msgSend__III(JNIEnv *env, jclass zz, jint obj, jint sel, jint arg0) { + return (jint) objc_msgSend((void *)obj, (void *)sel, (void *)arg0); +} + +JNIEXPORT jint JNICALL Java_org_eclipse_swt_browser_WebKit_objc_1msgSend__IIII(JNIEnv *env, jclass zz, jint obj, jint sel, jint arg0, jint arg1) { + return (jint) objc_msgSend((void *)obj, (void *)sel, (void *)arg0, (void *)arg1); +} + +JNIEXPORT jint JNICALL Java_org_eclipse_swt_browser_WebKit_objc_1msgSend__IIIII(JNIEnv *env, jclass zz, jint obj, jint sel, jint arg0, jint arg1, jint arg2) { + return (jint) objc_msgSend((void *)obj, (void *)sel, (void *)arg0, (void *)arg1, (void *)arg2); +} + +JNIEXPORT jint JNICALL Java_org_eclipse_swt_browser_WebKit_objc_1msgSend__IIIIII(JNIEnv *env, jclass zz, jint obj, jint sel, jint arg0, jint arg1, jint arg2, jint arg3) { + return (jint) objc_msgSend((void *)obj, (void *)sel, (void *)arg0, (void *)arg1, (void *)arg2, (void *)arg3); +} + +JNIEXPORT jint JNICALL Java_org_eclipse_swt_browser_WebKit_objc_1getClass(JNIEnv *env, jclass zz, jbyteArray name) { + jbyte *a = (*env)->GetByteArrayElements(env, name, NULL); + jint id = (jint) objc_getClass((const char *)a); + (*env)->ReleaseByteArrayElements(env, name, a, 0); + return id; +} + +JNIEXPORT jint JNICALL Java_org_eclipse_swt_browser_WebKit_sel_1registerName(JNIEnv *env, jclass zz, jbyteArray name) { + jbyte *a= (*env)->GetByteArrayElements(env, name, NULL); + jint sel= (jint) sel_registerName((const char *)a); + (*env)->ReleaseByteArrayElements(env, name, a, 0); + return sel; +} + +@interface WebKitDelegate : NSObject +{ + int user_data; + int (*proc) (int sender, int user_data, int selector, int arg0, int arg1, int arg2, int arg3); +} +@end + +@implementation WebKitDelegate + +- (id)initWithProc:(id)prc user_data:(int)data +{ + [super init]; + proc= (void *) prc; + user_data = data; + return self; +} + +/* WebFrameLoadDelegate */ + +- (void)webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame +{ + proc((int)sender, user_data, 1, (int)error, (int)frame, 0, 0); +} + +- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame +{ + proc((int)sender, user_data, 2, (int)frame, 0, 0, 0); +} + +- (void)webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame +{ + proc((int)sender, user_data, 3, (int)title, (int)frame, 0, 0); +} + +- (void)webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame +{ + proc((int)sender, user_data, 4, (int)frame, 0, 0, 0); +} + +- (void)webView:(WebView *)sender didCommitLoadForFrame:(WebFrame *)frame +{ + proc((int)sender, user_data, 10, (int)frame, 0, 0, 0); +} + +/* WebResourceLoadDelegate */ + +- (void)webView:(WebView *)sender resource:(id)identifier didFinishLoadingFromDataSource:(WebDataSource *)dataSource +{ + proc((int)sender, user_data, 5, (int)identifier, (int)dataSource, 0, 0); +} + +- (void)webView:(WebView *)sender resource:(id)identifier didFailLoadingWithError:(NSError *)error fromDataSource:(WebDataSource *)dataSource +{ + proc((int)sender, user_data, 6, (int)identifier, (int)error, (int)dataSource, 0); +} + +- (id)webView:(WebView *)sender identifierForInitialRequest:(NSURLRequest *)request fromDataSource:(WebDataSource *)dataSource +{ + return (id) proc((int)sender, user_data, 7, (int)request, (int)dataSource, 0, 0); +} + +- (NSURLRequest *)webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource +{ + return (NSURLRequest *) proc((int)sender, user_data, 8, (int)identifier, (int)request, (int)redirectResponse, (int)dataSource); +} + +/* handleNotification */ + +- (void)handleNotification:(NSNotification *)notification +{ + proc((int)[notification object], user_data, 9, (int)notification, 0, 0, 0); +} + +/* UIDelegate */ + +- (WebView *)webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request +{ + return (WebView *) proc((int)sender, user_data, 11, (int)request, 0, 0, 0); +} + +- (void)webViewShow:(WebView *)sender +{ + proc((int)sender, user_data, 12, 0, 0, 0, 0); +} + +- (void)webView:(WebView *)sender setFrame:(NSRect)frame +{ + proc((int)sender, user_data, 13, (int)&frame, 0, 0, 0); +} + +- (void)webViewClose:(WebView *)sender +{ + proc((int)sender, user_data, 14, 0, 0, 0, 0); +} + +- (NSArray *)webView:(WebView *)sender contextMenuItemsForElement:(NSDictionary *)element defaultMenuItems:(NSArray *)defaultMenuItems +{ + return (NSArray *)proc((int)sender, user_data, 15, (int)element, (int)defaultMenuItems, 0, 0); +} + +- (void)webView:(WebView *)sender setStatusBarVisible:(BOOL)visible +{ + proc((int)sender, user_data, 16, (int)visible, 0, 0, 0); +} + +- (void)webView:(WebView *)sender setResizable:(BOOL)resizable +{ + proc((int)sender, user_data, 17, (int)resizable, 0, 0, 0); +} + +- (void)webView:(WebView *)sender setToolbarsVisible:(BOOL)visible +{ + proc((int)sender, user_data, 18, (int)visible, 0, 0, 0); +} + +- (void)webView:(WebView *)sender setStatusText:(NSString *)text +{ + proc((int)sender, user_data, 23, (int)text, 0, 0, 0); +} + +- (void)webViewFocus:(WebView *)sender +{ + proc((int)sender, user_data, 24, 0, 0, 0, 0); +} + +- (void)webViewUnfocus:(WebView *)sender +{ + proc((int)sender, user_data, 25, 0, 0, 0, 0); +} + +- (void)webView:(WebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message +{ + proc((int)sender, user_data, 26, (int)message, 0, 0, 0); +} + +- (BOOL)webView:(WebView *)sender runJavaScriptConfirmPanelWithMessage:(NSString *)message +{ + return (BOOL) proc((int)sender, user_data, 27, (int)message, 0, 0, 0); +} + +- (void)webView:(WebView *)sender runOpenPanelForFileButtonWithResultListener:(id<WebOpenPanelResultListener>)resultListener +{ + proc((int)sender, user_data, 28, (int)resultListener, 0, 0, 0); +} + +/* WebPolicyDelegate */ +- (void)webView:(WebView *)sender decidePolicyForMIMEType:(NSString *)type request:(NSURLRequest *)request frame:(WebFrame*)frame decisionListener:(id<WebPolicyDecisionListener>)listener +{ + proc((int)sender, user_data, 19, (int)type, (int)request, (int)frame, (int)listener); +} + +- (void)webView:(WebView *)sender decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id<WebPolicyDecisionListener>)listener +{ + proc((int)sender, user_data, 20, (int)actionInformation, (int)request, (int)frame, (int)listener); +} + + +- (void)webView:(WebView *)sender decidePolicyForNewWindowAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request newFrameName:(NSString *)frameName decisionListener:(id<WebPolicyDecisionListener>)listener +{ + proc((int)sender, user_data, 21, (int)actionInformation, (int)request, (int)frameName, (int)listener); +} + + +- (void)webView:(WebView *)sender unableToImplementPolicyWithError:(NSError *)error frame:(WebFrame *)frame +{ + proc((int)sender, user_data, 22, (int)error, (int)frame, 0, 0); +} + +/* WebDownload */ + +- (void)download:(NSURLDownload *)download decideDestinationWithSuggestedFilename:(NSString *)filename +{ + proc((int)download, user_data, 29, (int)download, (int)filename, 0, 0); +} + +// GOOGLE: we need a notification when the window object is available so we can +// setup our own hooks before any JavaScript runs. +- (void)webView:(WebView *)sender windowScriptObjectAvailable:(WebScriptObject *)windowScriptObject +{ + proc((int)sender, user_data, 99, (int)windowScriptObject, 0, 0, 0); +} + +@end +
diff --git a/jni/mac/prebuilt/libgwt-ll.jnilib b/jni/mac/prebuilt/libgwt-ll.jnilib new file mode 100755 index 0000000..5a4030f --- /dev/null +++ b/jni/mac/prebuilt/libgwt-ll.jnilib Binary files differ
diff --git a/jni/mac/trace.cpp b/jni/mac/trace.cpp new file mode 100644 index 0000000..2127351 --- /dev/null +++ b/jni/mac/trace.cpp
@@ -0,0 +1,68 @@ +/* + * Copyright 2008 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 <iostream> +#include <map> +#include <JavaScriptCore/JavaScriptCore.h> +#include "trace.h" + +#ifdef ENABLE_JSVALUE_PROTECTION_CHECKING +static std::map<JSValueRef, int> _protectMap; +#endif + +/* + * + */ +void JSValueProtectChecked(JSContextRef jsContext, JSValueRef jsValue) { + JSValueProtect(jsContext, jsValue); +#ifdef ENABLE_JSVALUE_PROTECTION_CHECKING + _protectMap[jsValue]++; +#endif +} + +/* + * + */ +void JSValueUnprotectChecked(JSContextRef jsContext, JSValueRef jsValue) { +#ifdef ENABLE_JSVALUE_PROTECTION_CHECKING + // Unrecord in a hash_map + unsigned count = _protectMap[jsValue]; + if (count == 0) { + std::cerr << "[WARNING] JSValueUnprotect called on unprotected JSValueRef (0x" + << std::hex << ((unsigned)jsValue) << ")" << std::endl; + return; + } + _protectMap[jsValue] = count - 1; +#else + JSValueUnprotect(jsContext, jsValue); +#endif +} + +bool JSValueIsProtected(JSValueRef jsValue) { +#ifdef ENABLE_JSVALUE_PROTECTION_CHECKING + return _protectMap[jsValue] > 0; +#else + return true; +#endif +} + +bool JSValueProtectCheckingIsEnabled() { +#ifdef ENABLE_JSVALUE_PROTECTION_CHECKING + return true; +#else + return false; +#endif +}
diff --git a/jni/mac/trace.h b/jni/mac/trace.h new file mode 100644 index 0000000..e58ca96 --- /dev/null +++ b/jni/mac/trace.h
@@ -0,0 +1,45 @@ +/* + * Copyright 2008 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 TRACE_H__ +#define TRACE_H__ +#include <iostream> +#include <map> + +// Uncomment to trace enter, exit and fail for all native calls. +// #define ENABLE_CALL_TRACE + +// Uncomment to double check that JSValueRef's are protected and +// unprotected properly. +// #define ENABLE_JSVALUE_PROTECTION_CHECKING + +#ifdef ENABLE_CALL_TRACE +#define TR_ENTER() std::cout << "ENTER " << __PRETTY_FUNCTION__ << std::endl +#define TR_LEAVE() std::cout << "LEAVE " << __PRETTY_FUNCTION__ << std::endl +#define TR_FAIL() std::cout << "FAIL " << __FILE__ << "@" << __LINE__ \ + << std::endl; +#else +#define TR_ENTER() +#define TR_LEAVE() +#define TR_FAIL() +#endif // ENABLE_CALL_TRACE + +void JSValueUnprotectChecked(JSContextRef, JSValueRef); +void JSValueProtectChecked(JSContextRef, JSValueRef); +bool JSValueIsProtected(JSValueRef jsValue); +bool JSValueProtectCheckingIsEnabled(); + +#endif // TRACE_H__
diff --git a/jni/windows/IE6.cpp b/jni/windows/IE6.cpp new file mode 100644 index 0000000..58330a1 --- /dev/null +++ b/jni/windows/IE6.cpp
@@ -0,0 +1,220 @@ +/* + * 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. + */ + +// A very slim and portable set of JNI methods for GWT. + +#include <jni.h> +#include <windows.h> +#include "wininet.h" + +// A home-brewed vector class to eliminate dependence on STL. +// Reuse at your own peril, it hasn't been tested to do anything +// more than we're actually using it for here. +struct char_vector { + char_vector(long initSize=0): sz(0), cap(0), buf(0) { + resize(initSize); + } + + ~char_vector() { + clear(); + } + + char& operator[](long i) { + return buf[i]; + } + + void append(const char* start, const char* end) { + long len = end - start; + long cursize = sz; + resize(sz + len); + memcpy(&buf[cursize], start, len); + } + + void clear() { + if (buf) + free(buf); + buf = 0; + sz = cap = 0; + } + + void reserve(long newcap) { + if (newcap <= cap) + return; + char* newbuf = (char*)malloc(newcap); + if (!newbuf) + return; + if (buf) { + memcpy(newbuf, buf, sz); + free(buf); + } + buf = newbuf; + cap = newcap; + } + + void resize(long newsize) { + reserve(newsize); + sz = newsize; + } + + long size() { + return sz; + } + +private: + long sz; + long cap; + char* buf; +}; + +// Does an HTTP GET on the specified URL using the WinInet subsystem, which respects proxy settings. +// Returns a status code such that +// 0 Success +// < 0 Failure at some step (see the code below) +// > 0 Windows system error code +// +int fetch(const char* userAgent, const char* url, char_vector& response) { + int status = -1; + HINTERNET h1 = InternetOpenA(userAgent, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); + if (h1 != NULL) { + status = -2; + DWORD flags = INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_NO_UI; + HINTERNET h2 = InternetOpenUrlA(h1, url, "", -1L, flags, 0); + if (h2 != NULL) { + status = -3; + const int MAX_BUFFER = 1024 * 1024; // don't grow past this ever + bool notDone = true; + response.clear(); + response.reserve(4096); + char_vector chunk(4096); // initial buffer size + while (notDone && chunk.size() < MAX_BUFFER) { + DWORD toRead = (DWORD)chunk.size(); + DWORD didRead; + while (true) { + if (InternetReadFile(h2, (LPVOID)&chunk[0], toRead, &didRead)) { + if (didRead != 0) { + // Copy to response. + // + response.append(&chunk[0], &chunk[0] + didRead); + + // Keep going. + // + } else { + // EOF. + // + status = 0; + notDone = false; + break; + } + } else { + // Oops -- why? + // + DWORD err = GetLastError(); + if (err == ERROR_INSUFFICIENT_BUFFER) { + // Grow the buffer and retry. + // + chunk.resize(toRead * 2); + break; + } else { + // Give up. + // + status = err; + notDone = false; + break; + } + } + } + } + InternetCloseHandle(h2); + } + InternetCloseHandle(h1); + } + return status; +} + +extern "C" { + +class UTFChars { +public: + UTFChars(JNIEnv* env, jstring str): m_env(env), m_str(str) { + m_ptr = env->GetStringUTFChars(str, NULL); + } + + ~UTFChars() { + if (m_ptr) { + m_env->ReleaseStringUTFChars(m_str, m_ptr); + } + } + + operator const char*() const { + return m_ptr; + } + + JNIEnv* m_env; + jstring m_str; + const char* m_ptr; +}; + +/* + * Class: com_google_gwt_dev_shell_ie_LowLevelIE6 + * Method: _httpGet + * Signature: (Ljava/lang/String;Ljava/lang/String;[[B)I + */ +JNIEXPORT jint JNICALL Java_com_google_gwt_dev_shell_ie_LowLevelIE6__1httpGet(JNIEnv* env, jclass, jstring userAgent, jstring url, jobjectArray out) { + UTFChars userAgentChars(env, userAgent); + if (!userAgentChars || env->ExceptionCheck()) { + return -100; + } + + UTFChars urlChars(env, url); + if (!urlChars || env->ExceptionCheck()) { + return -101; + } + + // Do the fetch. + // + char_vector response; + int status = fetch(userAgentChars, urlChars, response); + + if (status != 0) { + // Failure. + // + return status; + } + + // Copy the response. + // + jbyteArray byteArray = env->NewByteArray(response.size()); + if (!byteArray || env->ExceptionCheck()) { + return -102; + } + + jbyte* bytes = env->GetByteArrayElements(byteArray, NULL); + if (!bytes || env->ExceptionCheck()) { + return -103; + } + + memcpy(bytes, &response[0], response.size()); + + env->ReleaseByteArrayElements(byteArray, bytes, 0); + + // May throw immediately after return if out array is bad. + // + env->SetObjectArrayElement(out, 0, byteArray); + + return 0; +} + +} // extern "C"
diff --git a/jni/windows/Makefile b/jni/windows/Makefile new file mode 100644 index 0000000..14da521 --- /dev/null +++ b/jni/windows/Makefile
@@ -0,0 +1,113 @@ +# 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. + +## +# Target settings +## +GWT_ROOT = ../../ +OBJDIR := $(GWT_ROOT)build/out/jni/win32/ +OUTDIR := $(GWT_ROOT)build/jni/win32/ +OUT := $(OUTDIR)gwt-ll.dll +STAGING := $(GWT_ROOT)build/staging/gwt-windows-0.0.0/ + +## +# Tools +## +CXX := mingw-gcc +AR := mingw-ar +STRIP := mingw-strip +LD := $(CXX) + +## +# List of source, object, and dependency paths plus the path to them +## +SRCDIRS := ./:../core/ +VPATH := .:../core +SRCS := gwt-ll.cpp IE6.cpp +OBJS := $(addprefix $(OBJDIR),$(SRCS:.cpp=.o)) +DEPS := $(addprefix $(OBJDIR),$(SRCS:.cpp=.d)) + +## +# Include path configuration +## +SYSINCS := \ + $(JAVA_HOME)/include \ + $(JAVA_HOME)/include/win32 +INCS := $(addprefix -isystem ,$(SYSINCS)) + +## +# Libraries and library path +## +LIBS := wininet +LIBPATH := -L$(OBJDIR) +LIBS := $(addprefix -l,$(LIBS)) + +# for notes on auto-dependency generation, see +# http://make.paulandlesley.org/autodep.html +# -MP obviates the need for sed hackery +CFLAGS := -Os -fno-exceptions -fshort-wchar -c -MMD -MP -Wno-system-headers $(CFLAGS) +LDFLAGS := -s -Wl,--kill-at $(LDFLAGS) + +#------------------------------------------------------------------------------- +# Rules +#------------------------------------------------------------------------------- + +## +# default rule +## +all: $(OUT) + +# install into prebuilt directory +install: $(OUT) + cp $(OUT) prebuilt/ + +## +# Include the dependency rules +## +-include $(DEPS) + +## +# Compilation rule for cpp files +## +$(OBJDIR)%.o : $(SRCDIR)%.cpp + @[ -d $(OBJDIR) ] || mkdir -p $(OBJDIR) + $(CXX) $(CFLAGS) $(INCS) -o $@ $< + +## +# Compilation rule for .def files to lib*.a files +## +$(OBJDIR)lib%.a : $(SRCDIR)%.def + @[ -d $(OBJDIR) ] || mkdir -p $(OBJDIR) + mingw-dlltool -k -d $< -l $@ + +## +# Actual output file +## +$(OUT): $(OBJS) $(OBJDIR)libwininet.a + @[ -d $(OUTDIR) ] || mkdir -p $(OUTDIR) + $(LD) -shared $(LDFLAGS) $(LIBPATH) -o $@ $^ $(LIBS) + $(STRIP) --strip-unneeded $@ + +## +# copy to staging area for hosted-mode development +## +staging: $(OUT) + @[ -d $(STAGING) ] || mkdir -p $(STAGING) + cp $(OUT) $(STAGING) + +## +# Clean rule +## +clean: + @-rm -rf $(OBJDIR) $(OUT)
diff --git a/jni/windows/build.xml b/jni/windows/build.xml new file mode 100755 index 0000000..f5951cf --- /dev/null +++ b/jni/windows/build.xml
@@ -0,0 +1,18 @@ +<project name="jni-windows" default="build" basedir="."> + <property name="gwt.root" location="../.." /> + <property name="project.tail" value="jni/windows" /> + <import file="${gwt.root}/common.ant.xml" /> + + <target name="build" description="Builds a JNI lib"> + <mkdir dir="${project.jni}" /> + <!-- TODO: Actually build this from source! --> + <copy todir="${project.jni}"> + <fileset dir="prebuilt" /> + </copy> + </target> + + <target name="clean" description="Cleans this project's intermediate and output files"> + <delete dir="${project.build}" failonerror="false" /> + <delete dir="${project.jni}" failonerror="false" /> + </target> +</project>
diff --git a/jni/windows/gwt-ll.vcproj b/jni/windows/gwt-ll.vcproj new file mode 100644 index 0000000..18fee56 --- /dev/null +++ b/jni/windows/gwt-ll.vcproj
@@ -0,0 +1,136 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="7.10" + Name="gwt-ll" + ProjectGUID="{AA853538-40A6-4413-A5B1-74031CB4E887}" + RootNamespace="gwt-ll" + Keyword="Win32Proj"> + <Platforms> + <Platform + Name="Win32"/> + </Platforms> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="Debug" + IntermediateDirectory="Debug" + ConfigurationType="2" + CharacterSet="2"> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories=""$(ProjectDir)..\..\..\..\..\third_party\j2sdk1.4.2_09\include";"$(ProjectDir)..\..\..\..\..\third_party\j2sdk1.4.2_09\include\win32"" + PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL" + MinimalRebuild="TRUE" + ExceptionHandling="FALSE" + BasicRuntimeChecks="3" + RuntimeLibrary="1" + ForceConformanceInForLoopScope="TRUE" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="FALSE" + DebugInformationFormat="3"/> + <Tool + Name="VCCustomBuildTool"/> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="wininet.lib" + OutputFile="$(OutDir)\$(ProjectName).dll" + LinkIncremental="1" + GenerateDebugInformation="TRUE" + ProgramDatabaseFile="$(OutDir)/$(ProjectName).pdb" + SubSystem="2" + ImportLibrary="$(OutDir)/$(ProjectName).lib" + TargetMachine="1"/> + <Tool + Name="VCMIDLTool"/> + <Tool + Name="VCPostBuildEventTool"/> + <Tool + Name="VCPreBuildEventTool"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="VCResourceCompilerTool"/> + <Tool + Name="VCWebServiceProxyGeneratorTool"/> + <Tool + Name="VCXMLDataGeneratorTool"/> + <Tool + Name="VCWebDeploymentTool"/> + <Tool + Name="VCManagedWrapperGeneratorTool"/> + <Tool + Name="VCAuxiliaryManagedWrapperGeneratorTool"/> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="Release" + IntermediateDirectory="Release" + ConfigurationType="2" + CharacterSet="2"> + <Tool + Name="VCCLCompilerTool" + AdditionalIncludeDirectories=""$(ProjectDir)..\..\..\..\..\third_party\j2sdk1.4.2_09\include";"$(ProjectDir)..\..\..\..\..\third_party\j2sdk1.4.2_09\include\win32"" + PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL" + ExceptionHandling="FALSE" + RuntimeLibrary="0" + ForceConformanceInForLoopScope="TRUE" + UsePrecompiledHeader="0" + WarningLevel="3" + Detect64BitPortabilityProblems="FALSE" + DebugInformationFormat="3"/> + <Tool + Name="VCCustomBuildTool"/> + <Tool + Name="VCLinkerTool" + AdditionalDependencies="wininet.lib" + OutputFile="$(OutDir)\$(ProjectName).dll" + LinkIncremental="1" + GenerateDebugInformation="TRUE" + SubSystem="2" + OptimizeReferences="2" + EnableCOMDATFolding="2" + ImportLibrary="$(OutDir)/$(ProjectName).lib" + TargetMachine="1"/> + <Tool + Name="VCMIDLTool"/> + <Tool + Name="VCPostBuildEventTool"/> + <Tool + Name="VCPreBuildEventTool"/> + <Tool + Name="VCPreLinkEventTool"/> + <Tool + Name="VCResourceCompilerTool"/> + <Tool + Name="VCWebServiceProxyGeneratorTool"/> + <Tool + Name="VCXMLDataGeneratorTool"/> + <Tool + Name="VCWebDeploymentTool"/> + <Tool + Name="VCManagedWrapperGeneratorTool"/> + <Tool + Name="VCAuxiliaryManagedWrapperGeneratorTool"/> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"> + <File + RelativePath="..\..\..\..\gwt-ll-core\src\main\cpp\gwt-ll.cpp"> + </File> + <File + RelativePath=".\IE6.cpp"> + </File> + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject>
diff --git a/jni/windows/prebuilt/gwt-ll.dll b/jni/windows/prebuilt/gwt-ll.dll new file mode 100755 index 0000000..49a0a1f --- /dev/null +++ b/jni/windows/prebuilt/gwt-ll.dll Binary files differ
diff --git a/jni/windows/wininet.def b/jni/windows/wininet.def new file mode 100644 index 0000000..83d6751 --- /dev/null +++ b/jni/windows/wininet.def
@@ -0,0 +1,6 @@ +LIBRARY wininet.dll +EXPORTS +InternetOpenA@20 @264 +InternetOpenUrlA@24 @265 +InternetReadFile@16 @272 +InternetCloseHandle@4 @223
diff --git a/jni/windows/wininet.h b/jni/windows/wininet.h new file mode 100644 index 0000000..935c615 --- /dev/null +++ b/jni/windows/wininet.h
@@ -0,0 +1,20 @@ +#ifndef __wininet_h__ +#define __wininet_h__ + +extern "C" { + +#define INTERNET_OPEN_TYPE_PRECONFIG 0 +#define INTERNET_FLAG_PRAGMA_NOCACHE 0x00000100 +#define INTERNET_FLAG_RELOAD 0x80000000 +#define INTERNET_FLAG_NO_COOKIES 0x00080000 +#define INTERNET_FLAG_NO_UI 0x00000200 + +typedef void* HINTERNET; +HINTERNET STDAPICALLTYPE InternetOpenA(LPCSTR lpszAgent, DWORD dwAccessType, LPCSTR lpszProxy, LPCSTR lpszProxyBypass, DWORD dwFlags); +HINTERNET STDAPICALLTYPE InternetOpenUrlA(HINTERNET hInternet, LPCSTR lpszUrl, LPCSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD_PTR dwContext); +BOOL STDAPICALLTYPE InternetReadFile(HINTERNET hFile, void* lpBuffer, DWORD dwNumberOfBytesToRead, LPDWORD lpdwNumberOfBytesRead); +BOOL STDAPICALLTYPE InternetCloseHandle(HINTERNET hInternet); + +} + +#endif // __wininet_h__