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