Adds proper GC handling of JavaScript values in Mac hosted mode. Also includes formatting and comments
throughout Mac hosted mode code.
Patch by: jat
Review by: me
knorton
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@831 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/mac/src/com/google/gwt/dev/shell/mac/JsValueSaf.java b/dev/mac/src/com/google/gwt/dev/shell/mac/JsValueSaf.java
index 569cb47..1d25655 100644
--- a/dev/mac/src/com/google/gwt/dev/shell/mac/JsValueSaf.java
+++ b/dev/mac/src/com/google/gwt/dev/shell/mac/JsValueSaf.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Google Inc.
+ * Copyright 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
@@ -21,11 +21,22 @@
/**
* Represents a Safari JavaScript value.
+ *
+ * The basic rule is that any JSValue passed to Java code from native code will
+ * always be GC-protected in the native code and Java will always unprotect it
+ * when the value is finalized. It should always be stored in a JsValue object
+ * immediately to make sure it is cleaned up properly when it is no longer
+ * needed. This approach is required to avoid a race condition where the value
+ * is allocated in JNI code but could be garbage collected before Java takes
+ * ownership of the value. Java values passed into JavaScript store a GlobalRef
+ * of a WebKitDispatchAdapter or MethodDispatch objects, which are freed when
+ * the JS value is finalized.
*/
public class JsValueSaf extends JsValue {
private static class JsCleanupSaf implements JsCleanup {
private final int jsval;
+ private final Throwable creationStackTrace;
/**
* Create a cleanup object which takes care of cleaning up the underlying JS
@@ -33,8 +44,9 @@
*
* @param jsval JSValue pointer as an integer
*/
- public JsCleanupSaf(int jsval) {
+ public JsCleanupSaf(int jsval, Throwable creationStackTrace) {
this.jsval = jsval;
+ this.creationStackTrace = creationStackTrace;
}
/*
@@ -43,12 +55,26 @@
* @see com.google.gwt.dev.shell.JsValue.JsCleanup#doCleanup()
*/
public void doCleanup() {
- // TODO(jat): perform cleanup operation
+ LowLevelSaf.gcUnlock(jsval, creationStackTrace);
}
}
- // pointer to underlying JSValue object as an integer
+ /*
+ * Underlying JSValue* as an integer.
+ */
private int jsval;
+
+ /*
+ * Stores a stack trace of the creation site for debugging.
+ */
+ private Throwable creationStackTrace;
+
+ /**
+ * Create a Java wrapper around an undefined JSValue.
+ */
+ public JsValueSaf() {
+ init(LowLevelSaf.jsUndefined());
+ }
/**
* Create a Java wrapper around the underlying JSValue.
@@ -56,11 +82,7 @@
* @param jsval a pointer to the underlying JSValue object as an integer
*/
public JsValueSaf(int jsval) {
- this.jsval = jsval;
- }
-
- public JsValueSaf() {
- this.jsval = LowLevelSaf.jsUndefined();
+ init(jsval);
}
public boolean getBoolean() {
@@ -134,47 +156,60 @@
}
public void setBoolean(boolean val) {
- jsval = LowLevelSaf.convertBoolean(val);
+ setJsVal(LowLevelSaf.convertBoolean(val));
}
public void setByte(byte val) {
- jsval = LowLevelSaf.convertDouble(val);
+ setJsVal(LowLevelSaf.convertDouble(val));
}
public void setChar(char val) {
- jsval = LowLevelSaf.convertDouble(val);
+ setJsVal(LowLevelSaf.convertDouble(val));
}
public void setDouble(double val) {
- jsval = LowLevelSaf.convertDouble(val);
+ setJsVal(LowLevelSaf.convertDouble(val));
}
public void setInt(int val) {
- jsval = LowLevelSaf.convertDouble(val);
- }
-
- public void setJsVal(int jsval) {
- this.jsval = jsval;
+ setJsVal(LowLevelSaf.convertDouble(val));
}
+ /**
+ * Set a new value. Unlock the previous value, but do *not* lock the new
+ * value (see class comment).
+ *
+ * @param jsval the new value to set
+ */
+ public void setJsVal(int jsval) {
+ LowLevelSaf.gcUnlock(this.jsval, creationStackTrace);
+ init(jsval);
+ }
+
public void setNull() {
- jsval = LowLevelSaf.jsNull();
+ setJsVal(LowLevelSaf.jsNull());
}
public void setShort(short val) {
- jsval = LowLevelSaf.convertDouble(val);
+ setJsVal(LowLevelSaf.convertDouble(val));
}
public void setString(String val) {
- jsval = LowLevelSaf.convertString(val);
+ setJsVal(LowLevelSaf.convertString(val));
}
public void setUndefined() {
- jsval = LowLevelSaf.jsUndefined();
+ setJsVal(LowLevelSaf.jsUndefined());
}
public void setValue(JsValue other) {
- jsval = ((JsValueSaf)other).jsval;
+ int jsvalOther = ((JsValueSaf)other).jsval;
+ /*
+ * Add another lock to this jsval, since both the other object and this
+ * one will eventually release it.
+ */
+ LowLevelSaf.gcLock(jsvalOther);
+ setJsVal(jsvalOther);
}
public void setWrappedJavaObject(CompilingClassLoader cl, Object val) {
@@ -187,11 +222,27 @@
} else {
dispObj = new WebKitDispatchAdapter(cl, val);
}
- jsval = LowLevelSaf.wrapDispatch(dispObj);
+ setJsVal(LowLevelSaf.wrapDispatch(dispObj));
}
protected JsCleanup createCleanupObject() {
- return new JsCleanupSaf(jsval);
+ return new JsCleanupSaf(jsval, creationStackTrace);
+ }
+
+ /**
+ * Initialization helper method.
+ * @param jsval underlying JSValue*
+ */
+ private void init(int jsval) {
+ this.jsval = jsval;
+
+ // only create and fill in the stack trace if we are debugging
+ if (LowLevelSaf.debugObjectCreation) {
+ this.creationStackTrace = new Throwable();
+ this.creationStackTrace.fillInStackTrace();
+ } else {
+ this.creationStackTrace = null;
+ }
}
}
diff --git a/dev/mac/src/com/google/gwt/dev/shell/mac/LowLevelSaf.java b/dev/mac/src/com/google/gwt/dev/shell/mac/LowLevelSaf.java
index f0e3e5f..03cc60d 100644
--- a/dev/mac/src/com/google/gwt/dev/shell/mac/LowLevelSaf.java
+++ b/dev/mac/src/com/google/gwt/dev/shell/mac/LowLevelSaf.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Google Inc.
+ * Copyright 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
@@ -20,22 +20,39 @@
import java.io.File;
import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.Stack;
/**
* Various low-level helper methods for dealing with Safari.
+ *
+ * The basic rule is that any JSValue passed to Java code from native code will
+ * always be GC-protected in the native code and Java will always unprotect it
+ * when the value is finalized. It should always be stored in a JsValue object
+ * immediately to make sure it is cleaned up properly when it is no longer
+ * needed. This approach is required to avoid a race condition where the value
+ * is allocated in JNI code but could be garbage collected before Java takes
+ * ownership of the value. Java values passed into JavaScript store a GlobalRef
+ * of a WebKitDispatchAdapter or MethodDispatch objects, which are freed when
+ * the JS value is finalized.
*/
public class LowLevelSaf {
+ /**
+ * Flag to enable tracking of object creation sites. Package-protected to
+ * allow JsValueSaf to use it as well.
+ */
+ static final boolean debugObjectCreation = false;
/**
- * Provides interface for methods to be exposed on javascript side.
+ * Provides interface for methods to be exposed on JavaScript side.
*/
public interface DispatchMethod {
int invoke(int execState, int jsthis, int[] jsargs);
}
/**
- * Provides interface for objects to be exposed on javascript side.
+ * Provides interface for objects to be exposed on JavaScript side.
*/
public interface DispatchObject {
int getField(String name);
@@ -180,8 +197,36 @@
_gcLock(jsval);
}
- public static void gcUnlock(int jsval) {
- _gcUnlock(jsval);
+ public static void gcUnlock(int jsval, Throwable creationStackTrace) {
+ String str = null;
+ if (debugObjectCreation) {
+ if (creationStackTrace == null) {
+ creationStackTrace = new Throwable();
+ creationStackTrace.fillInStackTrace();
+ }
+ StringWriter sWriter = new StringWriter();
+ PrintWriter pWriter = new PrintWriter(sWriter);
+ creationStackTrace.printStackTrace(pWriter);
+ str = sWriter.toString();
+ // remove the header line, keep the first 5 lines of the stack trace
+ int begin = str.indexOf("\n") + 1;
+ int nextNL = begin - 1;
+ // loop precondition: nextNL points to a newline or 0 if there is none
+ for (int i = 0; i < 5; ++i) {
+ nextNL = str.indexOf("\n", nextNL + 1);
+ if (nextNL < 0) {
+ break;
+ }
+ }
+ // loop postcondition: nextNL points to the fifth newline or is -1
+ // if there are not 5 newlines
+ if (nextNL < 0) {
+ str = str.substring(begin);
+ } else {
+ str = str.substring(begin, nextNL);
+ }
+ }
+ _gcUnlock(jsval, str);
}
public static int getExecState() {
@@ -211,7 +256,7 @@
}
public static native String getTypeString(int jsval);
-
+
public static synchronized void init() {
// Force LowLevel initialization to load gwt-ll
LowLevel.init();
@@ -450,7 +495,7 @@
private static native void _gcLock(int jsval);
- private static native void _gcUnlock(int jsval);
+ private static native void _gcUnlock(int jsval, String trace);
private static native int _getArgc();
@@ -483,8 +528,9 @@
private static native boolean _wrapFunction(String name,
DispatchMethod dispMeth, int[] rval);
+
// CHECKSTYLE_NAMING_OFF
-
+
/**
* Not instantiable.
*/
diff --git a/dev/mac/src/com/google/gwt/dev/shell/mac/ModuleSpaceSaf.java b/dev/mac/src/com/google/gwt/dev/shell/mac/ModuleSpaceSaf.java
index 51ff4d6..02371e6 100644
--- a/dev/mac/src/com/google/gwt/dev/shell/mac/ModuleSpaceSaf.java
+++ b/dev/mac/src/com/google/gwt/dev/shell/mac/ModuleSpaceSaf.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Google Inc.
+ * Copyright 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
@@ -57,7 +57,7 @@
}
public void dispose() {
- LowLevelSaf.gcUnlock(window);
+ LowLevelSaf.gcUnlock(window, null);
super.dispose();
}
diff --git a/dev/mac/src/com/google/gwt/dev/shell/mac/WebKitDispatchAdapter.java b/dev/mac/src/com/google/gwt/dev/shell/mac/WebKitDispatchAdapter.java
index 9e9a587..413a8e1 100644
--- a/dev/mac/src/com/google/gwt/dev/shell/mac/WebKitDispatchAdapter.java
+++ b/dev/mac/src/com/google/gwt/dev/shell/mac/WebKitDispatchAdapter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Google Inc.
+ * Copyright 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
@@ -39,9 +39,6 @@
private final JavaDispatch javaDispatch;
- // TODO(jat): remove these references
- // private final int scriptObject;
-
/**
* This constructor initializes as the static dispatcher, which handles only
* static method calls and field references.
@@ -52,7 +49,6 @@
WebKitDispatchAdapter(CompilingClassLoader cl) {
javaDispatch = new JavaDispatchImpl(cl);
this.classLoader = cl;
- // this.scriptObject = scriptObject;
}
/**
@@ -65,9 +61,11 @@
WebKitDispatchAdapter(CompilingClassLoader cl, Object target) {
javaDispatch = new JavaDispatchImpl(cl, target);
this.classLoader = cl;
- // this.scriptObject = scriptObject;
}
+ /* (non-Javadoc)
+ * @see com.google.gwt.dev.shell.mac.LowLevelSaf.DispatchObject#getField(java.lang.String)
+ */
public int getField(String name) {
int dispId = classLoader.getDispId(name);
if (dispId < 0) {
@@ -79,7 +77,6 @@
JsValueGlue.set(jsValue, classLoader, field.getType(),
javaDispatch.getFieldValue(dispId));
int jsval = jsValue.getJsValue();
- LowLevelSaf.gcLock(jsval);
return jsval;
} else {
Method method = javaDispatch.getMethod(dispId);
@@ -93,10 +90,16 @@
}
}
+ /* (non-Javadoc)
+ * @see com.google.gwt.dev.shell.mac.LowLevelSaf.DispatchObject#getTarget()
+ */
public Object getTarget() {
return javaDispatch.getTarget();
}
+ /* (non-Javadoc)
+ * @see com.google.gwt.dev.shell.mac.LowLevelSaf.DispatchObject#setField(java.lang.String, int)
+ */
public void setField(String name, int value) {
JsValue jsValue = new JsValueSaf(value);
int dispId = classLoader.getDispId(name);
diff --git a/jni/mac/DispWrapper.cpp b/jni/mac/DispWrapper.cpp
index 98f058e..aea305f 100644
--- a/jni/mac/DispWrapper.cpp
+++ b/jni/mac/DispWrapper.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Google Inc.
+ * Copyright 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
@@ -20,82 +20,140 @@
const ClassInfo DispWrapper::info = {"DispWrapper", 0, 0, 0};
-JSValue *DispWrapper::getter(ExecState* exec, JSObject* thisObj, const Identifier& propertyName, const PropertySlot& slot) {
- TRACE("ENTER DispWrapper::getter");
- if (propertyName.ustring() == "toString") {
- return new ToStringFunction();
- }
- if (thisObj->classInfo() == &DispWrapper::info) {
- DispWrapper* dispWrap = static_cast<DispWrapper*>(thisObj);
- jobject dispObj = dispWrap->dispObj;
- jstring jpropName = gEnv->NewString((const jchar*)propertyName.data(), propertyName.size());
- if (!jpropName || gEnv->ExceptionCheck()) {
- gEnv->ExceptionClear();
- return jsUndefined();
- }
- jint result = gEnv->CallIntMethod(dispObj, gGetFieldMeth, jpropName);
- if (!result || gEnv->ExceptionCheck()) {
- gEnv->ExceptionClear();
- return jsUndefined();
- }
- TRACE("SUCCESS DispWrapper::getter");
- return (JSValue*)result;
- }
- return jsUndefined();
+JSValue *DispWrapper::getter(ExecState* exec, JSObject* thisObj,
+ const Identifier& propertyName, const PropertySlot& slot)
+{
+ TRACE("ENTER DispWrapper::getter");
+ if (propertyName.ustring() == "toString") {
+ return new ToStringFunction();
+ }
+ if (thisObj->classInfo() == &DispWrapper::info) {
+ DispWrapper* dispWrap = static_cast<DispWrapper*>(thisObj);
+ jobject dispObj = dispWrap->dispObj;
+ jstring jpropName = gEnv->NewString((const jchar*)propertyName.data(),
+ propertyName.size());
+ if (!jpropName || gEnv->ExceptionCheck()) {
+ gEnv->ExceptionClear();
+ return jsUndefined();
+ }
+ jint result = gEnv->CallIntMethod(dispObj, gGetFieldMeth, jpropName);
+ if (!result || gEnv->ExceptionCheck()) {
+ gEnv->ExceptionClear();
+ return jsUndefined();
+ }
+ TRACE("SUCCESS DispWrapper::getter");
+ return (JSValue*)result;
+ }
+ return jsUndefined();
}
-DispWrapper::DispWrapper(jobject dispObj): dispObj(dispObj) {
-}
+/*
+ * Construct a JavaScript wrapper around a WebKitDispatchAdapter object.
+ *
+ * dispObj a GlobalRef to the Java object to wrap
+ */
+DispWrapper::DispWrapper(jobject dispObj): dispObj(dispObj) { }
+/*
+ * Free GlobalRef on the underlying WebKitDispatchAdapter object.
+ */
DispWrapper::~DispWrapper() {
- gEnv->DeleteGlobalRef(dispObj);
+ gEnv->DeleteGlobalRef(dispObj);
}
-bool DispWrapper::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot) {
- slot.setCustom(this, getter);
- return true;
+bool DispWrapper::getOwnPropertySlot(ExecState *exec,
+ const Identifier& propertyName, PropertySlot& slot)
+{
+ slot.setCustom(this, getter);
+ return true;
}
-bool DispWrapper::canPut(ExecState *exec, const Identifier &propertyName) const {
- return true;
+/*
+ * Tells JavaScript that we can store properties into this object.
+ * Note that we do not verify the property exists, so we
+ *
+ * exec JS execution state
+ * proeprtyName the property to be updated
+ */
+bool DispWrapper::canPut(ExecState *exec, const Identifier &propertyName)
+ const
+{
+ return true;
}
-void DispWrapper::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr) {
- TRACE("ENTER DispWrapper::put");
- jstring jpropName = gEnv->NewString((const jchar*)propertyName.data(), propertyName.size());
- if (!jpropName || gEnv->ExceptionCheck()) {
- gEnv->ExceptionClear();
- return;
- }
-
- gEnv->CallVoidMethod(dispObj, gSetFieldMeth, jpropName, (jint)value);
- if (gEnv->ExceptionCheck()) {
- gEnv->ExceptionClear();
- return;
- }
- TRACE("SUCCESS DispWrapper::put");
+/*
+ * Store a value into a field on a Java object.
+ *
+ * exec JS execution state
+ * propertyName the name of the field
+ * value the JS value to store in the field
+ * attr unused attributes of the property
+ *
+ * Silently catches any Java exceptions in WebKitDispatchAdapter.setField(),
+ * including undefined fields, so updates to undefined fields in Java objects
+ * will be silently ignored. TODO: is that the desired behavior?
+ */
+void DispWrapper::put(ExecState *exec, const Identifier &propertyName,
+ JSValue *value, int attr)
+{
+ TRACE("ENTER DispWrapper::put");
+ jstring jpropName = gEnv->NewString((const jchar*)propertyName.data(),
+ propertyName.size());
+ if (!jpropName || gEnv->ExceptionCheck()) {
+ gEnv->ExceptionClear();
+ return;
+ }
+ gwtGCProtect(value); // Java will take ownership of this value
+ gEnv->CallVoidMethod(dispObj, gSetFieldMeth, jpropName, (jint)value);
+ if (gEnv->ExceptionCheck()) {
+ gEnv->ExceptionClear();
+ return;
+ }
+ TRACE("SUCCESS DispWrapper::put");
}
-bool DispWrapper::deleteProperty(ExecState *exec, const Identifier &propertyName) {
- return false;
+/*
+ * Prevent JavaScript from deleting fields from a Java object.
+ */
+bool DispWrapper::deleteProperty(ExecState *exec,
+ const Identifier &propertyName)
+{
+ return false;
}
+/*
+ * Return a string representation of the Java object.
+ * Calls obj.toString() on the Java object.
+ *
+ * exec JS execution state
+ * hint unused
+ *
+ * Returns undefined if toString() failed, or the string returned (which may
+ * be null).
+ */
JSValue *DispWrapper::defaultValue(ExecState *exec, JSType hint) const {
- jstring result = (jstring)gEnv->CallObjectMethod(dispObj, gToStringMeth);
- if (gEnv->ExceptionCheck()) {
- return jsUndefined();
- } else if (!result) {
- return jsNull();
- } else {
- JStringWrap jresult(gEnv, result);
- return jsString(UString((const UChar*)jresult.jstr(), jresult.length()));
- }
+ jstring result = (jstring)gEnv->CallObjectMethod(dispObj, gToStringMeth);
+ if (gEnv->ExceptionCheck()) {
+ return jsUndefined();
+ } else if (!result) {
+ return jsNull();
+ } else {
+ JStringWrap jresult(gEnv, result);
+ return jsString(UString((const UChar*)jresult.jstr(), jresult.length()));
+ }
}
+/*
+ * Tell JavaScript that this object does not implement call functionality.
+ */
bool DispWrapper::implementsCall() const {
- return false;
+ return false;
}
-JSValue *DispWrapper::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args) {
- return jsUndefined();
+/*
+ * Prevent JavaScript from calling the WebKitDispatchAdapter object
+ * as if it were a function.
+ */
+JSValue *DispWrapper::callAsFunction(ExecState *, JSObject *, const List &) {
+ return jsUndefined();
}
diff --git a/jni/mac/DispWrapper.h b/jni/mac/DispWrapper.h
index 8ed5b7c..f752659 100644
--- a/jni/mac/DispWrapper.h
+++ b/jni/mac/DispWrapper.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Google Inc.
+ * Copyright 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
@@ -19,37 +19,42 @@
#include "gwt-webkit.h"
#include <kjs/object.h>
-// This class actually wraps Java objects
+/*
+ * This class wraps Java WebKitDispatchAdapter objects.
+ */
class DispWrapper : public KJS::JSObject {
public:
- // dispObj MUST be a global ref
+ // dispObj MUST be a global ref
DispWrapper(jobject dispObj);
- virtual ~DispWrapper();
- jobject getDispObj();
+ virtual ~DispWrapper();
+ jobject getDispObj();
public:
- // implementations of JSObject methods
- const KJS::ClassInfo *classInfo() const { return &info; }
+ // implementations of JSObject methods
+ const KJS::ClassInfo *classInfo() const { return &info; }
- virtual bool getOwnPropertySlot(KJS::ExecState*, const KJS::Identifier&, KJS::PropertySlot&);
- virtual bool canPut(KJS::ExecState*, const KJS::Identifier&) const;
- virtual void put(KJS::ExecState*, const KJS::Identifier&, KJS::JSValue*, int);
- virtual bool deleteProperty(KJS::ExecState*, const KJS::Identifier&);
- virtual KJS::JSValue *defaultValue(KJS::ExecState*, KJS::JSType) const;
- virtual bool implementsCall() const;
- virtual KJS::JSValue *callAsFunction(KJS::ExecState*, KJS::JSObject*, const KJS::List&);
-
- static const KJS::ClassInfo info;
+ virtual bool getOwnPropertySlot(KJS::ExecState*, const KJS::Identifier&,
+ KJS::PropertySlot&);
+ virtual bool canPut(KJS::ExecState*, const KJS::Identifier&) const;
+ virtual void put(KJS::ExecState*, const KJS::Identifier&, KJS::JSValue*, int);
+ virtual bool deleteProperty(KJS::ExecState*, const KJS::Identifier&);
+ virtual KJS::JSValue *defaultValue(KJS::ExecState*, KJS::JSType) const;
+ virtual bool implementsCall() const;
+ virtual KJS::JSValue *callAsFunction(KJS::ExecState*, KJS::JSObject*,
+ const KJS::List&);
+
+ static const KJS::ClassInfo info;
private:
- static KJS::JSValue* getter(KJS::ExecState*, KJS::JSObject*, const KJS::Identifier&, const KJS::PropertySlot&);
+ static KJS::JSValue* getter(KJS::ExecState*, KJS::JSObject*,
+ const KJS::Identifier&, const KJS::PropertySlot&);
private:
- jobject dispObj;
+ jobject dispObj;
};
inline jobject DispWrapper::getDispObj() {
- return dispObj;
+ return dispObj;
}
#endif
diff --git a/jni/mac/FuncWrapper.cpp b/jni/mac/FuncWrapper.cpp
index 76dcaa8..207705f 100644
--- a/jni/mac/FuncWrapper.cpp
+++ b/jni/mac/FuncWrapper.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Google Inc.
+ * Copyright 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
@@ -18,37 +18,75 @@
using namespace KJS;
-// FuncWrapper
-FuncWrapper::FuncWrapper(const UString& name, jobject funcObj): FunctionObject(name)
-, funcObj(funcObj) {
-}
+/*
+ * Constructor for FuncWrapper.
+ *
+ * name JavaScript name of the function
+ * funcObj a GlobalRef of the Java MethodDispatch object (to be freed in
+ * the destructor, so the caller no longer has ownership)
+ */
+FuncWrapper::FuncWrapper(const UString& name, jobject funcObj)
+ : FunctionObject(name), funcObj(funcObj) { }
+/*
+ * Destructor for FuncWrapper.
+ *
+ * Frees the GlobalRef for the Java MethodDispatch object.
+ */
FuncWrapper::~FuncWrapper() {
- gEnv->DeleteGlobalRef(funcObj);
+ gEnv->DeleteGlobalRef(funcObj);
}
-JSValue *FuncWrapper::callAsFunction(ExecState* execState, JSObject* thisObj, const List& args) {
- TRACE("ENTER FuncWrapper::callAsFunction");
+/*
+ * Call a Java MethodDispatch interface from JavaScript.
+ * All JSValue* values passed to Java must be GC-protected, since Java
+ * will take ownership of them and eventually unprotect them.
+ *
+ * execState the KJS execution state to run in
+ * thisObj the JavaScript object wrapper for the Java object this method
+ * is defined on
+ * args the argument list
+ *
+ * Returns the JSValue returned from the Java method.
+ */
+JSValue *FuncWrapper::callAsFunction(ExecState* execState, JSObject* thisObj,
+ const List& args)
+{
+ TRACE("ENTER FuncWrapper::callAsFunction");
- int argc = args.size();
- jintArray jsargs = gEnv->NewIntArray(argc);
- if (!jsargs || gEnv->ExceptionCheck())
- return TRACE("FAIL FuncWrapper::callAsFunction: NewIntArray"), jsUndefined();
+ // create the array of JSValue* (passed as integers to Java)
+ int argc = args.size();
+ jintArray jsargs = gEnv->NewIntArray(argc);
+ if (!jsargs || gEnv->ExceptionCheck()) {
+ TRACE("FAIL FuncWrapper::callAsFunction: NewIntArray");
+ return jsUndefined();
+ }
- for (int i = 0; i < argc; ++i) {
- JSValue* arg = args[i];
- gEnv->SetIntArrayRegion(jsargs, i, 1, (jint*)&arg);
- if (gEnv->ExceptionCheck())
- return TRACE("FAIL FuncWrapper::callAsFunction: SetIntArrayRegion"), jsUndefined();
- }
+ // protect the JSValue* values and store them in the array
+ for (int i = 0; i < argc; ++i) {
+ JSValue* arg = args[i];
+ gwtGCProtect(arg);
+ gEnv->SetIntArrayRegion(jsargs, i, 1, reinterpret_cast<jint*>(&arg));
+ if (gEnv->ExceptionCheck()) {
+ TRACE("FAIL FuncWrapper::callAsFunction: SetIntArrayRegion");
+ return jsUndefined();
+ }
+ }
- jint result = gEnv->CallIntMethod(funcObj, gInvokeMeth, execState, thisObj, jsargs);
- if (gEnv->ExceptionCheck())
- return TRACE("FAIL FuncWrapper::callAsFunction: java exception is active"), jsUndefined();
+ // protect the "this" object, as Java will eventually unprotect it
+ gwtGCProtect(thisObj);
+ jint result = gEnv->CallIntMethod(funcObj, gInvokeMeth, execState,
+ thisObj, jsargs);
+ if (gEnv->ExceptionCheck()) {
+ TRACE("FAIL FuncWrapper::callAsFunction: java exception is active");
+ return jsUndefined();
+ }
- if (execState->hadException())
- return TRACE("FAIL FuncWrapper::callAsFunction: js exception is active"), jsUndefined();
+ if (execState->hadException()) {
+ TRACE("FAIL FuncWrapper::callAsFunction: js exception is active");
+ return jsUndefined();
+ }
- TRACE("SUCCESS FuncWrapper::callAsFunction");
- return (JSValue*)result;
+ TRACE("SUCCESS FuncWrapper::callAsFunction");
+ return reinterpret_cast<JSValue*>(result);
}
diff --git a/jni/mac/FuncWrapper.h b/jni/mac/FuncWrapper.h
index f129ded..c827814 100644
--- a/jni/mac/FuncWrapper.h
+++ b/jni/mac/FuncWrapper.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Google Inc.
+ * Copyright 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
@@ -19,23 +19,26 @@
#include "FunctionObject.h"
#include <jni.h>
-// This class actually wraps Java method objects
+/*
+ * Wraps Java methods (MethodDispatch)
+ */
class FuncWrapper : public FunctionObject {
public:
- // funcObj MUST be a global ref
- FuncWrapper(const KJS::UString& name, jobject funcObj);
- virtual ~FuncWrapper();
- jobject getFuncObj();
+ // funcObj MUST be a global ref
+ FuncWrapper(const KJS::UString& name, jobject funcObj);
+ virtual ~FuncWrapper();
+ jobject getFuncObj();
public:
- virtual KJS::JSValue *callAsFunction(KJS::ExecState*, KJS::JSObject*, const KJS::List&);
+ virtual KJS::JSValue *callAsFunction(KJS::ExecState*, KJS::JSObject*,
+ const KJS::List&);
private:
- jobject funcObj;
+ jobject funcObj;
};
inline jobject FuncWrapper::getFuncObj() {
- return funcObj;
+ return funcObj;
}
#endif
diff --git a/jni/mac/FunctionObject.cpp b/jni/mac/FunctionObject.cpp
index eac0403..2f2e270 100644
--- a/jni/mac/FunctionObject.cpp
+++ b/jni/mac/FunctionObject.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Google Inc.
+ * Copyright 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
@@ -22,123 +22,144 @@
class CallFunction : public FunctionObject {
public:
- CallFunction(): FunctionObject("call") {
- }
+ CallFunction(): FunctionObject("call") {
+ }
- virtual JSValue *callAsFunction(ExecState *exec, JSObject *thisObj, const List &args) {
- // Copied from FunctionProtoFunc::callAsFunction()
- JSValue *thisArg = args[0];
- JSObject *func = thisObj;
+ virtual JSValue *callAsFunction(ExecState *exec, JSObject *thisObj,
+ const List &args)
+ {
+ // Copied from FunctionProtoFunc::callAsFunction()
+ JSValue *thisArg = args[0];
+ JSObject *func = thisObj;
- if (!func->implementsCall())
- return throwError(exec, TypeError);
+ if (!func->implementsCall()) {
+ return throwError(exec, TypeError);
+ }
- JSObject *callThis;
- if (thisArg->isUndefinedOrNull())
- callThis = exec->dynamicInterpreter()->globalObject();
- else
- callThis = thisArg->toObject(exec);
+ JSObject *callThis;
+ if (thisArg->isUndefinedOrNull()) {
+ callThis = exec->dynamicInterpreter()->globalObject();
+ } else {
+ callThis = thisArg->toObject(exec);
+ }
- return func->call(exec, callThis, args.copyTail());
- }
+ return func->call(exec, callThis, args.copyTail());
+ }
};
class ApplyFunction : public FunctionObject {
public:
- ApplyFunction(): FunctionObject("apply") {
- }
+ ApplyFunction(): FunctionObject("apply") {
+ }
- virtual JSValue *callAsFunction(ExecState *exec, JSObject *thisObj, const List &args) {
- // Copied from FunctionProtoFunc::callAsFunction()
- JSObject *func = thisObj;
- if (!func->implementsCall())
- return throwError(exec, TypeError);
+ virtual JSValue *callAsFunction(ExecState *exec, JSObject *thisObj,
+ const List &args)
+ {
+ // Copied from FunctionProtoFunc::callAsFunction()
+ JSObject *func = thisObj;
+ if (!func->implementsCall()) {
+ return throwError(exec, TypeError);
+ }
- JSValue *thisArg = args[0];
- JSObject *applyThis;
- if (thisArg->isUndefinedOrNull())
- applyThis = exec->dynamicInterpreter()->globalObject();
- else
- applyThis = thisArg->toObject(exec);
+ JSValue *thisArg = args[0];
+ JSObject *applyThis;
+ if (thisArg->isUndefinedOrNull()) {
+ applyThis = exec->dynamicInterpreter()->globalObject();
+ } else {
+ applyThis = thisArg->toObject(exec);
+ }
- JSValue *argArray = args[1];
- List applyArgs;
- if (!argArray->isUndefinedOrNull()) {
- if (!argArray->isObject(&ArrayInstance::info))
- return throwError(exec, TypeError);
+ JSValue *argArray = args[1];
+ List applyArgs;
+ if (!argArray->isUndefinedOrNull()) {
+ if (!argArray->isObject(&ArrayInstance::info)) {
+ return throwError(exec, TypeError);
+ }
- JSObject *argArrayObj = static_cast<JSObject *>(argArray);
- unsigned int length = argArrayObj->get(exec, lengthPropertyName)->toUInt32(exec);
- for (unsigned int i = 0; i < length; ++i) {
- applyArgs.append(argArrayObj->get(exec,i));
- }
- }
- return func->call(exec, applyThis, applyArgs);
- }
+ JSObject *argArrayObj = static_cast<JSObject *>(argArray);
+ unsigned int length = argArrayObj->get(exec, lengthPropertyName)
+ ->toUInt32(exec);
+ for (unsigned int i = 0; i < length; ++i) {
+ applyArgs.append(argArrayObj->get(exec,i));
+ }
+ }
+ return func->call(exec, applyThis, applyArgs);
+ }
};
static UString makeFunctionString(const UString& name) {
- return "\nfunction " + name + "() {\n [native code]\n}\n";
+ return "\nfunction " + name + "() {\n [native code]\n}\n";
}
-JSValue *FunctionObject::getter(ExecState* exec, JSObject* obj, const Identifier& propertyName, const PropertySlot& slot) {
- if (propertyName.ustring() == "toString") {
- return new ToStringFunction();
- } else if (propertyName.ustring() == "call") {
- return new CallFunction();
- } else if (propertyName.ustring() == "apply") {
- return new ApplyFunction();
- }
- return jsUndefined();
+JSValue *FunctionObject::getter(ExecState* exec, JSObject* obj,
+ const Identifier& propertyName, const PropertySlot& slot)
+{
+ if (propertyName.ustring() == "toString") {
+ return new ToStringFunction();
+ } else if (propertyName.ustring() == "call") {
+ return new CallFunction();
+ } else if (propertyName.ustring() == "apply") {
+ return new ApplyFunction();
+ }
+ return jsUndefined();
}
FunctionObject::FunctionObject(const UString& name): name(name) {
}
-bool FunctionObject::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot) {
- if (propertyName.ustring() == "toString") {
- slot.setCustom(this, getter);
- return true;
- }
- if (propertyName.ustring() == "call") {
- slot.setCustom(this, getter);
- return true;
- }
- if (propertyName.ustring() == "apply") {
- slot.setCustom(this, getter);
- return true;
- }
- return false;
+bool FunctionObject::getOwnPropertySlot(ExecState *exec,
+ const Identifier& propertyName, PropertySlot& slot)
+{
+ if (propertyName.ustring() == "toString") {
+ slot.setCustom(this, getter);
+ return true;
+ }
+ if (propertyName.ustring() == "call") {
+ slot.setCustom(this, getter);
+ return true;
+ }
+ if (propertyName.ustring() == "apply") {
+ slot.setCustom(this, getter);
+ return true;
+ }
+ return false;
}
-bool FunctionObject::canPut(ExecState *exec, const Identifier &propertyName) const {
- return false;
+bool FunctionObject::canPut(ExecState *exec, const Identifier &propertyName)
+ const
+{
+ return false;
}
-void FunctionObject::put(ExecState *exec, const Identifier &propertyName, JSValue *value, int attr) {
+void FunctionObject::put(ExecState *exec, const Identifier &propertyName,
+ JSValue *value, int attr)
+{
}
-bool FunctionObject::deleteProperty(ExecState *exec, const Identifier &propertyName) {
- return false;
+bool FunctionObject::deleteProperty(ExecState *exec,
+ const Identifier &propertyName)
+{
+ return false;
}
JSValue *FunctionObject::defaultValue(ExecState *exec, JSType hint) const {
- return jsString(makeFunctionString(name));
+ return jsString(makeFunctionString(name));
}
bool FunctionObject::implementsCall() const {
- return true;
+ return true;
}
// ToStringFunction
-
ToStringFunction::ToStringFunction(): FunctionObject("toString") {
}
-JSValue *ToStringFunction::callAsFunction(ExecState *exec, JSObject *thisObj, const List &args) {
- if (!thisObj) {
- return throwError(exec, TypeError);
- }
- return jsString(thisObj->toString(exec));
+JSValue *ToStringFunction::callAsFunction(ExecState *exec, JSObject *thisObj,
+ const List &args)
+{
+ if (!thisObj) {
+ return throwError(exec, TypeError);
+ }
+ return jsString(thisObj->toString(exec));
}
diff --git a/jni/mac/FunctionObject.h b/jni/mac/FunctionObject.h
index 4d6fa4e..4f8e908 100644
--- a/jni/mac/FunctionObject.h
+++ b/jni/mac/FunctionObject.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Google Inc.
+ * Copyright 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
@@ -21,35 +21,39 @@
class FunctionObject : public KJS::JSObject {
protected:
- FunctionObject(const KJS::UString& name);
+ FunctionObject(const KJS::UString& name);
public:
- const KJS::ClassInfo *classInfo() const { return &info; }
+ const KJS::ClassInfo *classInfo() const { return &info; }
- // shared implementations of JSObject methods
- virtual bool getOwnPropertySlot(KJS::ExecState*, const KJS::Identifier&, KJS::PropertySlot&);
- virtual bool canPut(KJS::ExecState*, const KJS::Identifier&) const;
- virtual void put(KJS::ExecState*, const KJS::Identifier&, KJS::JSValue*, int);
- virtual bool deleteProperty(KJS::ExecState*, const KJS::Identifier&);
- virtual KJS::JSValue *defaultValue(KJS::ExecState*, KJS::JSType) const;
- virtual bool implementsCall() const;
-
- // subclasses must implement
- virtual KJS::JSValue *callAsFunction(KJS::ExecState*, KJS::JSObject*, const KJS::List&) = 0;
+ // shared implementations of JSObject methods
+ virtual bool getOwnPropertySlot(KJS::ExecState*, const KJS::Identifier&,
+ KJS::PropertySlot&);
+ virtual bool canPut(KJS::ExecState*, const KJS::Identifier&) const;
+ virtual void put(KJS::ExecState*, const KJS::Identifier&, KJS::JSValue*, int);
+ virtual bool deleteProperty(KJS::ExecState*, const KJS::Identifier&);
+ virtual KJS::JSValue *defaultValue(KJS::ExecState*, KJS::JSType) const;
+ virtual bool implementsCall() const;
+
+ // subclasses must implement
+ virtual KJS::JSValue *callAsFunction(KJS::ExecState*, KJS::JSObject*,
+ const KJS::List&) = 0;
- static const KJS::ClassInfo info;
+ static const KJS::ClassInfo info;
private:
- static KJS::JSValue* getter(KJS::ExecState*, KJS::JSObject*, const KJS::Identifier&, const KJS::PropertySlot&);
+ static KJS::JSValue* getter(KJS::ExecState*, KJS::JSObject*,
+ const KJS::Identifier&, const KJS::PropertySlot&);
private:
- KJS::UString name;
+ KJS::UString name;
};
class ToStringFunction : public FunctionObject {
public:
- ToStringFunction();
- virtual KJS::JSValue *callAsFunction(KJS::ExecState*, KJS::JSObject*, const KJS::List&);
+ ToStringFunction();
+ virtual KJS::JSValue *callAsFunction(KJS::ExecState*, KJS::JSObject*,
+ const KJS::List&);
};
#endif
diff --git a/jni/mac/JStringWrap.h b/jni/mac/JStringWrap.h
index 9d2c306..e17981f 100644
--- a/jni/mac/JStringWrap.h
+++ b/jni/mac/JStringWrap.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Google Inc.
+ * Copyright 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
@@ -18,18 +18,25 @@
#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); }
+ 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;
+ JNIEnv* env;
+ jstring s;
+ const char* p;
+ const jchar* jp;
};
#endif
diff --git a/jni/mac/Makefile b/jni/mac/Makefile
index 2787428..e0614ee 100644
--- a/jni/mac/Makefile
+++ b/jni/mac/Makefile
@@ -1,4 +1,4 @@
-# Copyright 2006 Google Inc.
+# Copyright 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
@@ -13,6 +13,11 @@
# the License.
##
+# Try a GWT_TOOLS default if it isn't set
+##
+GWT_TOOLS ?= ../../../tools
+
+##
# External WebKit products.
##
WEBKIT_REDIST=$(GWT_TOOLS)/redist/webkit/WebKit-418.9.tar.gz
@@ -77,6 +82,7 @@
staging: all
cp libgwt-*.jnilib ../../build/staging/gwt-mac-0.0.0/
+
##
# Copy WebKit binary frameworks locally.
##
@@ -99,6 +105,8 @@
gwt-args.o: gwt-args.cpp
$(CC) -c -o gwt-args.o $(CFLAGS) gwt-args.cpp
+gwt-webkit.o: gwt-webkit.h
+
##
# Rule for final lib for gwt-ll.
##
@@ -112,6 +120,9 @@
$(CC) -o $(GWT_WEBKIT_LIB) $(JSCORE_LFLAGS) $(GWT_WEBKIT_OBJECTS)
$(FIX_INSTALL_NAME) JavaScriptCore $(JSCORE_INSTALL_NAME) $(GWT_WEBKIT_LIB)
+install: $(GWT_LL_LIB) $(GWT_WEBKIT_LIB)
+ cp $(GWT_LL_LIB) $(GWT_WEBKIT_LIB) prebuilt/
+
##
# Unpack and patch SWT source.
##
diff --git a/jni/mac/gwt-args.cpp b/jni/mac/gwt-args.cpp
index 4ab3ffa..12b6eee 100644
--- a/jni/mac/gwt-args.cpp
+++ b/jni/mac/gwt-args.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Google Inc.
+ * Copyright 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
@@ -27,7 +27,7 @@
*/
JNIEXPORT jint JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1getArgc
(JNIEnv* env , jclass) {
- return *_NSGetArgc();
+ return *_NSGetArgc();
}
/*
@@ -35,12 +35,14 @@
* Method: _getArgv
* Signature: ()Ljava/lang/String;
*/
-JNIEXPORT jstring JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1getArgv
- (JNIEnv* env, jclass, jint i) {
- int argc = *_NSGetArgc();
- if (i < 0 || i >= argc) {
- return 0;
- }
- char **argv = *_NSGetArgv();
- return env->NewStringUTF(argv[i]);
+JNIEXPORT jstring JNICALL
+Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1getArgv
+ (JNIEnv* env, jclass, jint i)
+{
+ int argc = *_NSGetArgc();
+ if (i < 0 || i >= argc) {
+ return 0;
+ }
+ char **argv = *_NSGetArgv();
+ return env->NewStringUTF(argv[i]);
}
diff --git a/jni/mac/gwt-ll.h b/jni/mac/gwt-ll.h
index a2df826..a1e2212 100644
--- a/jni/mac/gwt-ll.h
+++ b/jni/mac/gwt-ll.h
@@ -114,10 +114,10 @@
/*
* Class: com_google_gwt_dev_shell_mac_LowLevelSaf
* Method: _gcUnlock
- * Signature: (I)V
+ * Signature: (ILjava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1gcUnlock
- (JNIEnv *, jclass, jint);
+ (JNIEnv *, jclass, jint, jstring);
/*
* Class: com_google_gwt_dev_shell_mac_LowLevelSaf
diff --git a/jni/mac/gwt-webkit.cpp b/jni/mac/gwt-webkit.cpp
index 08d854b..800033c 100644
--- a/jni/mac/gwt-webkit.cpp
+++ b/jni/mac/gwt-webkit.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Google Inc.
+ * Copyright 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
@@ -28,7 +28,13 @@
using namespace KJS;
-static void PrintJSValue(JSValue* val, char* prefix="") {
+/*
+ * Print a JSValue in human-readable form.
+ *
+ * val JSValue* to print
+ * prefix a string to print before the value
+ */
+void PrintJSValue(JSValue* val, char* prefix) {
static const char* typeStrings[]={
"unspecified",
"number",
@@ -39,25 +45,54 @@
"object",
"getter/setter",
};
+ char buf[256];
+ snprintf(buf, sizeof(buf), "%s{%08x}:", prefix, unsigned(val));
+ TRACE(buf);
JSType type = val->type();
const char* typeString=typeStrings[type];
- char buf[256];
- char* p = buf;
- p += snprintf(p, sizeof(buf)-(p-buf), "%s%s: ", prefix, typeString);
- if (val->isNumber()) {
- p += snprintf(p, sizeof(buf)-(p-buf), "%lf", val->getNumber());
- } else if(val->isString()) {
- CString str(val->getString().UTF8String());
- p += snprintf(p, sizeof(buf)-(p-buf), "%.*s", str.size(), str.c_str());
- } else if(val->isObject()) {
- const JSObject* obj = val->getObject();
- const ClassInfo* cinfo = obj->classInfo();
- const char* cname = cinfo ? cinfo->className : "js object";
- p += snprintf(p, sizeof(buf)-(p-buf), "%s @ %08x", cname, unsigned(obj));
- } else if(val->isBoolean()) {
- p += snprintf(p, sizeof(buf)-(p-buf), "%s", val->getBoolean() ? "true" : "false");
- }
- TRACE(buf);
+ char* p = buf;
+ p += snprintf(p, sizeof(buf)-(p-buf), " %s: ", typeString);
+ //p += snprintf(p, sizeof(buf)-(p-buf), "%s{%08x} %s: ", prefix,
+ // unsigned(val), typeString);
+ if (val->isNumber()) {
+ p += snprintf(p, sizeof(buf)-(p-buf), "%lf", val->getNumber());
+ } else if(val->isString()) {
+ CString str(val->getString().UTF8String());
+ p += snprintf(p, sizeof(buf)-(p-buf), "%.*s", (int)str.size(),
+ str.c_str());
+ } else if(val->isObject()) {
+ const JSObject* obj = val->getObject();
+ const ClassInfo* cinfo = obj->classInfo();
+ const char* cname = cinfo ? cinfo->className : "js object";
+ p += snprintf(p, sizeof(buf)-(p-buf), "%s @ %08x", cname, unsigned(obj));
+ } else if(val->isBoolean()) {
+ p += snprintf(p, sizeof(buf)-(p-buf), "%s", val->getBoolean() ? "true" : "false");
+ }
+ TRACE(buf);
+}
+
+/*
+ * Called for each gcProtect, only if TRACING is enabled.
+ *
+ * val JSValue* to be protected
+ */
+void gcProtectHook(JSValue* val) {
+ PrintJSValue(val, "gcProtect: val=");
+}
+
+/*
+ * Called for each gcUnprotect, only if TRACING is enabled.
+ *
+ * val JSValue* to be protected
+ * trace string containing trace information for the creation site, or null
+ * if not available
+ */
+void gcUnprotectHook(JSValue* val, const char* trace) {
+ if (trace) {
+ TRACE("gcUnprotect - value created at:");
+ TRACE(trace);
+ }
+ PrintJSValue(val, "gcUnprotect: val=");
}
/*
@@ -66,14 +101,16 @@
* Signature: (I)Z
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_isNull
- (JNIEnv *env, jclass, jint jsval) {
- TRACE("ENTER LowLevelSaf__isNull");
+ (JNIEnv *env, jclass, jint jsval)
+{
+ TRACE("ENTER LowLevelSaf__isNull");
JSValue* val = (JSValue*)jsval;
- if (!val)
+ if (!val) {
return JNI_FALSE;
+ }
- TRACE("SUCCESS LowLevelSaf__isNull");
+ TRACE("SUCCESS LowLevelSaf__isNull");
return val->isNull();
}
@@ -84,13 +121,15 @@
*/
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_gwt_dev_shell_mac_LowLevelSaf_isUndefined
- (JNIEnv *env, jclass, jint jsval) {
- TRACE("ENTER LowLevelSaf__isUndefined");
+ (JNIEnv *env, jclass, jint jsval)
+{
+ TRACE("ENTER LowLevelSaf__isUndefined");
JSValue* val = (JSValue*)jsval;
- if (!val)
+ if (!val) {
return JNI_FALSE;
+ }
- TRACE("SUCCESS LowLevelSaf__isUndefined");
+ TRACE("SUCCESS LowLevelSaf__isUndefined");
return val->isUndefined();
}
@@ -101,8 +140,9 @@
*/
extern "C" JNIEXPORT jint JNICALL
Java_com_google_gwt_dev_shell_mac_LowLevelSaf_jsNull
- (JNIEnv *, jclass) {
- return (jint)jsNull();
+ (JNIEnv *, jclass)
+{
+ return reinterpret_cast<jint>(jsNull());
}
/*
@@ -112,7 +152,8 @@
*/
extern "C" JNIEXPORT jstring JNICALL
Java_com_google_gwt_dev_shell_mac_LowLevelSaf_getTypeString
- (JNIEnv *env, jclass, jint jsval) {
+ (JNIEnv *env, jclass, jint jsval)
+{
static const char* typeStrings[]={
"unspecified",
"number",
@@ -124,16 +165,17 @@
"getter/setter",
};
JSValue* val = (JSValue*)jsval;
- if (!val)
+ if (!val) {
return 0;
+ }
JSType type = val->type();
const char* typeString=typeStrings[type];
if (type == ObjectType) {
- if (val->isObject(&DispWrapper::info)) {
- typeString = "Java object";
- } else {
- typeString = "JS object";
- }
+ if (val->isObject(&DispWrapper::info)) {
+ typeString = "Java object";
+ } else {
+ typeString = "JS object";
+ }
}
return env->NewStringUTF(typeString);
}
@@ -144,8 +186,9 @@
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_jsUndefined
- (JNIEnv *env, jclass) {
- return (jint)jsUndefined();
+ (JNIEnv *env, jclass)
+{
+ return reinterpret_cast<jint>(jsUndefined());
}
/*
@@ -153,19 +196,23 @@
* Method: _coerceToBoolean
* Signature: (II[Z)Z
*/
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1coerceToBoolean
- (JNIEnv * env, jclass, jint execState, jint jsval, jbooleanArray rval) {
- TRACE("ENTER LowLevelSaf__1coerceToBoolean");
+JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1coerceToBoolean
+ (JNIEnv * env, jclass, jint execState, jint jsval, jbooleanArray rval)
+{
+ TRACE("ENTER LowLevelSaf__1coerceToBoolean");
- if (!execState || !jsval)
+ if (!execState || !jsval) {
return JNI_FALSE;
+ }
jboolean result = ((JSValue*)jsval)->toBoolean((ExecState*)execState);
env->SetBooleanArrayRegion(rval, 0, 1, &result);
- if (env->ExceptionCheck())
- return JNI_FALSE;
+ if (env->ExceptionCheck()) {
+ return JNI_FALSE;
+ }
- TRACE("SUCCESS LowLevelSaf__1coerceToBoolean");
+ TRACE("SUCCESS LowLevelSaf__1coerceToBoolean");
return JNI_TRUE;
}
@@ -174,19 +221,23 @@
* Method: _coerceToDouble
* Signature: (II[D)Z
*/
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1coerceToDouble
- (JNIEnv *env, jclass, jint execState, jint jsval, jdoubleArray rval) {
- TRACE("ENTER LowLevelSaf__1coerceToDouble");
+JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1coerceToDouble
+ (JNIEnv *env, jclass, jint execState, jint jsval, jdoubleArray rval)
+{
+ TRACE("ENTER LowLevelSaf__1coerceToDouble");
- if (!execState || !jsval)
+ if (!execState || !jsval) {
return JNI_FALSE;
+ }
jdouble result = ((JSValue*)jsval)->toNumber((ExecState*)execState);
env->SetDoubleArrayRegion(rval, 0, 1, &result);
- if (env->ExceptionCheck())
- return JNI_FALSE;
+ if (env->ExceptionCheck()) {
+ return JNI_FALSE;
+ }
- TRACE("SUCCESS LowLevelSaf__1coerceToDouble");
+ TRACE("SUCCESS LowLevelSaf__1coerceToDouble");
return JNI_TRUE;
}
@@ -195,13 +246,16 @@
* Method: _coerceToString
* Signature: (II[Ljava/lang/String;)Z
*/
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1coerceToString
- (JNIEnv *env, jclass, jint execState, jint jsval, jobjectArray rval) {
- TRACE("ENTER LowLevelSaf__1coerceToString");
+JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1coerceToString
+ (JNIEnv *env, jclass, jint execState, jint jsval, jobjectArray rval)
+{
+ TRACE("ENTER LowLevelSaf__1coerceToString");
JSValue *val = (JSValue*)jsval;
- if (!execState || !val)
+ if (!execState || !val) {
return JNI_FALSE;
+ }
/*
* Convert all objects to their string representation, EXCEPT
@@ -216,8 +270,9 @@
}
env->SetObjectArrayElement(rval,0,result);
- if (env->ExceptionCheck())
+ if (env->ExceptionCheck()) {
return JNI_FALSE;
+ }
TRACE("SUCCESS LowLevelSaf__1coerceToString");
return JNI_TRUE;
@@ -228,22 +283,35 @@
* Method: _convertBoolean
* Signature: (IZ[I)Z
*/
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1convertBoolean
- (JNIEnv *env, jclass, jboolean jval, jintArray rval) {
- TRACE("ENTER LowLevelSaf__1convertBoolean");
+JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1convertBoolean
+ (JNIEnv *env, jclass, jboolean jval, jintArray rval)
+{
+ TRACE("ENTER LowLevelSaf__1convertBoolean");
+#ifdef ENABLE_TRACING
char buf[256];
snprintf(buf, sizeof(buf), " val=%s", jval ? "true" : "false");
TRACE(buf);
+#endif
JSValue *jsval = (jval == JNI_FALSE) ? jsBoolean(false) : jsBoolean(true);
- if (!jsval)
+
+ /*
+ * Since we know this is a boolean primitive, the protect is a no-op, but
+ * it is useful to have it for the trace log.
+ */
+ gwtGCProtect(jsval);
+
+ if (!jsval) {
return JNI_FALSE;
+ }
env->SetIntArrayRegion(rval,0,1,(const jint*)&jsval);
- if (env->ExceptionCheck())
+ if (env->ExceptionCheck()) {
return JNI_FALSE;
+ }
- TRACE("SUCCESS LowLevelSaf__1convertBoolean");
+ TRACE("SUCCESS LowLevelSaf__1convertBoolean");
return JNI_TRUE;
}
@@ -253,8 +321,9 @@
* Signature: (ID[I)Z
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1convertDouble
- (JNIEnv *env, jclass, jdouble jval, jintArray rval) {
- TRACE("ENTER LowLevelSaf__1convertDouble");
+ (JNIEnv *env, jclass, jdouble jval, jintArray rval)
+{
+ TRACE("ENTER LowLevelSaf__1convertDouble");
#ifdef ENABLE_TRACING
char buf[256];
snprintf(buf, sizeof(buf), " val=%lf", jval);
@@ -262,14 +331,17 @@
#endif
JSValue *jsval = jsNumber(jval);
- if (!jsval)
+ gwtGCProtect(jsval);
+ if (!jsval) {
return JNI_FALSE;
+ }
env->SetIntArrayRegion(rval,0,1,(const jint*)&jsval);
- if (env->ExceptionCheck())
+ if (env->ExceptionCheck()) {
return JNI_FALSE;
+ }
- TRACE("SUCCESS LowLevelSaf__1convertDouble");
+ TRACE("SUCCESS LowLevelSaf__1convertDouble");
return JNI_TRUE;
}
@@ -279,12 +351,14 @@
* Signature: (ILjava/lang/String;[I)Z
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1convertString
- (JNIEnv *env, jclass, jstring jval, jintArray rval) {
- TRACE("ENTER LowLevelSaf__1convertString");
+ (JNIEnv *env, jclass, jstring jval, jintArray rval)
+{
+ TRACE("ENTER LowLevelSaf__1convertString");
JStringWrap jstr(env, jval);
- if (!jstr.jstr())
+ if (!jstr.jstr()) {
return JNI_FALSE;
+ }
#ifdef ENABLE_TRACING
char buf[256];
snprintf(buf, sizeof(buf), " val=%s", jstr.str());
@@ -292,24 +366,16 @@
#endif
JSValue *jsval = jsString(UString((const UChar*)jstr.jstr(), jstr.length()));
- /*
- * TODO / LEAK / HACK: We will be persisting these objects on the java side,
- * so in order to keep the JS GC from collecting our objects when they are
- * away, we need to add them to the protect list. This should be refactored
- * out in favor of better memory mgmt scheme.
- */
- gcProtectNullTolerant(jsval);
- if (!jsval)
- return JNI_FALSE;
-#ifdef ENABLE_TRACING
- snprintf(buf, sizeof(buf), " return={%08x} ", unsigned(jsval));
- PrintJSValue(jsval, buf);
-#endif
-
- env->SetIntArrayRegion(rval,0,1,(const jint*)&jsval);
- if (env->ExceptionCheck())
+ gwtGCProtect(jsval);
+ if (!jsval) {
return JNI_FALSE;
+ }
+
+ env->SetIntArrayRegion(rval,0,1,(const jint*)&jsval);
+ if (env->ExceptionCheck()) {
+ return JNI_FALSE;
+ }
TRACE("SUCCESS LowLevelSaf__1convertString");
return JNI_TRUE;
@@ -321,14 +387,18 @@
* Signature: (ILjava/lang/String;)Z
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1executeScript
- (JNIEnv* env, jclass, jint execState, jstring code) {
- TRACE("ENTER LowLevelSaf__1executeScript");
- if (!execState || !code)
+ (JNIEnv* env, jclass, jint execState, jstring code)
+{
+ TRACE("ENTER LowLevelSaf__1executeScript");
+ if (!execState || !code) {
return JNI_FALSE;
+ }
JStringWrap jcode(env, code);
- if (!jcode.jstr())
+ if (!jcode.jstr()) {
return JNI_FALSE;
+ }
+
#ifdef ENABLE_TRACING
char buf[1024];
snprintf(buf, sizeof(buf), " code=%s", jcode.str());
@@ -336,8 +406,9 @@
#endif
Interpreter* interp = ((ExecState*)execState)->dynamicInterpreter();
- if (!interp)
+ if (!interp) {
return JNI_FALSE;
+ }
interp->evaluate(UString(), 0, (const UChar*)jcode.jstr(), jcode.length());
TRACE("SUCCESS LowLevelSaf__1executeScript");
@@ -349,32 +420,40 @@
* Method: _executeScriptWithInfo
* Signature: (ILjava/lang/String;Ljava/lang/String;I)Z
*/
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1executeScriptWithInfo
- (JNIEnv* env, jclass, jint execState, jstring code, jstring file, jint line) {
- TRACE("ENTER LowLevelSaf__1executeScriptWithInfo");
- if (!execState || !code || !file)
+JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1executeScriptWithInfo
+ (JNIEnv* env, jclass, jint execState, jstring code, jstring file, jint line)
+{
+ TRACE("ENTER LowLevelSaf__1executeScriptWithInfo");
+ if (!execState || !code || !file) {
return JNI_FALSE;
+ }
JStringWrap jcode(env, code);
- if (!jcode.jstr())
+ if (!jcode.jstr()) {
return JNI_FALSE;
+ }
JStringWrap jfile(env, file);
- if (!jcode.jstr())
+ if (!jcode.jstr()) {
return JNI_FALSE;
+ }
#ifdef ENABLE_TRACING
char buf[1024];
- snprintf(buf, sizeof(buf), " code=%s, file=%s, line=%d", jcode.str(), jfile.str(), line);
+ snprintf(buf, sizeof(buf), " code=%s, file=%s, line=%d", jcode.str(),
+ jfile.str(), static_cast<int>(line));
TRACE(buf);
#endif
Interpreter* interp = ((ExecState*)execState)->dynamicInterpreter();
- if (!interp)
+ if (!interp) {
return JNI_FALSE;
+ }
- interp->evaluate(UString((const UChar*)jfile.jstr(), jfile.length()), line, (const UChar*)jcode.jstr(), jcode.length());
- TRACE("SUCCESS LowLevelSaf__1executeScriptWithInfo");
+ interp->evaluate(UString((const UChar*)jfile.jstr(), jfile.length()), line,
+ (const UChar*)jcode.jstr(), jcode.length());
+ TRACE("SUCCESS LowLevelSaf__1executeScriptWithInfo");
return JNI_TRUE;
}
@@ -384,18 +463,22 @@
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1gcLock
- (JNIEnv *, jclass, jint jsval) {
- gcProtectNullTolerant((JSValue*)jsval);
+ (JNIEnv *, jclass, jint jsval)
+{
+ gwtGCProtect(reinterpret_cast<JSValue*>(jsval));
}
/*
* Class: com_google_gwt_dev_shell_mac_LowLevelSaf
* Method: _gcUnlock
- * Signature: (I)V
+ * Signature: (ILjava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1gcUnlock
- (JNIEnv *, jclass, jint jsval) {
- gcUnprotectNullTolerant((JSValue*)jsval);
+ (JNIEnv* jniEnv, jclass, jint jsval, jstring creationDesc)
+{
+ JStringWrap creationStr(jniEnv, creationDesc);
+ gwtGCUnprotect(reinterpret_cast<JSValue*>(jsval),
+ creationDesc ? creationStr.str() : 0);
}
/*
@@ -403,24 +486,29 @@
* Method: _getGlobalExecState
* Signature: (I[I)Z
*/
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1getGlobalExecState
- (JNIEnv *env, jclass, jint scriptObject, jintArray rval) {
- TRACE("ENTER LowLevelSaf__1getGlobalExecState");
+JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1getGlobalExecState
+ (JNIEnv *env, jclass, jint scriptObject, jintArray rval)
+{
+ TRACE("ENTER LowLevelSaf__1getGlobalExecState");
- if (!scriptObject || !((JSValue*)scriptObject)->isObject())
+ if (!scriptObject || !((JSValue*)scriptObject)->isObject()) {
return JNI_FALSE;
+ }
- Interpreter* interp = Interpreter::interpreterWithGlobalObject((JSObject*)scriptObject);
- if (!interp)
+ Interpreter* interp
+ = Interpreter::interpreterWithGlobalObject((JSObject*)scriptObject);
+ if (!interp) {
return JNI_FALSE;
+ }
ExecState* execState = interp->globalExec();
- env->SetIntArrayRegion(rval, 0, 1, (jint*)&execState);
- if (env->ExceptionCheck())
- return JNI_FALSE;
- TRACE("SUCCESS LowLevelSaf__1getGlobalExecState");
+ env->SetIntArrayRegion(rval, 0, 1, (jint*)&execState);
+ if (env->ExceptionCheck()) {
+ return JNI_FALSE;
+ }
+ TRACE("SUCCESS LowLevelSaf__1getGlobalExecState");
return JNI_TRUE;
-
}
/*
@@ -428,8 +516,10 @@
* Method: _initNative
* Signature: (Ljava/lang/Class;Ljava/lang/Class;)Z
*/
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1initNative
- (JNIEnv* env, jclass llClass, jclass dispObjCls, jclass dispMethCls) {
+JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1initNative
+ (JNIEnv* env, jclass llClass, jclass dispObjCls, jclass dispMethCls)
+{
Interpreter::setShouldPrintExceptions(true);
gEnv = env;
gClass = static_cast<jclass>(env->NewGlobalRef(llClass));
@@ -439,23 +529,29 @@
return false;
}
- gGetFieldMeth = env->GetMethodID(gDispObjCls, "getField", "(Ljava/lang/String;)I");
- gSetFieldMeth = env->GetMethodID(gDispObjCls, "setField", "(Ljava/lang/String;I)V");
+ gGetFieldMeth
+ = env->GetMethodID(gDispObjCls, "getField", "(Ljava/lang/String;)I");
+ gSetFieldMeth
+ = env->GetMethodID(gDispObjCls, "setField", "(Ljava/lang/String;I)V");
gInvokeMeth = env->GetMethodID(gDispMethCls, "invoke", "(II[I)I");
- gToStringMeth = env->GetMethodID(gDispObjCls, "toString", "()Ljava/lang/String;");
- if (!gGetFieldMeth || !gSetFieldMeth || !gInvokeMeth || !gToStringMeth || env->ExceptionCheck()) {
+ gToStringMeth
+ = env->GetMethodID(gDispObjCls, "toString", "()Ljava/lang/String;");
+ if (!gGetFieldMeth || !gSetFieldMeth || !gInvokeMeth || !gToStringMeth
+ || env->ExceptionCheck())
+ {
return false;
}
#ifdef FILETRACE
- gout = fopen("/tmp/gwt-ll.log", "w");
- filetrace("LOG STARTED");
+ gout = fopen("/tmp/gwt-ll.log", "w");
+ filetrace("LOG STARTED");
#endif // FILETRACE
#ifdef JAVATRACE
- gTraceMethod = env->GetStaticMethodID(gClass, "trace", "(Ljava/lang/String;)V");
- if (!gTraceMethod || env->ExceptionCheck())
- return false;
+ gTraceMethod = env->GetStaticMethodID(gClass, "trace", "(Ljava/lang/String;)V");
+ if (!gTraceMethod || env->ExceptionCheck()) {
+ return false;
+ }
#endif // JAVATRACE
return true;
@@ -466,52 +562,64 @@
* Method: _invoke
* Signature: (IILjava/lang/String;II[I[I)Z
*/
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1invoke
- (JNIEnv* env, jclass, jint jsexecState, jint jsScriptObject, jstring method, jint jsthis, jint argc, jintArray argv, jintArray rval) {
- TRACE("ENTER LowLevelSaf__1invoke");
+JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1invoke
+ (JNIEnv* env, jclass, jint jsexecState, jint jsScriptObject, jstring method,
+ jint jsthis, jint argc, jintArray argv, jintArray rval)
+{
+ TRACE("ENTER LowLevelSaf__1invoke");
- if (!jsexecState || !jsScriptObject || !method || !rval)
+ if (!jsexecState || !jsScriptObject || !method || !rval) {
return JNI_FALSE;
+ }
JStringWrap jmethod(env, method);
#ifdef ENABLE_TRACING
char buf[256];
- snprintf(buf, sizeof(buf), "sciptObject=%08x, method=%s, argc=%d",
- jsScriptObject, jmethod.str(), argc);
+ snprintf(buf, sizeof(buf), "scriptObject=%08x, method=%s, argc=%d",
+ static_cast<unsigned>(jsScriptObject), jmethod.str(),
+ static_cast<unsigned>(argc));
TRACE(buf);
PrintJSValue((JSValue*)jsthis, " jsthis=");
#endif
ExecState* execState = (ExecState*)jsexecState;
JSObject* scriptObj = (JSObject*)jsScriptObject;
- if (!scriptObj->isObject())
+ if (!scriptObj->isObject()) {
return JNI_FALSE;
+ }
- if (!jmethod.jstr())
+ if (!jmethod.jstr()) {
return JNI_FALSE;
+ }
JSObject* thisObj = (JSObject*)jsthis;
if (!thisObj || thisObj->isNull() || thisObj->isUndefined()) {
thisObj = scriptObj;
}
- if (!thisObj->isObject())
+ if (!thisObj->isObject()) {
return JNI_FALSE;
+ }
- JSValue* maybeFunc = scriptObj->get(execState, Identifier((const UChar*)jmethod.jstr(), jmethod.length()));
- if (!maybeFunc || !maybeFunc->isObject())
+ JSValue* maybeFunc = scriptObj->get(execState,
+ Identifier((const UChar*)jmethod.jstr(), jmethod.length()));
+ if (!maybeFunc || !maybeFunc->isObject()) {
return JNI_FALSE;
+ }
JSObject* func = (JSObject*)maybeFunc;
- if (!func->implementsCall())
+ if (!func->implementsCall()) {
return JNI_FALSE;
+ }
List args;
for (int i = 0; i < argc; ++i) {
jint argi;
env->GetIntArrayRegion(argv, i, 1, &argi);
- if (env->ExceptionCheck())
+ if (env->ExceptionCheck()) {
return JNI_FALSE;
+ }
#ifdef ENABLE_TRACING
- snprintf(buf, sizeof(buf), " arg[%d]={%08x} ", i, argi);
+ snprintf(buf, sizeof(buf), " arg[%d]=", i);
TRACE(buf);
PrintJSValue((JSValue*)argi, buf);
#endif
@@ -523,13 +631,11 @@
}
JSValue* result = func->call(execState, thisObj, args);
- gcProtectNullTolerant(result);
-#ifdef ENABLE_TRACING
- PrintJSValue(result, " return=");
-#endif
+ gwtGCProtect(result);
env->SetIntArrayRegion(rval, 0, 1, (jint*)&result);
- if (env->ExceptionCheck())
+ if (env->ExceptionCheck()) {
return JNI_FALSE;
+ }
TRACE("SUCCESS LowLevelSaf__1invoke");
return JNI_TRUE;
@@ -542,10 +648,12 @@
*/
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_gwt_dev_shell_mac_LowLevelSaf_isBoolean
- (JNIEnv *, jclass, jint jsval) {
- if (!jsval)
+ (JNIEnv *, jclass, jint jsval)
+{
+ if (!jsval) {
return JNI_FALSE;
- return ((JSValue*)jsval)->isBoolean() ? JNI_TRUE : JNI_FALSE;
+ }
+ return reinterpret_cast<JSValue*>(jsval)->isBoolean() ? JNI_TRUE : JNI_FALSE;
}
/*
@@ -555,10 +663,12 @@
*/
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_gwt_dev_shell_mac_LowLevelSaf_isNumber
- (JNIEnv *, jclass, jint jsval) {
- if (!jsval)
+ (JNIEnv *, jclass, jint jsval)
+{
+ if (!jsval) {
return JNI_FALSE;
- return ((JSValue*)jsval)->isNumber() ? JNI_TRUE : JNI_FALSE;
+ }
+ return reinterpret_cast<JSValue*>(jsval)->isNumber() ? JNI_TRUE : JNI_FALSE;
}
/*
@@ -570,19 +680,22 @@
*/
JNIEXPORT jboolean JNICALL
Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1isString
- (JNIEnv *, jclass, jint jsval) {
- if (!jsval)
+ (JNIEnv *, jclass, jint jsval)
+{
+ if (!jsval) {
return JNI_FALSE;
+ }
JSValue* jsValue = reinterpret_cast<JSValue*>(jsval);
if(jsValue->isString()) return JNI_TRUE;
- if(jsValue->isObject()) {
- const JSObject* obj = jsValue->getObject();
- const ClassInfo* cinfo = obj->classInfo();
- if (cinfo && !strcmp(cinfo->className, "String")) {
- return JNI_TRUE;
- }
- }
- return JNI_FALSE;
+ // check for JavaScript String objects
+ if(jsValue->isObject()) {
+ const JSObject* obj = jsValue->getObject();
+ const ClassInfo* cinfo = obj->classInfo();
+ if (cinfo && !strcmp(cinfo->className, "String")) {
+ return JNI_TRUE;
+ }
+ }
+ return JNI_FALSE;
}
/*
@@ -591,10 +704,12 @@
* Signature: (I)Z
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1isObject
- (JNIEnv *, jclass, jint jsval) {
- if (!jsval)
+ (JNIEnv *, jclass, jint jsval)
+{
+ if (!jsval) {
return JNI_FALSE;
- return ((JSValue*)jsval)->isObject() ? JNI_TRUE : JNI_FALSE;
+ }
+ return reinterpret_cast<JSValue*>(jsval)->isObject() ? JNI_TRUE : JNI_FALSE;
}
/*
@@ -603,20 +718,23 @@
* Signature: (I[Z)Z
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1isWrappedDispatch
- (JNIEnv* env, jclass, jint jsval, jbooleanArray rval) {
- TRACE("ENTER LowLevelSaf__1isWrappedDispatch");
- if (!jsval)
+ (JNIEnv* env, jclass, jint jsval, jbooleanArray rval)
+{
+ TRACE("ENTER LowLevelSaf__1isWrappedDispatch");
+ if (!jsval) {
return JNI_FALSE;
+ }
JSValue* val = (JSValue*)jsval;
jboolean result = val->isObject(&DispWrapper::info) ? JNI_TRUE : JNI_FALSE;
- env->SetBooleanArrayRegion(rval, 0, 1, &result);
- if (env->ExceptionCheck())
- return JNI_FALSE;
+ env->SetBooleanArrayRegion(rval, 0, 1, &result);
+ if (env->ExceptionCheck()) {
+ return JNI_FALSE;
+ }
- TRACE("SUCCESS LowLevelSaf__1isWrappedDispatch");
- return JNI_TRUE;
+ TRACE("SUCCESS LowLevelSaf__1isWrappedDispatch");
+ return JNI_TRUE;
}
/*
@@ -625,7 +743,8 @@
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1jsLock
- (JNIEnv *, jclass) {
+ (JNIEnv *, jclass)
+{
JSLock::lock();
}
@@ -635,7 +754,8 @@
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1jsUnlock
- (JNIEnv *, jclass) {
+ (JNIEnv *, jclass)
+{
JSLock::unlock();
}
@@ -645,14 +765,17 @@
* Signature: (II)Z
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1raiseJavaScriptException
- (JNIEnv *env, jclass, jint execState, jint jsval) {
- TRACE("ENTER LowLevelSaf__1raiseJavaScriptException");
+ (JNIEnv *env, jclass, jint execState, jint jsval)
+{
+ TRACE("ENTER LowLevelSaf__1raiseJavaScriptException");
- if (!execState || !jsval)
+ if (!execState || !jsval) {
return JNI_FALSE;
+ }
- ((ExecState*)execState)->setException((JSValue*)jsval);
- TRACE("SUCCESS LowLevelSaf__1raiseJavaScriptException");
+ reinterpret_cast<ExecState*>(execState)->setException(
+ reinterpret_cast<JSValue*>(jsval));
+ TRACE("SUCCESS LowLevelSaf__1raiseJavaScriptException");
return JNI_TRUE;
}
@@ -662,22 +785,26 @@
* Signature: (I[Lcom/google/gwt/dev/shell/mac/LowLevelSaf/DispatchObject;)Z
*/
JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1unwrapDispatch
- (JNIEnv* env, jclass, jint jsval, jobjectArray rval) {
- TRACE("ENTER LowLevelSaf__1unwrapDispatch");
- if (!jsval)
+ (JNIEnv* env, jclass, jint jsval, jobjectArray rval)
+{
+ TRACE("ENTER LowLevelSaf__1unwrapDispatch");
+ if (!jsval) {
return JNI_FALSE;
+ }
- JSValue* val = (JSValue*)jsval;
- if (!val->isObject(&DispWrapper::info))
+ JSValue* val = reinterpret_cast<JSValue*>(jsval);
+ if (!val->isObject(&DispWrapper::info)) {
return JNI_FALSE;
+ }
DispWrapper* wrapper = static_cast<DispWrapper*>(val);
- env->SetObjectArrayElement(rval, 0, wrapper->getDispObj());
- if (env->ExceptionCheck())
- return JNI_FALSE;
+ env->SetObjectArrayElement(rval, 0, wrapper->getDispObj());
+ if (env->ExceptionCheck()) {
+ return JNI_FALSE;
+ }
- TRACE("SUCCESS LowLevelSaf__1unwrapDispatch");
- return JNI_TRUE;
+ TRACE("SUCCESS LowLevelSaf__1unwrapDispatch");
+ return JNI_TRUE;
}
/*
@@ -685,34 +812,27 @@
* Method: _wrapDispatch
* Signature: (Lcom/google/gwt/dev/shell/mac/LowLevelSaf/DispatchObject;[I)Z
*/
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1wrapDispatch
- (JNIEnv* env, jclass, jobject dispObj, jintArray rval) {
- TRACE("ENTER LowLevelSaf__1wrapDispatch");
-
- jobject dispObjRef = env->NewGlobalRef(dispObj);
- if (!dispObjRef || env->ExceptionCheck())
- return JNI_FALSE;
+JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1wrapDispatch
+ (JNIEnv* env, jclass, jobject dispObj, jintArray rval)
+{
+ TRACE("ENTER LowLevelSaf__1wrapDispatch");
+ jobject dispObjRef = env->NewGlobalRef(dispObj);
+ if (!dispObjRef || env->ExceptionCheck()) {
+ return JNI_FALSE;
+ }
DispWrapper* wrapper = new DispWrapper(dispObjRef);
- /*
- * TODO / LEAK / HACK: We will be persisting these objects on the java side,
- * so in order to keep the JS GC from collecting our objects when they are
- * away, we need to add them to the protect list. This should be refactored
- * out in favor of better memory mgmt scheme.
- */
- gcProtectNullTolerant(wrapper);
-#ifdef ENABLE_TRACING
- char buf[256];
- snprintf(buf, sizeof(buf), " return={%08x} ", unsigned(wrapper));
- PrintJSValue((JSValue*)wrapper, buf);
-#endif
- env->SetIntArrayRegion(rval, 0, 1, (jint*)&wrapper);
- if (env->ExceptionCheck())
- return JNI_FALSE;
+ gwtGCProtect(wrapper);
- TRACE("SUCCESS LowLevelSaf__1wrapDispatch");
- return JNI_TRUE;
+ env->SetIntArrayRegion(rval, 0, 1, (jint*)&wrapper);
+ if (env->ExceptionCheck()) {
+ return JNI_FALSE;
+ }
+
+ TRACE("SUCCESS LowLevelSaf__1wrapDispatch");
+ return JNI_TRUE;
}
/*
@@ -720,61 +840,54 @@
* Method: _wrapFunction
* Signature: (Ljava/lang/String;Lcom/google/gwt/dev/shell/mac/LowLevelSaf/DispatchMethod;[I)Z
*/
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1wrapFunction
- (JNIEnv* env, jclass, jstring name, jobject dispMeth, jintArray rval) {
- TRACE("ENTER LowLevelSaf__1wrapFunction");
+JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1wrapFunction
+ (JNIEnv* env, jclass, jstring name, jobject dispMeth, jintArray rval)
+{
+ TRACE("ENTER LowLevelSaf__1wrapFunction");
- jobject dispMethRef = env->NewGlobalRef(dispMeth);
- if (!dispMethRef || env->ExceptionCheck())
- return JNI_FALSE;
+ jobject dispMethRef = env->NewGlobalRef(dispMeth);
+ if (!dispMethRef || env->ExceptionCheck()) {
+ return JNI_FALSE;
+ }
JStringWrap jname(env, name);
- if (!jname.jstr())
+ if (!jname.jstr()) {
return JNI_FALSE;
+ }
- FuncWrapper* wrapper = new FuncWrapper(UString((const UChar*)jname.jstr(), jname.length()), dispMethRef);
- /*
- * TODO / LEAK / HACK: We will be persisting these objects on the java side,
- * so in order to keep the JS GC from collecting our objects when they are
- * away, we need to add them to the protect list. This should be refactored
- * out in favor of better memory mgmt scheme.
- */
- gcProtectNullTolerant(wrapper);
-#ifdef ENABLE_TRACING
- char buf[256];
- snprintf(buf, sizeof(buf), " return={%08x} ", unsigned(wrapper));
- PrintJSValue((JSValue*)wrapper, buf);
-#endif
- env->SetIntArrayRegion(rval, 0, 1, (jint*)&wrapper);
- if (env->ExceptionCheck())
- return JNI_FALSE;
+ FuncWrapper* wrapper = new FuncWrapper(UString((const UChar*)jname.jstr(),
+ jname.length()), dispMethRef);
- TRACE("SUCCESS LowLevelSaf__1wrapFunction");
- return JNI_TRUE;
+ gwtGCProtect(wrapper);
+ env->SetIntArrayRegion(rval, 0, 1, (jint*)&wrapper);
+ if (env->ExceptionCheck()) {
+ return JNI_FALSE;
+ }
+
+ TRACE("SUCCESS LowLevelSaf__1wrapFunction");
+ return JNI_TRUE;
}
#ifdef FILETRACE
FILE* gout = 0;
-void filetrace(const char* s)
-{
- fprintf(gout, s);
- fprintf(gout, "\n");
- fflush(gout);
+void filetrace(const char* s) {
+ fprintf(gout, s);
+ fprintf(gout, "\n");
+ fflush(gout);
}
#endif // FILETRACE
#ifdef JAVATRACE
jmethodID gTraceMethod = 0;
-void javatrace(const char* s)
-{
- if (!gEnv->ExceptionCheck())
- {
- jstring out = gEnv->NewStringUTF(s);
- if (!gEnv->ExceptionCheck())
- gEnv->CallStaticVoidMethod(gClass, gTraceMethod, out);
- else
- gEnv->ExceptionClear();
- }
+void javatrace(const char* s) {
+ if (!gEnv->ExceptionCheck()) {
+ jstring out = gEnv->NewStringUTF(s);
+ if (!gEnv->ExceptionCheck()) {
+ gEnv->CallStaticVoidMethod(gClass, gTraceMethod, out);
+ } else {
+ gEnv->ExceptionClear();
+ }
+ }
}
#endif // JAVATRACE
-
diff --git a/jni/mac/gwt-webkit.h b/jni/mac/gwt-webkit.h
index d235e06..ae1216a 100644
--- a/jni/mac/gwt-webkit.h
+++ b/jni/mac/gwt-webkit.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2006 Google Inc.
+ * Copyright 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
@@ -17,6 +17,7 @@
#define GWT_WEBKIT_H
#include <jni.h>
+#include <kjs/object.h>
#include "JStringWrap.h"
extern JNIEnv* gEnv;
@@ -31,7 +32,7 @@
//#define FILETRACE
//#define JAVATRACE
-#if define(FILETRACE) || defined(JAVATRACE)
+#if defined(FILETRACE) || defined(JAVATRACE)
#define ENABLE_TRACING
#endif
@@ -55,4 +56,35 @@
void javatrace(const char* s);
#endif // JAVATRACE
+void PrintJSValue(KJS::JSValue*, char* prefix="");
+void gcProtectHook(KJS::JSValue*);
+void gcUnprotectHook(KJS::JSValue*, const char* trace);
+
+/*
+ * Lock KJS GC and protect the supplied value.
+ *
+ * value JSValue* to protect from GC
+ */
+inline void gwtGCProtect(KJS::JSValue* value) {
+ KJS::JSLock lock;
+ gcProtectNullTolerant(value);
+#ifdef ENABLE_TRACING
+ gcProtectHook(value);
+#endif
+}
+
+/*
+ * Lock KJS GC and unprotect the supplied value.
+ *
+ * value JSValue* to unprotect from GC
+ * trace human-readable string describing object creation location
+ */
+inline void gwtGCUnprotect(KJS::JSValue* value, const char* trace) {
+ KJS::JSLock lock;
+#ifdef ENABLE_TRACING
+ gcUnprotectHook(value, trace);
+#endif
+ gcUnprotectNullTolerant(value);
+}
+
#endif
diff --git a/jni/mac/prebuilt/libgwt-webkit.jnilib b/jni/mac/prebuilt/libgwt-webkit.jnilib
index 328bcb3..6fe1a8d 100755
--- a/jni/mac/prebuilt/libgwt-webkit.jnilib
+++ b/jni/mac/prebuilt/libgwt-webkit.jnilib
Binary files differ