Fixes issues #472, #627, #736, #739

Refactor hosted mode code and rewrite Mozilla code to properly handle garbage
collection of JavaScript objects held as references within Java objects.  The
Mac changes simply adapt the existing Mac code to the new infrastructure, but
do not fix the outstanding memory leak problems -- these will be addressed in
a future patch.

Also added sample eclipse projects for the Mac and Linux JNI code.

Review by: scottb
           knorton


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@554 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/GWTShell.java b/dev/core/src/com/google/gwt/dev/GWTShell.java
index 334d742..e06dea2 100644
--- a/dev/core/src/com/google/gwt/dev/GWTShell.java
+++ b/dev/core/src/com/google/gwt/dev/GWTShell.java
@@ -232,9 +232,11 @@
     }
 
     /**
-     * @param moduleName
-     * @param logger
-     * @return
+     * Load a module.
+     * 
+     * @param moduleName name of the module to load
+     * @param logger TreeLogger to use
+     * @return the loaded module
      * @throws UnableToCompleteException
      */
     private ModuleDef loadModule(final String moduleName, TreeLogger logger)
@@ -580,11 +582,11 @@
    * specified constituent parts. This method is made to be overridden for
    * subclasses that need to change the behavior of ShellModuleSpaceHost.
    * 
-   * @param logger
+   * @param logger TreeLogger to use
    * @param typeOracle
    * @param moduleDef
    * @param genDir
-   * @return
+   * @return ShellModuleSpaceHost instance
    */
   protected ShellModuleSpaceHost doCreateShellModuleSpaceHost(
       TreeLogger logger, TypeOracle typeOracle, ModuleDef moduleDef, File genDir) {
@@ -628,7 +630,7 @@
   /**
    * By default we will open the application window.
    * 
-   * @return
+   * @return true if we are running in headless mode
    */
   protected boolean isHeadless() {
     return headlessMode;
diff --git a/dev/core/src/com/google/gwt/dev/shell/BrowserWidget.java b/dev/core/src/com/google/gwt/dev/shell/BrowserWidget.java
index 5224c1f..5bc3fbc 100644
--- a/dev/core/src/com/google/gwt/dev/shell/BrowserWidget.java
+++ b/dev/core/src/com/google/gwt/dev/shell/BrowserWidget.java
@@ -331,10 +331,7 @@
       Map.Entry entry = (Map.Entry) iter.next();
       String moduleName = (String) entry.getKey();
       ModuleSpace space = (ModuleSpace) entry.getValue();
-
-      space.dispose();
-      logger.log(TreeLogger.SPAM, "Cleaning up resources for module "
-          + moduleName, null);
+      unloadModule(space, moduleName);
     }
     moduleSpacesByName.clear();
 
@@ -344,6 +341,18 @@
       toolbar.openWebModeButton.setEnabled(false);
     }
   }
+  
+  /**
+   * Unload the specified module.
+   * 
+   * @param moduleSpace a ModuleSpace instance to unload.
+   * @param moduleName the name of the specified module
+   */
+  protected void unloadModule(ModuleSpace moduleSpace, String moduleName) {
+    moduleSpace.dispose();
+    logger.log(TreeLogger.SPAM, "Cleaning up resources for module "
+        + moduleName, null);
+  }
 
   private Composite buildLocationBar(Composite parent) {
     Color white = new Color(null, 255, 255, 255);
diff --git a/dev/core/src/com/google/gwt/dev/shell/Handle.java b/dev/core/src/com/google/gwt/dev/shell/Handle.java
deleted file mode 100644
index 1fb4871..0000000
--- a/dev/core/src/com/google/gwt/dev/shell/Handle.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright 2006 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.shell;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.util.Iterator;
-import java.util.Vector;
-
-/**
- * Abstracts the concept of an opaque handle to a JavaScript object.
- */
-public abstract class Handle {
-
-  public static final String HANDLE_CLASS = "com.google.gwt.core.client.JavaScriptObject";
-
-  /**
-   * For a thread-safety check to make sure only one thread ever accesses it.
-   */
-  private static Thread theOnlyThreadAllowed;
-
-  /**
-   * A queue of Integers (IUnknown ptrs, really), ready to be released by the
-   * main thread.
-   */
-  private static Vector toBeReleased = new Vector();
-  private static Object toBeReleasedLock = new Object();
-
-  private static Handle sImpl;
-
-  public static Object createHandle(Class type, int ptr) {
-    try {
-      checkThread();
-      Constructor ctor = type.getDeclaredConstructor(new Class[] {Integer.TYPE});
-      ctor.setAccessible(true);
-
-      Object handle = ctor.newInstance(new Object[] {new Integer(ptr)});
-      sImpl.lockPtr(ptr);
-      return handle;
-    } catch (InstantiationException e) {
-      throw new RuntimeException("Error creating handle", e);
-    } catch (IllegalAccessException e) {
-      throw new RuntimeException("Error creating handle", e);
-    } catch (SecurityException e) {
-      throw new RuntimeException("Error creating handle", e);
-    } catch (NoSuchMethodException e) {
-      throw new RuntimeException("Error creating handle", e);
-    } catch (IllegalArgumentException e) {
-      throw new RuntimeException("Error creating handle", e);
-    } catch (InvocationTargetException e) {
-      throw new RuntimeException("Error creating handle", e);
-    }
-  }
-
-  /**
-   * Moves this ptr into a queue of COM objects that are ready to be released.
-   */
-  public static void enqueuePtr(int opaque) {
-    // Add to the queue to be released by the main thread later.
-    //
-    Integer intOpaque = new Integer(opaque);
-    synchronized (toBeReleasedLock) {
-      toBeReleased.add(intOpaque);
-    }
-  }
-
-  public static int getPtrFromHandle(Object handle) {
-    try {
-      checkThread();
-
-      Class handleClass = handle.getClass();
-      while (handleClass != null && !handleClass.getName().equals(HANDLE_CLASS)) {
-        handleClass = handleClass.getSuperclass();
-      }
-
-      if (handleClass == null) {
-        throw new RuntimeException("Error reading handle");
-      }
-
-      Field opaqueField = handleClass.getDeclaredField("opaque");
-      opaqueField.setAccessible(true);
-      Integer opaque = (Integer) opaqueField.get(handle);
-      return opaque.intValue();
-    } catch (IllegalAccessException e) {
-      throw new RuntimeException("Error reading handle", e);
-    } catch (SecurityException e) {
-      throw new RuntimeException("Error reading handle", e);
-    } catch (NoSuchFieldException e) {
-      throw new RuntimeException("Error reading handle", e);
-    }
-  }
-
-  /**
-   * The main thread should call this from time to time to release external COM
-   * objects that Java is no longer referencing.
-   */
-  public static void releaseQueuedPtrs() {
-    checkThread();
-    Vector temp;
-    synchronized (toBeReleasedLock) {
-      temp = toBeReleased;
-      toBeReleased = new Vector();
-    }
-    for (Iterator iter = temp.iterator(); iter.hasNext();) {
-      Integer ptr = (Integer) iter.next();
-      sImpl.unlockPtr(ptr.intValue());
-    }
-    temp.clear();
-  }
-
-  /**
-   * Ensures that the current thread is actually the UI thread.
-   */
-  private static synchronized void checkThread() {
-    if (theOnlyThreadAllowed == null) {
-      theOnlyThreadAllowed = Thread.currentThread();
-    } else if (theOnlyThreadAllowed != Thread.currentThread()) {
-      throw new RuntimeException("This object has permanent thread affinity.");
-    }
-  }
-
-  protected Handle() {
-    if (sImpl != null) {
-      throw new RuntimeException("More than one Handle class!");
-    }
-    sImpl = this;
-  }
-
-  protected abstract void lockPtr(int ptr);
-
-  protected abstract void unlockPtr(int ptr);
-}
diff --git a/dev/core/src/com/google/gwt/dev/shell/HostedModeException.java b/dev/core/src/com/google/gwt/dev/shell/HostedModeException.java
new file mode 100644
index 0000000..2d3f03d
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/shell/HostedModeException.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2006 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.shell;
+
+/**
+ * An exception that can only occur in hosted mode, but may indicate potential
+ * problems in web mode.
+ */
+public class HostedModeException extends RuntimeException {
+
+  public HostedModeException(String message) {
+    super(message);
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/shell/JavaScriptHost.java b/dev/core/src/com/google/gwt/dev/shell/JavaScriptHost.java
index 2b703a7..76c2ba4 100644
--- a/dev/core/src/com/google/gwt/dev/shell/JavaScriptHost.java
+++ b/dev/core/src/com/google/gwt/dev/shell/JavaScriptHost.java
@@ -37,10 +37,6 @@
     sHost.createNative(file, line, jsniSignature, paramNames, js);
   }
 
-  public static void ditchHandle(int opaque) {
-    sHost.ditchHandle(opaque);
-  }
-
   public static void exceptionCaught(int number, String name, String description) {
     sHost.exceptionCaught(number, name, description);
   }
diff --git a/dev/core/src/com/google/gwt/dev/shell/JsValue.java b/dev/core/src/com/google/gwt/dev/shell/JsValue.java
new file mode 100644
index 0000000..bb65d38
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/shell/JsValue.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright 2006 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.shell;
+
+import java.util.Iterator;
+import java.util.Vector;
+
+/**
+ * Represents a JavaScript value.
+ * 
+ * Note that in general the various get*() methods will return
+ * platform-independent values only if the corresponding is*() method returns
+ * true.  In some cases, an IllegalStateException may be thrown if the
+ * JavaScript value is not of the appropriate type or bogus values may be
+ * returned.  Note that getString will try very hard to return a reasonable
+ * result for any value, but it is intended only for human consumption and the
+ * exact format for anything besides a string value cannot be relied upon.
+ */
+public abstract class JsValue {
+
+  /**
+   * Allows JsValue subclasses to clean themselves up.
+   */
+  protected interface JsCleanup {
+    void doCleanup();
+  }
+
+  /**
+   * For a thread-safety check to make sure only one thread ever accesses it.
+   */
+  private static Thread theOnlyThreadAllowed;
+
+  /**
+   * A queue of JsCleanup objects ready to be released by the main thread.
+   */
+  private static Vector toBeReleased = new Vector();
+
+  private static final Object toBeReleasedLock = new Object();
+
+  /**
+   * The main thread should call this from time to time to release hosted-mode
+   * objects that Java is no longer referencing.
+   */
+  public static void mainThreadCleanup() {
+    checkThread();
+    Vector temp;
+    synchronized (toBeReleasedLock) {
+      temp = toBeReleased;
+      toBeReleased = new Vector();
+    }
+    for (Iterator iter = temp.iterator(); iter.hasNext();) {
+      JsCleanup cleanup = (JsCleanup) iter.next();
+      cleanup.doCleanup();
+    }
+    temp.clear();
+  }
+
+  /**
+   * Ensures that the current thread is actually the UI thread.
+   */
+  private static synchronized void checkThread() {
+    if (theOnlyThreadAllowed == null) {
+      theOnlyThreadAllowed = Thread.currentThread();
+    } else if (theOnlyThreadAllowed != Thread.currentThread()) {
+      throw new RuntimeException("This object has permanent thread affinity.");
+    }
+  }
+
+  /**
+   * Moves this JS value to the queue of objects that are ready to be released.
+   */
+  private static void queueCleanup(JsCleanup cleanup) {
+    // Add to the queue to be released by the main thread later.
+    //
+    synchronized (toBeReleasedLock) {
+      toBeReleased.add(cleanup);
+    }
+  }
+
+  /**
+   * Get the value of the object as a boolean.  May attempt to convert the
+   * value to a boolean if it is not a boolean.
+   * 
+   * @return the value of the underlying object as a boolean
+   */
+  public abstract boolean getBoolean();
+
+  /**
+   * Get the value of the object as an integer. May attempt to convert the
+   * value to an integer if it is not an integer.
+   * 
+   * @return the value of the underlying object as an int
+   */
+  public abstract int getInt();
+
+  /**
+   * Get the value of the object as a double. May attempt to convert the
+   * value to a double if it is not a double.
+   * 
+   * @return the value of the underlying object as a double
+   */
+  public abstract double getNumber();
+
+  /**
+   * Get the value of the object as a string.  Tries very hard to return a
+   * reasonable value for any underyling type, but this should only be used
+   * for human consumption as the exact format is not reliable across platforms. 
+   * 
+   * @return the value of the underlying object as a string
+   */
+  public abstract String getString();
+
+  /**
+   * Returns a human-readable string describing the type of the JS object.
+   * This is intended only for human consumption and may vary across
+   * platforms.
+   */
+  public abstract String getTypeString();
+
+  /**
+   * Unwrap a wrapped Java object.
+   * 
+   * @return the original Java object wrapped in this JS object
+   */
+  public abstract Object getWrappedJavaObject();
+
+  /**
+   * Returns true if the JS value is a boolean.
+   */
+  public abstract boolean isBoolean();
+
+  /**
+   * Returns true if getInt() can be used on this value.
+   */
+  public abstract boolean isInt();
+
+  /**
+   * Returns true if the JS value is a native JS object.
+   */
+  public abstract boolean isJavaScriptObject();
+
+  /**
+   * Returns true if the JS value is null.
+   */
+  public abstract boolean isNull();
+
+  /**
+   * Returns true if the JS value is a numeric type.
+   */
+  public abstract boolean isNumber();
+
+  /**
+   * Returns true if the JS value is a string.
+   */
+  public abstract boolean isString();
+
+  /**
+   * Returns true if the JS value is undefined (void).
+   */
+  public abstract boolean isUndefined();
+
+  /**
+   * Returns true if the JS value is a wrapped Java object.
+   */
+  public abstract boolean isWrappedJavaObject();
+
+  /**
+   * Sets the JS object to be a boolean value.
+   * 
+   * @param val the boolean value to set
+   */
+  public abstract void setBoolean(boolean val);
+
+  /**
+   * Sets the JS object to be a number, passed as an byte.
+   * 
+   * @param val the value to store
+   */
+  public abstract void setByte(byte val);
+
+  /**
+   * Sets the JS object to be a number, passed as a char.
+   * 
+   * @param val the value to store
+   */
+  public abstract void setChar(char val);
+
+  /**
+   * Sets the JS object to be a number, passed as a double.
+   * 
+   * @param val the value to store
+   */
+  public abstract void setDouble(double val);
+
+  /**
+   * Sets the JS object to be a number, passed as an int.
+   * 
+   * @param val the value to store
+   */
+  public abstract void setInt(int val);
+
+  /**
+   * Set the JS object to be null.
+   * 
+   * @throws HostedModeException
+   */
+  public abstract void setNull();
+
+  /**
+   * Sets the JS object to be a number, passed as a short.
+   * 
+   * @param val the value to store
+   */
+  public abstract void setShort(short val);
+
+  /**
+   * Set the JS object to the supplied string.
+   * 
+   * @param val the string to put in the JS object
+   * @throws HostedModeException on JS allocation failures
+   */
+  public abstract void setString(String val);
+
+  /**
+   * Set the JS object to be undefined (void).
+   * 
+   * @throws HostedModeException on JS allocation failures
+   */
+  public abstract void setUndefined();
+
+  /**
+   * Make this JsValue refer to the same underlying object as another JsValue.
+   * 
+   * @param other JsValue to copy JS object from
+   */
+  public abstract void setValue(JsValue other);
+
+  /**
+   * Wrap a Java method as a JavaScript function pointer.
+   * 
+   * @param string the name of the method
+   * @param dispMethod the DispatchMethod object describing the method tow wrap
+   */
+  // TODO(jat): platform-independent version of this?
+  // The problem is that each platform has different conventions for passing
+  // JavaScript values back into a Java method.
+  // public abstract void setWrappedFunction(String string,
+  // DispatchMethod dispMethod);
+  /**
+   * Set the JS object to the supplied object, which will be wrapped in a
+   * platform-dependent JavaScript class.
+   * 
+   * @param cl the classloader to create the wrapper object with
+   * @param val the Java object to wrap
+   * @throws HostedModeException
+   */
+  public abstract void setWrappedJavaObject(CompilingClassLoader cl, Object val);
+
+  /**
+   * Produce a string representation of the JsValue.
+   */
+  public String toString() {
+    if (isUndefined()) {
+      return "void";
+    } else if (isNull()) {
+      return "null";
+    } else if (isBoolean()) {
+      return "bool: " + (getBoolean() ? "true" : "false");
+    } else if (isInt()) {
+      return "int: " + Integer.toString(getInt());
+    } else if (isNumber()) {
+      return "double: " + Double.toString(getNumber());
+    } else if (isWrappedJavaObject()) {
+      return "Java object: " + getWrappedJavaObject().toString();
+    } else if (isJavaScriptObject()) {
+      return "JS object [" + getTypeString() + "] : " + getString();
+    } else if (isString()) {
+      return "string: '" + getString() + "'";
+    } else {
+      return "*unknown type: " + getTypeString() + "*";
+    }
+  }
+
+  /**
+   * Create an object which frees the underlying JS resource.
+   * 
+   * @return a JsCleanup object which will free the underlying JS resource
+   */
+  protected abstract JsCleanup createCleanupObject();
+
+  /**
+   * When the Java object is garbage-collected, make sure the associated JS
+   * resource is freed. A helper object is used to avoid issues with
+   * resurrecting this object.
+   */
+  protected final void finalize() throws Throwable {
+    queueCleanup(createCleanupObject());
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/shell/JsValueGlue.java b/dev/core/src/com/google/gwt/dev/shell/JsValueGlue.java
new file mode 100644
index 0000000..4fcf2c8
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/shell/JsValueGlue.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright 2006 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.shell;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.dev.util.TypeInfo;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Glue layer that performs GWT-specific operations on JsValues. Used to isolate
+ * HostedModeExceptions/etc from JsValue code
+ */
+public class JsValueGlue {
+  public static final String JSO_CLASS = "com.google.gwt.core.client.JavaScriptObject";
+
+  /**
+   * Create a JavaScriptObject instance referring to this JavaScript object.
+   * 
+   * The caller is responsible for ensuring that the requested type is a
+   * subclass of JavaScriptObject.
+   * 
+   * @param type The subclass of JavaScriptObject to create
+   * @return the constructed JavaScriptObject
+   */
+  public static Object createJavaScriptObject(JsValue value, Class type) {
+    try {
+      // checkThread();
+      if (!value.isJavaScriptObject()) {
+        throw new RuntimeException(
+            "Only Object type JavaScript objects can be made into JavaScriptObject");
+      }
+
+      /* find the JavaScriptObject type, while verifying this is a subclass */
+      Class jsoType = getJavaScriptObjectSuperclass(type);
+      if (jsoType == null) {
+        throw new RuntimeException("Requested type " + type.getName()
+            + " not a subclass of JavaScriptObject");
+      }
+
+      /* create the object using the default constructor */
+      Constructor ctor = type.getDeclaredConstructor(new Class[] {});
+      ctor.setAccessible(true);
+      Object jso = ctor.newInstance(new Object[] {});
+
+      /* set the hostedModeReference field to this JsValue using reflection */
+      Field referenceField = jsoType.getDeclaredField("hostedModeReference");
+      referenceField.setAccessible(true);
+      referenceField.set(jso, value);
+      return jso;
+    } catch (InstantiationException e) {
+      throw new RuntimeException("Error creating JavaScript object", e);
+    } catch (IllegalAccessException e) {
+      throw new RuntimeException("Error creating JavaScript object", e);
+    } catch (SecurityException e) {
+      throw new RuntimeException("Error creating JavaScript object", e);
+    } catch (NoSuchFieldException e) {
+      throw new RuntimeException("Error creating JavaScript object", e);
+    } catch (NoSuchMethodException e) {
+      throw new RuntimeException("Error creating JavaScript object", e);
+    } catch (IllegalArgumentException e) {
+      throw new RuntimeException("Error creating JavaScript object", e);
+    } catch (InvocationTargetException e) {
+      throw new RuntimeException("Error creating JavaScript object", e);
+    }
+  }
+
+  /**
+   * Return an object containing the value JavaScript object as a specified
+   * type.
+   * 
+   * @param value the JavaScript value
+   * @param type expected type of the returned object
+   * @param msgPrefix a prefix for error/warning messages
+   * @return the object reference
+   * @throws HostedModeException if the JavaScript object is not assignable to
+   *           the supplied type.
+   */
+  public static Object get(JsValue value, Class type, String msgPrefix) {
+    double doubleVal;
+    if (value.isNull()) {
+      return null;
+    }
+    if (value.isUndefined()) {
+      // undefined is never legal to return from JavaScript into Java
+      throw new HostedModeException(msgPrefix
+          + ": JavaScript undefined, expected " + type.getName());
+   }
+    if (value.isWrappedJavaObject()) {
+      Object origObject = value.getWrappedJavaObject();
+      if (!type.isAssignableFrom(origObject.getClass())) {
+        throw new HostedModeException(msgPrefix + ": Java object of type "
+            + origObject.getClass().getName() + ", expected " + type.getName());
+      }
+      return origObject;
+    }
+    if (getJavaScriptObjectSuperclass(type) != null) {
+      if (!value.isJavaScriptObject()) {
+        throw new HostedModeException(msgPrefix + ": JS object of type "
+            + value.getTypeString() + ", expected " + type.getName());
+      }
+      return createJavaScriptObject(value, type);
+    }
+    switch (TypeInfo.classifyType(type)) {
+      case TypeInfo.TYPE_WRAP_BOOLEAN:
+      case TypeInfo.TYPE_PRIM_BOOLEAN:
+        if (!value.isBoolean()) {
+          throw new HostedModeException(msgPrefix + ": JS value of type "
+              + value.getTypeString() + ", expected boolean");
+        }
+        return Boolean.valueOf(value.getBoolean());
+
+      case TypeInfo.TYPE_WRAP_BYTE:
+      case TypeInfo.TYPE_PRIM_BYTE:
+        return new Byte((byte) getIntRange(value, Byte.MIN_VALUE,
+            Byte.MAX_VALUE, "byte", msgPrefix));
+
+      case TypeInfo.TYPE_WRAP_CHAR:
+      case TypeInfo.TYPE_PRIM_CHAR:
+        return new Character((char) getIntRange(value, Character.MIN_VALUE,
+            Character.MAX_VALUE, "char", msgPrefix));
+
+      case TypeInfo.TYPE_WRAP_DOUBLE:
+      case TypeInfo.TYPE_PRIM_DOUBLE:
+        if (!value.isNumber()) {
+          throw new HostedModeException(msgPrefix + ": JS value of type "
+              + value.getTypeString() + ", expected double");
+        }
+        return new Double(value.getNumber());
+
+      case TypeInfo.TYPE_WRAP_FLOAT:
+      case TypeInfo.TYPE_PRIM_FLOAT:
+        if (!value.isNumber()) {
+          throw new HostedModeException(msgPrefix + ": JS value of type "
+              + value.getTypeString() + ", expected float");
+        }
+        doubleVal = value.getNumber();
+        if (doubleVal < Float.MIN_VALUE || doubleVal > Float.MAX_VALUE) {
+          throw new HostedModeException(msgPrefix + ": JS value " + doubleVal
+              + " out of range for a float");
+        }
+        return new Float((float) doubleVal);
+
+      case TypeInfo.TYPE_WRAP_INT:
+      case TypeInfo.TYPE_PRIM_INT:
+        return new Integer(getIntRange(value, Integer.MIN_VALUE,
+            Integer.MAX_VALUE, "int", msgPrefix));
+
+      case TypeInfo.TYPE_WRAP_LONG:
+      case TypeInfo.TYPE_PRIM_LONG:
+        if (!value.isNumber()) {
+          throw new HostedModeException(msgPrefix + ": JS value of type "
+              + value.getTypeString() + ", expected long");
+        }
+        doubleVal = value.getNumber();
+        if (doubleVal < Long.MIN_VALUE || doubleVal > Long.MAX_VALUE) {
+          throw new HostedModeException(msgPrefix + ": JS double value " + doubleVal
+              + " out of range for a long");
+        }
+        // TODO(jat): can this actually detect loss of precision?
+        long longVal = (long) doubleVal;
+        if (doubleVal != longVal) {
+          // TODO(jat): should this be an error or exception?
+          ModuleSpace.getLogger().log(TreeLogger.WARN, msgPrefix
+              + ": Loss of precision converting double to long", null);
+        }
+        return new Long(longVal);
+
+      case TypeInfo.TYPE_WRAP_SHORT:
+      case TypeInfo.TYPE_PRIM_SHORT:
+        return new Short((short) getIntRange(value, Short.MIN_VALUE,
+            Short.MAX_VALUE, "short", msgPrefix));
+
+      case TypeInfo.TYPE_WRAP_STRING:
+        if (!value.isString()) {
+          throw new HostedModeException(msgPrefix + ": JS value of type "
+              + value.getTypeString() + ", expected string");
+        }
+        return value.getString();
+
+      case TypeInfo.TYPE_USER:
+        if (value.isString()) {
+          return value.getString();
+        }
+        // if it isn't a String, it's an error, break to error
+        break;
+    }
+
+    // Just don't know what do to with this.
+    throw new IllegalArgumentException(msgPrefix + ": Cannot convert to type "
+        + TypeInfo.getSourceRepresentation(type, "") + " from "
+        + value.getTypeString());
+  }
+
+  /**
+   * Returns the underlying JsValue from a JavaScriptObject instance.
+   * 
+   * The tricky part is that it is in a different classloader so therefore can't
+   * be specified directly. The type is specified as Object, and reflection is
+   * used to retrieve the hostedModeReference field.
+   * 
+   * @param jso the instance of JavaScriptObject to retrieve the JsValue from.
+   * @return the JsValue representing the JavaScript object
+   */
+  public static JsValue getUnderlyingObject(Object jso) {
+    try {
+      /*
+       * verify that jso is assignable to
+       * com.google.gwt.core.client.JavaScriptObject
+       */
+      Class type = getJavaScriptObjectSuperclass(jso.getClass());
+
+      if (type == null) {
+        throw new HostedModeException(
+            "Underlying JSO not a subclass of JavaScriptObject");
+      }
+
+      Field referenceField = type.getDeclaredField("hostedModeReference");
+      referenceField.setAccessible(true);
+      return (JsValue) referenceField.get(jso);
+    } catch (IllegalAccessException e) {
+      throw new RuntimeException("Error reading handle", e);
+    } catch (SecurityException e) {
+      throw new RuntimeException("Error reading handle", e);
+    } catch (NoSuchFieldException e) {
+      throw new RuntimeException("Error reading handle", e);
+    }
+  }
+
+  /**
+   * Set the underlying value.
+   * 
+   * @param value JsValue to set
+   * @param type static type of the object
+   * @param obj the object to store in the JS value
+   */
+  public static void set(JsValue value, CompilingClassLoader cl, Class type,
+      Object obj) {
+    if (obj == null) {
+      value.setNull();
+    } else if (type.equals(String.class)) {
+      value.setString((String) obj);
+    } else if (type.equals(boolean.class)) {
+      value.setBoolean(((Boolean) obj).booleanValue());
+    } else if (type.equals(short.class)) {
+      value.setInt(((Short) obj).shortValue());
+    } else if (type.equals(int.class)) {
+      value.setInt(((Integer) obj).intValue());
+    } else if (type.equals(byte.class)) {
+      value.setInt(((Byte) obj).byteValue());
+    } else if (type.equals(char.class)) {
+      value.setInt(((Character) obj).charValue());
+    } else if (type.equals(long.class)) {
+      long longVal = ((Long) obj).longValue();
+      double doubleVal = longVal;
+      if ((long) doubleVal != longVal) {
+        // TODO(jat): should this be an error or exception?
+        ModuleSpace.getLogger().log(TreeLogger.WARN,
+            "Loss of precision converting long to double", null);
+      }
+      value.setDouble(doubleVal);
+    } else if (type.equals(float.class)) {
+      value.setDouble(((Float) obj).floatValue());
+    } else if (type.equals(double.class)) {
+      value.setDouble(((Double) obj).doubleValue());
+    } else {
+      // not a boxed primitive
+      try {
+        Class jso = Class.forName(JSO_CLASS, true, cl);
+        if (jso.isAssignableFrom(type) && jso.isAssignableFrom(obj.getClass())) {
+          JsValue jsObject = getUnderlyingObject(obj);
+          value.setValue(jsObject);
+          return;
+        }
+      } catch (ClassNotFoundException e) {
+        // Ignore the exception, if we can't find the class then obviously we
+        // don't have to worry about o being one
+      }
+
+      // Fallthrough case: Object.
+      if (!type.isAssignableFrom(obj.getClass())) {
+          throw new HostedModeException("object is of type "
+              + obj.getClass().getName() + ", expected " + type.getName());
+        }
+      value.setWrappedJavaObject(cl, obj);
+    }
+  }
+
+  private static int getIntRange(JsValue value, int low, int high,
+      String typeName, String msgPrefix) {
+    int intVal;
+    if (value.isInt()) {
+      intVal = value.getInt();
+      if (intVal < low || intVal > high) {
+        throw new HostedModeException(msgPrefix + ": JS int value " + intVal
+            + " out of range for a " + typeName);
+      }
+    } else if (value.isNumber()) {
+      double doubleVal = value.getNumber();
+      if (doubleVal < low || doubleVal > high) {
+        throw new HostedModeException(msgPrefix + ": JS double value "
+            + doubleVal + " out of range for a " + typeName);
+      }
+      intVal = (int) doubleVal;
+      if (intVal != doubleVal) {
+        ModuleSpace.getLogger().log(TreeLogger.WARN, msgPrefix
+            + ": Rounding double to int for " + typeName, null);
+      }
+    } else {
+      throw new HostedModeException(msgPrefix + ": JS value of type "
+          + value.getTypeString() + ", expected " + typeName);
+    }
+    return intVal;
+  }
+
+  /**
+   * Verify that the supplied class is a subclass of
+   * com.google.gwt.core.client.JavaScriptObject, and return the
+   * JavaScriptObject class if it is. This is required since JavaScriptObject
+   * actually lives in a different classloader and can't be referenced directly.
+   * 
+   * @param type class to test
+   * @return the JavaScriptObject class object if it is a subclass, or null if
+   *         not.
+   */
+  private static Class getJavaScriptObjectSuperclass(Class type) {
+    while (type != null && !type.getName().equals(JSO_CLASS)) {
+      type = type.getSuperclass();
+    }
+    return type;
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/shell/JsniInjector.java b/dev/core/src/com/google/gwt/dev/shell/JsniInjector.java
index 5c0ba42..efaa27e 100644
--- a/dev/core/src/com/google/gwt/dev/shell/JsniInjector.java
+++ b/dev/core/src/com/google/gwt/dev/shell/JsniInjector.java
@@ -213,7 +213,10 @@
    * Create a legal Java method call that will result in a JSNI invocation.
    * 
    * @param method
-   * @return
+   * @param expectedHeaderLines
+   * @param expectedBodyLines
+   * @return a String of the Java code to call a JSNI method, using
+   *     JavaScriptHost.invokeNative*
    */
   private String genNonNativeVersionOfJsniMethod(JMethod method,
       int expectedHeaderLines, int expectedBodyLines) {
@@ -295,12 +298,13 @@
     sb.append(Jsni.buildTypeList(method));
     sb.append(',');
 
-    // Build an array containing the arguments based on the names of the params.
+    // Build an array containing the arguments based on the names of the
+    // parameters.
     //
     sb.append(Jsni.buildArgList(method));
     sb.append(");}");
 
-    // Add extra lines at the end to match JSNI body
+    // Add extra lines at the end to match JSNI body.
     //
     for (int i = 0; i < expectedBodyLines; ++i) {
       sb.append('\n');
@@ -415,7 +419,7 @@
         branch.log(TreeLogger.SPAM, patched[i].getReadableDeclaration(), null);
       }
 
-      // Insert an initializer block immediatley after the opening brace of the
+      // Insert an initializer block immediately after the opening brace of the
       // class.
       //
       char[] block = genInitializerBlock(loc, source, patched);
diff --git a/dev/core/src/com/google/gwt/dev/shell/LowLevel.java b/dev/core/src/com/google/gwt/dev/shell/LowLevel.java
index d57f565..e27adad 100644
--- a/dev/core/src/com/google/gwt/dev/shell/LowLevel.java
+++ b/dev/core/src/com/google/gwt/dev/shell/LowLevel.java
@@ -99,8 +99,11 @@
         System.load(installPath + '/' + System.mapLibraryName(libName));
       } catch (UnsatisfiedLinkError e) {
         StringBuffer sb = new StringBuffer();
-        sb.append("Unable to load required native library '" + libName + "'");
-        sb.append("\n\tYour GWT installation may be corrupt");
+        sb.append("Unable to load required native library '" + libName
+            + "'.  Detailed error:\n");
+        sb.append(e.getMessage() + ")\n\n");
+        sb.append("Your GWT installation may be corrupt");
+        System.err.println(sb.toString());
         throw new UnsatisfiedLinkError(sb.toString());
       }
       sInitialized = true;
diff --git a/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java b/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java
index 75dc42a..9263f0b 100644
--- a/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java
+++ b/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java
@@ -31,6 +31,7 @@
 public abstract class ModuleSpace implements ShellJavaScriptHost {
 
   protected static ThreadLocal sCaughtJavaExceptionObject = new ThreadLocal();
+
   protected static ThreadLocal sThrownJavaExceptionObject = new ThreadLocal();
 
   /**
@@ -50,8 +51,8 @@
       Class javaScriptExceptionClass = Class.forName(
           "com.google.gwt.core.client.JavaScriptException", true, cl);
       Class string = String.class;
-      Constructor ctor = javaScriptExceptionClass.getDeclaredConstructor(new Class[] {
-          string, string});
+      Constructor ctor = javaScriptExceptionClass.getDeclaredConstructor(
+          new Class[] {string, string});
       return (RuntimeException) ctor.newInstance(new Object[] {name, desc});
     } catch (InstantiationException e) {
       caught = e;
@@ -92,8 +93,102 @@
     host.getClassLoader().clear();
   }
 
-  public void ditchHandle(int opaque) {
-    Handle.enqueuePtr(opaque);
+  public boolean invokeNativeBoolean(String name, Object jthis, Class[] types,
+      Object[] args) {
+    JsValue result = invokeNative(name, jthis, types, args);
+    Boolean value = (Boolean)JsValueGlue.get(result, Boolean.class,
+        "invokeNativeBoolean(" + name + ")");
+    return value.booleanValue();
+  }
+
+  public byte invokeNativeByte(String name, Object jthis, Class[] types,
+      Object[] args) {
+    JsValue result = invokeNative(name, jthis, types, args);
+    Byte value = (Byte)JsValueGlue.get(result, Byte.class,
+        "invokeNativeByte(" + name + ")");
+    return value.byteValue();
+  }
+
+  public char invokeNativeChar(String name, Object jthis, Class[] types,
+      Object[] args) {
+    JsValue result = invokeNative(name, jthis, types, args);
+    Character value = (Character)JsValueGlue.get(result, Character.class,
+        "invokeNativeCharacter(" + name + ")");
+    return value.charValue();
+  }
+
+  public double invokeNativeDouble(String name, Object jthis, Class[] types,
+      Object[] args) {
+    JsValue result = invokeNative(name, jthis, types, args);
+    Double value = (Double)JsValueGlue.get(result, Double.class,
+        "invokeNativeDouble(" + name + ")");
+    return value.doubleValue();
+  }
+
+  public float invokeNativeFloat(String name, Object jthis, Class[] types,
+      Object[] args) {
+    JsValue result = invokeNative(name, jthis, types, args);
+    Float value = (Float)JsValueGlue.get(result, Float.class,
+        "invokeNativeFloat(" + name + ")");
+    return value.floatValue();
+  }
+
+  public Object invokeNativeHandle(String name, Object jthis, Class returnType,
+      Class[] types, Object[] args) {
+
+    JsValue result = invokeNative(name, jthis, types, args);
+    return JsValueGlue.get(result, returnType,
+        "invokeNativeHandle(" + name + ")");
+  }
+
+  public int invokeNativeInt(String name, Object jthis, Class[] types,
+      Object[] args) {
+    JsValue result = invokeNative(name, jthis, types, args);
+    Integer value = (Integer)JsValueGlue.get(result, Integer.class,
+        "invokeNativeInteger(" + name + ")");
+    return value.intValue();
+  }
+
+  public long invokeNativeLong(String name, Object jthis, Class[] types,
+      Object[] args) {
+    JsValue result = invokeNative(name, jthis, types, args);
+    Long value = (Long)JsValueGlue.get(result, Long.class,
+        "invokeNativeLong(" + name + ")");
+    return value.longValue();
+  }
+
+  public Object invokeNativeObject(String name, Object jthis, Class[] types,
+      Object[] args) {
+    JsValue result = invokeNative(name, jthis, types, args);
+    return JsValueGlue.get(result, Object.class, "invokeNativeObject("
+        + name + ")");
+  }
+
+  public short invokeNativeShort(String name, Object jthis, Class[] types,
+      Object[] args) {
+    JsValue result = invokeNative(name, jthis, types, args);
+    Short value = (Short)JsValueGlue.get(result, Short.class,
+        "invokeNativeShort(" + name + ")");
+    return value.shortValue();
+  }
+
+  public String invokeNativeString(String name, Object jthis, Class[] types,
+      Object[] args) {
+    JsValue result = invokeNative(name, jthis, types, args);
+    return (String)JsValueGlue.get(result, String.class,
+        "invokeNativeString(" + name + ")");
+  }
+
+  public void invokeNativeVoid(String name, Object jthis, Class[] types,
+      Object[] args) {
+    JsValue result = invokeNative(name, jthis, types, args);
+    if (!result.isUndefined()) {
+      getLogger().log(
+          TreeLogger.WARN,
+          "JSNI method '" + name + "' returned a value of type "
+              + result.getTypeString() + "; should not have returned a value",
+          null);
+    }
   }
 
   /**
@@ -238,6 +333,18 @@
     return newScript;
   }
 
+  /**
+   * Invokes a native JavaScript function.
+   * 
+   * @param name the name of the function to invoke
+   * @param jthis the function's 'this' context
+   * @param types the type of each argument
+   * @param args the arguments to be passed
+   * @return the return value as a Variant.
+   */
+  protected abstract JsValue doInvoke(String name, Object jthis, Class[] types,
+      Object[] args);
+
   protected CompilingClassLoader getIsolatedClassLoader() {
     return host.getClassLoader();
   }
@@ -247,6 +354,22 @@
    */
   protected abstract void initializeStaticDispatcher();
 
+  /**
+   * Invokes a native JavaScript function.
+   * 
+   * @param name the name of the function to invoke
+   * @param jthis the function's 'this' context
+   * @param types the type of each argument
+   * @param args the arguments to be passed
+   * @return the return value as a Variant.
+   */
+  protected final JsValue invokeNative(String name, Object jthis,
+      Class[] types, Object[] args) {
+    // Whenever a native method is invoked, release any enqueued cleanup objects
+    JsValue.mainThreadCleanup();
+    return doInvoke(name, jthis, types, args);
+  }
+
   protected boolean isExceptionActive() {
     return sCaughtJavaExceptionObject.get() != null;
   }
@@ -362,4 +485,5 @@
     }
     throw new RuntimeException("Error intializing JavaScriptHost", caught);
   }
+
 }
diff --git a/dev/core/src/com/google/gwt/dev/shell/ShellJavaScriptHost.java b/dev/core/src/com/google/gwt/dev/shell/ShellJavaScriptHost.java
index ebea2e4..858a1dc 100644
--- a/dev/core/src/com/google/gwt/dev/shell/ShellJavaScriptHost.java
+++ b/dev/core/src/com/google/gwt/dev/shell/ShellJavaScriptHost.java
@@ -40,13 +40,6 @@
       String[] paramNames, String js);
 
   /**
-   * Releases a handle.
-   * 
-   * @param opaque the handle to be released
-   */
-  abstract void ditchHandle(int opaque);
-
-  /**
    * Call this when a JavaScript exception is caught.
    */
   abstract void exceptionCaught(int number, String name, String description);
diff --git a/dev/linux/src/com/google/gwt/dev/shell/moz/BrowserWidgetMoz.java b/dev/linux/src/com/google/gwt/dev/shell/moz/BrowserWidgetMoz.java
index 3226535..1ce9b3e 100644
--- a/dev/linux/src/com/google/gwt/dev/shell/moz/BrowserWidgetMoz.java
+++ b/dev/linux/src/com/google/gwt/dev/shell/moz/BrowserWidgetMoz.java
@@ -29,6 +29,9 @@
 import org.eclipse.swt.internal.mozilla.nsIWebBrowser;
 import org.eclipse.swt.widgets.Shell;
 
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * Represents an individual browser window and all of its controls.
  */
@@ -36,12 +39,15 @@
 
   private class ExternalObjectImpl implements ExternalObject {
 
+    private Map modulesByScriptObject = new HashMap();
+    private Map namesByScriptObject = new HashMap();
+
     public boolean gwtOnLoad(int scriptObject, String moduleName) {
       try {
         if (moduleName == null) {
-          // Indicates the page is being unloaded.
+          // Indicates one or more modules are being unloaded.
           //
-          onPageUnload();
+          handleUnload(scriptObject);
           return true;
         }
 
@@ -51,10 +57,12 @@
             BrowserWidgetMoz.this, moduleName);
         ModuleSpace moduleSpace = new ModuleSpaceMoz(msh, scriptObject);
         attachModuleSpace(moduleName, moduleSpace);
+        modulesByScriptObject.put(new Integer(scriptObject), moduleSpace);
+        namesByScriptObject.put(new Integer(scriptObject), moduleName);
         return true;
       } catch (Throwable e) {
         // We do catch Throwable intentionally because there are a ton of things
-        // that can go wrong trying to load a module, including Error-dervied
+        // that can go wrong trying to load a module, including Error-derived
         // things like NoClassDefFoundError.
         // 
         getHost().getLogger().log(TreeLogger.ERROR,
@@ -63,8 +71,32 @@
       }
     }
 
-    public int resolveReference(String ident) {
-      return LowLevelMoz.JSVAL_VOID;
+    /**
+     * Unload one or more modules.
+     * 
+     * TODO(jat): Note that currently the JS code does not unload individual
+     * modules, so this change is in preparation for when the JS code is
+     * fixed. 
+     * 
+     * @param scriptObject window to unload, 0 if all
+     */
+    protected void handleUnload(int scriptObject) {
+      // TODO(jat): true below restores original behavior of always
+      // unloading all modules until the resulting GC issues (the
+      // order of destruction is undefined so JS_RemoveRoot gets
+      // called on a non-existent JSContext)
+      if (true || scriptObject == 0) {
+        onPageUnload();
+        modulesByScriptObject.clear();
+        namesByScriptObject.clear();
+        return;
+      }
+      Integer key = new Integer(scriptObject);
+      ModuleSpace moduleSpace = (ModuleSpace)modulesByScriptObject.get(key);
+      String moduleName = (String)namesByScriptObject.get(key);
+      unloadModule(moduleSpace, moduleName);
+      modulesByScriptObject.remove(key);
+      namesByScriptObject.remove(key);
     }
   }
 
diff --git a/dev/linux/src/com/google/gwt/dev/shell/moz/GeckoDispatchAdapter.java b/dev/linux/src/com/google/gwt/dev/shell/moz/GeckoDispatchAdapter.java
index 83151ec..54fb699 100644
--- a/dev/linux/src/com/google/gwt/dev/shell/moz/GeckoDispatchAdapter.java
+++ b/dev/linux/src/com/google/gwt/dev/shell/moz/GeckoDispatchAdapter.java
@@ -18,6 +18,8 @@
 import com.google.gwt.dev.shell.CompilingClassLoader;
 import com.google.gwt.dev.shell.JavaDispatch;
 import com.google.gwt.dev.shell.JavaDispatchImpl;
+import com.google.gwt.dev.shell.JsValue;
+import com.google.gwt.dev.shell.JsValueGlue;
 import com.google.gwt.dev.shell.moz.LowLevelMoz.DispatchMethod;
 import com.google.gwt.dev.shell.moz.LowLevelMoz.DispatchObject;
 
@@ -37,53 +39,61 @@
 
   private final JavaDispatch javaDispatch;
 
-  private final int scriptObject;
-
   /**
    * This constructor initializes as the static dispatcher, which handles only
    * static method calls and field references.
    * 
    * @param cl this class's classLoader
-   * @param aScriptObject the execution iframe's window
    */
-  GeckoDispatchAdapter(CompilingClassLoader cl, int aScriptObject) {
+  GeckoDispatchAdapter(CompilingClassLoader cl) {
     javaDispatch = new JavaDispatchImpl(cl);
     classLoader = cl;
-    scriptObject = aScriptObject;
   }
 
   /**
    * This constructor initializes a dispatcher, around a particular instance.
    * 
    * @param cl this class's classLoader
-   * @param aScriptObject the execution iframe's window
    * @param target the object being wrapped as an IDispatch
    */
-  GeckoDispatchAdapter(CompilingClassLoader cl, int aScriptObject, Object target) {
+  GeckoDispatchAdapter(CompilingClassLoader cl, Object target) {
     javaDispatch = new JavaDispatchImpl(cl, target);
     classLoader = cl;
-    scriptObject = aScriptObject;
   }
 
-  public int getField(String name) {
+  /**
+   * Retrieve a field and store in the passed JsValue.
+   * This function is called exclusively from native code.
+   * 
+   * @param name name of the field to retrieve
+   * @param jsValue a reference to the JsValue object to receive the value of the field
+   */
+  public void getField(String name, int jsRootedValue) {
+    JsValueMoz jsValue = new JsValueMoz(jsRootedValue);
+    // TODO(jat): factor out remaining code into platform-independent code
     int dispId = classLoader.getDispId(name);
     if (dispId < 0) {
-      return LowLevelMoz.JSVAL_VOID;
+      // no field by that name, return undefined
+      jsValue.setUndefined();
+      return;
     }
     if (javaDispatch.isField(dispId)) {
       Field field = javaDispatch.getField(dispId);
-      return SwtGeckoGlue.convertObjectToJSVal(scriptObject, classLoader,
-          field.getType(), javaDispatch.getFieldValue(dispId));
+      JsValueGlue.set(jsValue, classLoader, field.getType(),
+          javaDispatch.getFieldValue(dispId));
+      return;
     } else {
       Method method = javaDispatch.getMethod(dispId);
       DispatchMethod dispMethod;
       dispMethod = (DispatchMethod) classLoader.getMethodDispatch(method);
       if (dispMethod == null) {
-        dispMethod = new MethodDispatch(classLoader, method, scriptObject);
+        dispMethod = new MethodDispatch(classLoader, method);
         classLoader.putMethodDispatch(method, dispMethod);
       }
-      return LowLevelMoz.wrapFunction(scriptObject, method.toString(),
-          dispMethod);
+      jsValue.setWrappedFunction(method.toString(), dispMethod);
+//      LowLevelMoz.wrapFunction(scriptObject, method.toString(),
+//          dispMethod, (JsValueMoz)jsValue);
+      return;
     }
   }
 
@@ -91,9 +101,11 @@
     return javaDispatch.getTarget();
   }
 
-  public void setField(String name, int value) {
+  public void setField(String name, int jsRootedValue) {
+    JsValue jsValue = new JsValueMoz(jsRootedValue);
     int dispId = classLoader.getDispId(name);
     if (dispId < 0) {
+      // no field by that name
       // TODO: expandos?
       throw new RuntimeException("No such field " + name);
     }
@@ -101,8 +113,7 @@
       throw new RuntimeException("Cannot reassign method " + name);
     }
     Field field = javaDispatch.getField(dispId);
-    Object val = SwtGeckoGlue.convertJSValToObject(scriptObject,
-        field.getType(), value);
+    Object val = JsValueGlue.get(jsValue, field.getType(), "setField");
     javaDispatch.setFieldValue(dispId, val);
   }
 }
diff --git a/dev/linux/src/com/google/gwt/dev/shell/moz/HandleMoz.java b/dev/linux/src/com/google/gwt/dev/shell/moz/HandleMoz.java
deleted file mode 100644
index c5bc609..0000000
--- a/dev/linux/src/com/google/gwt/dev/shell/moz/HandleMoz.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2006 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.shell.moz;
-
-import com.google.gwt.dev.shell.Handle;
-
-class HandleMoz extends Handle {
-
-  static {
-    // put myself in Handle's sImpl field
-    new HandleMoz();
-  }
-
-  public static Object createHandle(Class type, int ptr) {
-    return Handle.createHandle(type, ptr);
-  }
-
-  public static int getJSObjectFromHandle(Object o) {
-    return LowLevelMoz.unwrapJSObject(getPtrFromHandle(o));
-  }
-
-  /**
-   * Not instantiable.
-   */
-  private HandleMoz() {
-  }
-
-  protected void lockPtr(int ptr) {
-    SwtGeckoGlue.addRefInt(ptr);
-  }
-
-  protected void unlockPtr(int ptr) {
-    SwtGeckoGlue.releaseInt(ptr);
-  }
-
-}
diff --git a/dev/linux/src/com/google/gwt/dev/shell/moz/JsValueMoz.java b/dev/linux/src/com/google/gwt/dev/shell/moz/JsValueMoz.java
new file mode 100644
index 0000000..d8456f0
--- /dev/null
+++ b/dev/linux/src/com/google/gwt/dev/shell/moz/JsValueMoz.java
@@ -0,0 +1,536 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.shell.moz;
+
+import com.google.gwt.dev.shell.CompilingClassLoader;
+import com.google.gwt.dev.shell.JsValue;
+import com.google.gwt.dev.shell.moz.LowLevelMoz.DispatchMethod;
+import com.google.gwt.dev.shell.moz.LowLevelMoz.DispatchObject;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Represents a Mozilla JavaScript value.
+ * 
+ * TODO(jat): 64-bit compatibility - currently underlying pointers are passed
+ * around in a Java int, which only works on standard 32-bit platforms where
+ * sizeof(void*)=4
+ */
+public class JsValueMoz extends JsValue {
+  private static class JsCleanupMoz implements JsCleanup {
+    private final int jsRootedValue;
+
+    public JsCleanupMoz(int jsRootedValue) {
+      this.jsRootedValue = jsRootedValue;
+    }
+
+    public void doCleanup() {
+      JsValueMoz.destroyJsRootedValue(jsRootedValue);
+    }
+  }
+
+  /**
+   * This must match the value from jsapi.h
+   */
+  private static final int JSVAL_VOID = 0x80000001;
+
+  // TODO(jat): remove debugging code before 1.4 final
+  private static Map alreadyCleanedJsRootedValues
+      = Collections.synchronizedMap(new HashMap());
+  private static int maxActive = 0;
+  private static int numActive = 0;
+  private static Map seenJsRootedValues 
+      = Collections.synchronizedMap(new HashMap());
+  private static int totAlloc = 0;
+
+  /**
+   * Create a new undefined JavaScript value.
+   * 
+   * @param scriptObject
+   * @return JsValueMoz object with an undefined value.
+   */
+  public static JsValueMoz createUndefinedValue(int scriptObject) {
+    return new JsValueMoz(scriptObject, JSVAL_VOID);
+  }
+
+  /**
+   * Print collected statistics on JsValueMoz usage.
+   */
+  public static void dumpStatistics() {
+    System.gc();
+    System.out.println("JsValueMoz usage:");
+    System.out.println(" " + totAlloc + " total instances created");
+    System.out.println(" " + maxActive + " at any one time");
+    System.out.println(" " + seenJsRootedValues.size() + " uncleaned entries");
+  }
+
+  // CHECKSTYLE_NAMING_OFF -- native methods start with '_'
+  protected static native boolean _getBoolean(int jsRootedValue);
+
+  protected static native int _getInt(int jsRootedValue);
+
+  protected static native double _getNumber(int jsRootedValue);
+
+  protected static native String _getString(int jsRootedValue);
+
+  protected static native String _getTypeString(int jsRootedValue);
+
+  protected static native DispatchObject _getWrappedJavaObject(int jsRootedValue);
+
+  protected static native boolean _isBoolean(int jsRootedValue);
+
+  protected static native boolean _isInt(int jsRootedValue);
+
+  protected static native boolean _isJavaScriptObject(int jsRootedValue);
+
+  protected static native boolean _isJavaScriptString(int jsRootedValue);
+
+  protected static native boolean _isNull(int jsRootedValue);
+
+  protected static native boolean _isNumber(int jsRootedValue);
+
+  protected static native boolean _isString(int jsRootedValue);
+
+  protected static native boolean _isUndefined(int jsRootedValue);
+
+  protected static native boolean _isWrappedJavaObject(int jsRootedValue);
+
+  protected static native void _setBoolean(int jsRootedValue, boolean val);
+
+  protected static native void _setDouble(int jsRootedValue, double val);
+
+  protected static native void _setInt(int jsRootedValue, int val);
+
+  protected static native void _setJsRootedValue(int jsRootedValue,
+      int jsOtherRootedValue);
+
+  protected static native void _setNull(int jsRootedValue);
+
+  protected static native void _setString(int jsRootedValue, String val);
+
+  protected static native void _setUndefined(int jsRootedValue);
+
+  protected static native void _setWrappedFunction(int jsRootedValue,
+      String methodName, DispatchMethod dispatchMethod);
+
+  protected static native void _setWrappedJavaObject(int jsRootedValue,
+      DispatchObject val);
+
+  private static native int _copyJsRootedValue(int jsRootedValue);
+
+  private static native int _createJsRootedValue(int scriptObject, int jsval);
+
+  private static native void _destroyJsRootedValue(int jsRootedValue);
+
+  // CHECKSTYLE_NAMING_ON
+
+  /**
+   * Create a JsRootedValue and return a pointer to it as a Java int.
+   * 
+   * @param scriptObject opaque script object pointer as an integer.
+   * @param jsval JavaScript jsval for initial value
+   * @return pointer to JsRootedValue object as an integer
+   */
+  private static int createJsRootedValue(int scriptObject, int jsval) {
+    int jsRootedValue = _createJsRootedValue(scriptObject, jsval);
+    return jsRootedValue;
+  }
+
+  /**
+   * Destroy a JsRootedValue.
+   * 
+   * @param jsRootedValue pointer to underlying JsRootedValue as an integer.
+   */
+  private static void destroyJsRootedValue(int jsRootedValue) {
+    _destroyJsRootedValue(jsRootedValue);
+  }
+
+  // pointer to underlying JsRootedValue object as an integer
+  private int jsRootedValue;
+
+  /**
+   * Create a JsValueMoz object wrapping a JsRootedValue object given the
+   * pointer to it as an integer.
+   * 
+   * @param jsRootedValue pointer to underlying JsRootedValue as an integer.
+   */
+  public JsValueMoz(int jsRootedValue) {
+    this.jsRootedValue = jsRootedValue;
+    createInstance();
+  }
+
+  /**
+   * Copy constructor.
+   * 
+   * @param other JsValueMoz instance to copy
+   */
+  public JsValueMoz(JsValueMoz other) {
+    jsRootedValue = _copyJsRootedValue(other.jsRootedValue);
+    createInstance();
+  }
+  
+  /**
+   * Create a JsValue object with the JavaScript value jsval.
+   * Only used internally.
+   * 
+   * @param scriptObject reference to containing window object in JavaScript
+   * @param jsval a JavaScript jsval as a 32-bit int
+   */
+  protected JsValueMoz(int scriptObject, int jsval) {
+    jsRootedValue = _createJsRootedValue(scriptObject, jsval);
+    createInstance();
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#getBoolean()
+   */
+  public boolean getBoolean() {
+    return _getBoolean(jsRootedValue);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#getInt()
+   */
+  public int getInt() {
+    return _getInt(jsRootedValue);
+  }
+
+  /**
+   * Returns the underlying JavaScript object pointer as an integer.
+   */
+  public int getJsRootedValue() {
+    return jsRootedValue;
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#getNumber()
+   */
+  public double getNumber() {
+    return _getNumber(jsRootedValue);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#getString()
+   */
+  public String getString() {
+    return _getString(jsRootedValue);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#getTypeString()
+   */
+  public String getTypeString() {
+    return _getTypeString(jsRootedValue);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#getWrappedJavaObject()
+   */
+  public Object getWrappedJavaObject() {
+    DispatchObject obj = _getWrappedJavaObject(jsRootedValue);
+    return obj.getTarget();
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#isBoolean()
+   */
+  public boolean isBoolean() {
+    return _isBoolean(jsRootedValue);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#isInt()
+   */
+  public boolean isInt() {
+    return _isInt(jsRootedValue);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#isJavaScriptObject()
+   */
+  public boolean isJavaScriptObject() {
+    return _isJavaScriptObject(jsRootedValue);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#isNull()
+   */
+  public boolean isNull() {
+    return _isNull(jsRootedValue);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#isNumber()
+   */
+  public boolean isNumber() {
+    return _isNumber(jsRootedValue);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#isString()
+   */
+  public boolean isString() {
+    // String objects are acceptable for String value returns
+    return _isString(jsRootedValue) || _isJavaScriptString(jsRootedValue);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#isUndefined()
+   */
+  public boolean isUndefined() {
+    return _isUndefined(jsRootedValue);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#isWrappedJavaObject()
+   */
+  public boolean isWrappedJavaObject() {
+    return _isWrappedJavaObject(jsRootedValue);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#setBoolean(boolean)
+   */
+  public void setBoolean(boolean val) {
+    _setBoolean(jsRootedValue, val);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#setByte(byte)
+   * 
+   * TODO(jat): remove this method
+   */
+  public void setByte(byte val) {
+    _setInt(jsRootedValue, val);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#setChar(char)
+   * 
+   * TODO(jat): remove this method
+   */
+  public void setChar(char val) {
+    _setInt(jsRootedValue, val);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#setDouble(double)
+   */
+  public void setDouble(double val) {
+    _setDouble(jsRootedValue, val);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#setInt(int)
+   */
+  public void setInt(int val) {
+    _setInt(jsRootedValue, val);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#setNull()
+   */
+  public void setNull() {
+    _setNull(jsRootedValue);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#setShort(short)
+   * 
+   * TODO(jat): remove this method
+   */
+  public void setShort(short val) {
+    _setInt(jsRootedValue, val);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#setString(java.lang.String)
+   */
+  public void setString(String val) {
+    _setString(jsRootedValue, val);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#setUndefined()
+   */
+  public void setUndefined() {
+    _setUndefined(jsRootedValue);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#setValue(com.google.gwt.dev.shell.JsValue)
+   */
+  public void setValue(JsValue other) {
+    _setJsRootedValue(jsRootedValue, ((JsValueMoz) other).jsRootedValue);
+  }
+
+  /**
+   * Wrap a function call to a Java method in this JavaScript value.
+   * 
+   * @param methodName the name of the method to invoke
+   * @param dispatchMethod the wrapper object
+   */
+  public void setWrappedFunction(String methodName,
+      DispatchMethod dispatchMethod) {
+    _setWrappedFunction(jsRootedValue, methodName, dispatchMethod);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#setWrappedJavaObject(com.google.gwt.dev.shell.CompilingClassLoader,
+   *      java.lang.Object)
+   */
+  public void setWrappedJavaObject(CompilingClassLoader cl, Object val) {
+    if (val == null) {
+      setNull();
+      return;
+    }
+    DispatchObject dispObj;
+    if (val instanceof DispatchObject) {
+      dispObj = (DispatchObject) val;
+    } else {
+      dispObj = new GeckoDispatchAdapter(cl, val);
+    }
+    _setWrappedJavaObject(jsRootedValue, dispObj);
+  }
+
+  /**
+   * create a cleanup object that will free the underlying JsRootedValue object.
+   */
+  protected JsCleanup createCleanupObject() {
+    JsCleanup cleanup = new JsCleanupMoz(jsRootedValue);
+    destroyInstance();
+    return cleanup;
+  }
+
+  /**
+   * Count a JsValueMoz instance being created.
+   */
+  protected void createInstance() {
+    // TODO(jat): remove this debug code before 1.4 final
+    Integer jsrv = new Integer(jsRootedValue);
+    if (seenJsRootedValues.containsKey(jsrv)) {
+      Throwable t = (Throwable) seenJsRootedValues.get(jsrv);
+      String msg = hexString(jsRootedValue);
+      System.err.println(msg + ", original caller stacktrace:");
+      t.printStackTrace();
+      throw new RuntimeException(msg);
+    }
+    Throwable t = new Throwable();
+    t.fillInStackTrace();
+    seenJsRootedValues.put(jsrv, t);
+    if (alreadyCleanedJsRootedValues.containsKey(jsrv)) {
+      alreadyCleanedJsRootedValues.remove(jsrv);
+    }
+    if (++numActive > maxActive) {
+      maxActive = numActive;
+    }
+    ++totAlloc;
+  }
+
+  /**
+   * Count a JsValueMoz instance being destroyed.
+   */
+  protected void destroyInstance() {
+    // TODO(jat): remove this debug code before 1.4 final
+    if (jsRootedValue == 0) {
+      throw new RuntimeException("Cleaning already-cleaned JsValueMoz");
+    }
+    Integer jsrv = new Integer(jsRootedValue);
+    if (!seenJsRootedValues.containsKey(jsrv)) {
+      throw new RuntimeException("cleaning up 0x" + hexString(jsRootedValue)
+          + ", not active");
+    }
+    if (alreadyCleanedJsRootedValues.containsKey(jsrv)) {
+      Throwable t = (Throwable) seenJsRootedValues.get(jsrv);
+      String msg = "Already cleaned 0x" + hexString(jsRootedValue);
+      System.err.println(msg + ", original allocator stacktrace:");
+      t.printStackTrace();
+      throw new RuntimeException(msg);
+    }
+    Throwable t = new Throwable();
+    t.fillInStackTrace();
+    alreadyCleanedJsRootedValues.put(jsrv, t);
+    seenJsRootedValues.remove(jsrv);
+    jsRootedValue = 0;
+    --numActive;
+  }
+
+  /**
+   * Convert an address to a hex string.
+   * TODO(jat): remove this method
+   * 
+   * @param jsRootedValue underlying JavaScript value as an opaque integer
+   * @return a string with the JavaScript value represented as hex
+   */
+  private String hexString(int jsRootedValue) {
+    long l = jsRootedValue;
+    l = l & 0xffffffffL;
+    return Long.toHexString(l);
+  }
+
+}
diff --git a/dev/linux/src/com/google/gwt/dev/shell/moz/LowLevelMoz.java b/dev/linux/src/com/google/gwt/dev/shell/moz/LowLevelMoz.java
index a4440ea..cfb4ab7 100644
--- a/dev/linux/src/com/google/gwt/dev/shell/moz/LowLevelMoz.java
+++ b/dev/linux/src/com/google/gwt/dev/shell/moz/LowLevelMoz.java
@@ -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
@@ -29,20 +29,40 @@
 public class LowLevelMoz {
 
   /**
-   * 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 jsthis, int[] jsargs);
+    /**
+     * Invoke a Java method from JavaScript.
+     * 
+     * @param jsthis the wrapped Java object to invoke
+     * @param jsargs an array of JavaScript values to pass as parameters
+     * @param returnValue the JavaScript value in which to store the returned
+     *     value
+     */
+    void invoke(int jscontext, int jsthis, int[] jsargs, int returnValue);
   }
 
   /**
-   * Provides interface for objects to be exposed on javascript side.
+   * Provides interface for objects to be exposed to JavaScript code.
    */
   public interface DispatchObject {
-    int getField(String name);
+    /**
+     * Retrieve a field from an object.
+     * 
+     * @param name the name of the field
+     * @param value pointer to the JsRootedValue to receive the field value
+     */
+    void getField(String name, int value);
 
     Object getTarget();
 
+    /**
+     * Set the value of a field on an object.
+     * 
+     * @param name the name of the field
+     * @param value pointer to the JsRootedValue to store into the field
+     */
     void setField(String name, int value);
   }
 
@@ -58,187 +78,12 @@
    */
   interface ExternalObject {
     boolean gwtOnLoad(int scriptGlobalObject, String moduleName);
-
-    /**
-     * TODO: rip this out.
-     */
-    int resolveReference(String ident);
   }
 
-  public static final int JSVAL_NULL = 0;
-  public static final int JSVAL_VOID = 0x80000001;
-  private static final int JSVAL_OBJECT = 0;
-  private static final int JSVAL_STRING = 4;
-  private static final int JSVAL_TAGMASK = 0x7;
-
+  private static int invokeCount = 0;
   private static Vector sExternalFactories = new Vector();
   private static boolean sInitialized = false;
 
-  public static boolean coerceToBoolean(int scriptObject, int jsval) {
-    boolean[] rval = new boolean[1];
-    if (!_coerceToBoolean(scriptObject, jsval, rval)) {
-      throw new RuntimeException("Failed to coerce to boolean value.");
-    }
-    return rval[0];
-  }
-
-  public static byte coerceToByte(int scriptObject, int jsval) {
-    int[] rval = new int[1];
-    if (!_coerceTo31Bits(scriptObject, jsval, rval)) {
-      throw new RuntimeException("Failed to coerce to byte value");
-    }
-    return (byte) rval[0];
-  }
-
-  public static char coerceToChar(int scriptObject, int jsval) {
-    int[] rval = new int[1];
-    if (!_coerceTo31Bits(scriptObject, jsval, rval)) {
-      throw new RuntimeException("Failed to coerce to char value");
-    }
-    return (char) rval[0];
-  }
-
-  public static double coerceToDouble(int scriptObject, int jsval) {
-    double[] rval = new double[1];
-    if (!_coerceToDouble(scriptObject, jsval, rval)) {
-      throw new RuntimeException("Failed to coerce to double value");
-    }
-    return rval[0];
-  }
-
-  public static float coerceToFloat(int scriptObject, int jsval) {
-    double[] rval = new double[1];
-    if (!_coerceToDouble(scriptObject, jsval, rval)) {
-      throw new RuntimeException("Failed to coerce to double value");
-    }
-    return (float) rval[0];
-  }
-
-  public static int coerceToInt(int scriptObject, int jsval) {
-    double[] rval = new double[1];
-    if (!_coerceToDouble(scriptObject, jsval, rval)) {
-      throw new RuntimeException("Failed to coerce to int value");
-    }
-    return (int) rval[0];
-  }
-
-  public static long coerceToLong(int scriptObject, int jsval) {
-    double[] rval = new double[1];
-    if (!_coerceToDouble(scriptObject, jsval, rval)) {
-      throw new RuntimeException("Failed to coerce to long value");
-    }
-    return (long) rval[0];
-  }
-
-  public static short coerceToShort(int scriptObject, int jsval) {
-    int[] rval = new int[1];
-    if (!_coerceTo31Bits(scriptObject, jsval, rval)) {
-      throw new RuntimeException("Failed to coerce to short value");
-    }
-    return (short) rval[0];
-  }
-
-  public static String coerceToString(int scriptObject, int jsval) {
-    String[] rval = new String[1];
-    if (!_coerceToString(scriptObject, jsval, rval)) {
-      throw new RuntimeException("Failed to coerce to String value");
-    }
-    return rval[0];
-  }
-
-  public static int convertBoolean(int scriptObject, boolean v) {
-    int[] rval = new int[1];
-    if (!_convertBoolean(scriptObject, v, rval)) {
-      throw new RuntimeException("Failed to convert Boolean value: "
-          + String.valueOf(v));
-    }
-    return rval[0];
-  }
-
-  public static int convertByte(int scriptObject, byte v) {
-    int[] rval = new int[1];
-    if (!_convert31Bits(scriptObject, v, rval)) {
-      throw new RuntimeException("Failed to convert Byte value: "
-          + String.valueOf(v));
-    }
-    return rval[0];
-  }
-
-  public static int convertChar(int scriptObject, char v) {
-    int[] rval = new int[1];
-    if (!_convert31Bits(scriptObject, v, rval)) {
-      throw new RuntimeException("Failed to convert Char value: "
-          + String.valueOf(v));
-    }
-    return rval[0];
-  }
-
-  public static int convertDouble(int scriptObject, double v) {
-    int[] rval = new int[1];
-    if (!_convertDouble(scriptObject, v, rval)) {
-      throw new RuntimeException("Failed to convert Double value: "
-          + String.valueOf(v));
-    }
-    return rval[0];
-  }
-
-  public static int convertFloat(int scriptObject, float v) {
-    int[] rval = new int[1];
-    if (!_convertDouble(scriptObject, v, rval)) {
-      throw new RuntimeException("Failed to convert Float value: "
-          + String.valueOf(v));
-    }
-    return rval[0];
-  }
-
-  public static int convertInt(int scriptObject, int v) {
-    int[] rval = new int[1];
-    if (!_convertDouble(scriptObject, v, rval)) {
-      throw new RuntimeException("Failed to convert Int value: "
-          + String.valueOf(v));
-    }
-    return rval[0];
-  }
-
-  public static int convertLong(int scriptObject, long v) {
-    int[] rval = new int[1];
-    if (!_convertDouble(scriptObject, v, rval)) {
-      throw new RuntimeException("Failed to convert Long value: "
-          + String.valueOf(v));
-    }
-    return rval[0];
-  }
-
-  public static int convertShort(int scriptObject, short v) {
-    int[] rval = new int[1];
-    if (!_convert31Bits(scriptObject, v, rval)) {
-      throw new RuntimeException("Failed to convert Short value: "
-          + String.valueOf(v));
-    }
-    return rval[0];
-  }
-
-  public static int convertString(int scriptObject, String v) {
-    int[] rval = new int[1];
-    if (!_convertString(scriptObject, v, rval)) {
-      throw new RuntimeException("Failed to convert String value: "
-          + String.valueOf(v));
-    }
-    return rval[0];
-  }
-
-  /**
-   * Executes JavaScript code.
-   * 
-   * @param scriptObject An opaque handle to the script frame window
-   * @param code The JavaScript code to execute
-   */
-  public static void executeScript(int scriptObject, String code) {
-    if (!_executeScript(scriptObject, code)) {
-      throw new RuntimeException("Failed to execute script: " + code);
-    }
-  }
-
   /**
    * Executes JavaScript code, retaining file and line information.
    * 
@@ -283,62 +128,27 @@
    * 
    * @param scriptObject An opaque handle to the script frame window
    * @param methodName the method name on jsthis to call
-   * @param jsthis A wrapped java object as a jsval
-   * @param jsargs the arguments to pass to the method
-   * @return the result of the invocation
+   * @param jsthis A wrapped java object as a JsRootedValue pointer
+   * @param jsargs the arguments to pass to the method as JsRootedValue pointers
+   * 
+   * @throws RuntimeException if the invoke fails
    */
-  public static int invoke(int scriptObject, String methodName, int jsthis,
-      int[] jsargs) {
-    int[] rval = new int[1];
-    if (!_invoke(scriptObject, methodName, jsthis, jsargs.length, jsargs, rval)) {
+  public static void invoke(int scriptObject, String methodName,
+      int jsthis, int[] jsargs, int retval) {
+    if (!_invoke(scriptObject, methodName, jsthis, jsargs, retval)) {
       throw new RuntimeException("Failed to invoke native method: "
           + methodName + " with " + jsargs.length + " arguments.");
     }
-    return rval[0];
-  }
-
-  /**
-   * Is the jsval a JSObject?
-   * 
-   * @param jsval the value
-   * @return true if jsval is a JSObject
-   */
-  public static boolean isJSObject(int jsval) {
-    return (jsval & JSVAL_TAGMASK) == JSVAL_OBJECT;
-  }
-
-  /**
-   * Is the jsval a string primitive?
-   * 
-   * @param jsval the value
-   * @return true if the jsval is a string primitive
-   */
-  public static boolean isString(int jsval) {
-    return (jsval & JSVAL_TAGMASK) == JSVAL_STRING;
-  }
-
-  /**
-   * Is the jsval JSObject a wrapped DispatchObject?
-   * 
-   * @param scriptObject An opaque handle to the script frame window
-   * @param jsval the value
-   * @return true if the JSObject is a wrapped DispatchObject
-   */
-  public static boolean isWrappedDispatch(int scriptObject, int jsval) {
-    boolean[] rval = new boolean[1];
-    if (!_isWrappedDispatch(scriptObject, jsval, rval)) {
-      throw new RuntimeException("Failed isWrappedDispatch.");
-    }
-    return rval[0];
   }
 
   /**
    * Call this to raise an exception in JavaScript before returning control.
+   * Currently, the JavaScript exception throw is always null.
    * 
-   * @param scriptObject An opaque handle to the script frame window
+   * @param jscontext A JSContext pointer as a Java int
    */
-  public static void raiseJavaScriptException(int scriptObject, int jsval) {
-    if (!_raiseJavaScriptException(scriptObject, jsval)) {
+  public static void raiseJavaScriptException(int jscontext) {
+    if (!_raiseJavaScriptException(jscontext)) {
       throw new RuntimeException(
           "Failed to raise Java Exception into JavaScript.");
     }
@@ -369,79 +179,6 @@
   }
 
   /**
-   * Unwraps a wrapped DispatchObject.
-   * 
-   * @param scriptObject An opaque handle to the script frame window
-   * @param jsval a value previously returned from wrapDispatch
-   * @return the original DispatchObject
-   */
-  public static DispatchObject unwrapDispatch(int scriptObject, int jsval) {
-    DispatchObject[] rval = new DispatchObject[1];
-    if (!_unwrapDispatch(scriptObject, jsval, rval)) {
-      throw new RuntimeException("Failed to unwrapDispatch.");
-    }
-    return rval[0];
-  }
-
-  /**
-   * Unwraps a wrapped JSObject.
-   * 
-   * @param nsISupports a value previously returned from wrapJSObject
-   * @return the original jsval JSObject
-   */
-  public static int unwrapJSObject(int nsISupports) {
-    int[] rval = new int[1];
-    if (!_unwrapJSObject(nsISupports, rval)) {
-      throw new RuntimeException("Failed to unwrapJSObject.");
-    }
-    return rval[0];
-  }
-
-  /**
-   * @param scriptObject An opaque handle to the script frame window
-   * @param dispObj the DispatchObject to wrap
-   * @return the wrapped object as a jsval JSObject
-   */
-  public static int wrapDispatch(int scriptObject, DispatchObject dispObj) {
-    int[] rval = new int[1];
-    if (!_wrapDispatch(scriptObject, dispObj, rval)) {
-      throw new RuntimeException("Failed to wrapDispatch.");
-    }
-    return rval[0];
-  }
-
-  /**
-   * @param scriptObject An opaque handle to the script frame window
-   * @param name the name of the function to be wrapped
-   * @param dispMeth the DispatchMethod to wrap
-   * @return the wrapped method as a jsval JSObject
-   */
-  public static int wrapFunction(int scriptObject, String name,
-      DispatchMethod dispMeth) {
-    int[] rval = new int[1];
-    if (!_wrapFunction(scriptObject, name, dispMeth, rval)) {
-      throw new RuntimeException("Failed to wrapFunction.");
-    }
-    return rval[0];
-  }
-
-  /**
-   * Creates an nsISupports interface locking the contained JSObject jsval, so
-   * that we can lock/free it correctly via reference counting.
-   * 
-   * @param scriptObject the global script window
-   * @param jsval the JSObject to wrap
-   * @return an nsISupports wrapper object
-   */
-  public static int wrapJSObject(int scriptObject, int jsval) {
-    int[] rval = new int[1];
-    if (!_wrapJSObject(scriptObject, jsval, rval)) {
-      throw new RuntimeException("Failed to createJSObjectHolder.");
-    }
-    return rval[0];
-  }
-
-  /**
    * Called from native code to create an external object for a particular
    * window.
    * 
@@ -469,66 +206,53 @@
   }
 
   // CHECKSTYLE_NAMING_OFF: Non JSNI native code may have leading '_'s.
-  private static native boolean _coerceTo31Bits(int scriptObject, int jsval,
-      int[] rval);
-
-  private static native boolean _coerceToBoolean(int scriptObject, int jsval,
-      boolean[] rval);
-
-  private static native boolean _coerceToDouble(int scriptObject, int jsval,
-      double[] rval);
-
-  private static native boolean _coerceToString(int scriptObject, int jsval,
-      String[] rval);
-
-  private static native boolean _convert31Bits(int scriptObject, int v,
-      int[] rval);
-
-  private static native boolean _convertBoolean(int scriptObject, boolean v,
-      int[] rval);
-
-  private static native boolean _convertDouble(int scriptObject, double v,
-      int[] rval);
-
-  private static native boolean _convertString(int scriptObject, String v,
-      int[] rval);
-
   private static native boolean _executeScript(int scriptObject, String code);
 
   private static native boolean _executeScriptWithInfo(int scriptObject,
       String newScript, String file, int line);
 
+  /**
+   * Native method for invoking a JavaScript method.
+   * 
+   * @param scriptObject nsIScriptGlobalObject* as an int
+   * @param methodName name of JavaScript method
+   * @param jsThisInt JavaScript object to invoke the method on, as a
+   *   JsRootedValue int
+   * @param jsArgsInt array of arguments, as an array of JsRootedValue ints
+   * @param jsRetValint pointer to JsRootedValue to receive return value
+   * @return true on success
+   */
   private static native boolean _invoke(int scriptObject, String methodName,
-      int jsthis, int jsargCount, int[] jsargs, int[] rval);
+      int jsThisInt, int[] jsArgsInt, int jsRetValInt);
 
-  private static native boolean _isWrappedDispatch(int scriptObject, int jsval,
-      boolean[] rval);
-
-  private static native boolean _raiseJavaScriptException(int scriptObject,
-      int jsval);
+  private static native boolean _raiseJavaScriptException(int jscontext);
 
   private static native boolean _registerExternalFactoryHandler();
 
-  private static native boolean _unwrapDispatch(int scriptObject, int jsval,
-      DispatchObject[] rval);
-
-  private static native boolean _unwrapJSObject(int nsISupports, int[] rval);
-
-  private static native boolean _wrapDispatch(int scriptObject,
-      DispatchObject dispObj, int[] rval);
-
-  private static native boolean _wrapFunction(int scriptObject, String name,
-      DispatchMethod dispMeth, int[] rval);
-
-  private static native boolean _wrapJSObject(int scriptObject, int jsval,
-      int[] rval);
-
   // CHECKSTYLE_NAMING_ON
 
   /**
+   * Print debug information for a JS method invocation.
+   * TODO(jat): remove this method
+   * 
+   * @param methodName the name of the JS method being invoked
+   * @param jsthis the JS object with the named method
+   * @param jsargs an array of arguments to the method
+   */
+  private static void printInvocationParams(String methodName, JsValueMoz jsthis, JsValueMoz[] jsargs) {
+    System.out.println("LowLevelMoz.invoke:");
+    System.out.println(" method = " + methodName);
+    System.out.println(" # args = " + (jsargs.length));
+    System.out.println(" jsthis = " + jsthis.toString());
+    for (int i = 0; i < jsargs.length; ++i) {
+      System.out.println(" jsarg[" + i + "] = " + jsargs[i].toString());
+    }
+    System.out.println("");
+  }
+
+  /**
    * Not instantiable.
    */
   private LowLevelMoz() {
   }
-
 }
diff --git a/dev/linux/src/com/google/gwt/dev/shell/moz/MethodDispatch.java b/dev/linux/src/com/google/gwt/dev/shell/moz/MethodDispatch.java
index 8c2521a..9ac0296 100644
--- a/dev/linux/src/com/google/gwt/dev/shell/moz/MethodDispatch.java
+++ b/dev/linux/src/com/google/gwt/dev/shell/moz/MethodDispatch.java
@@ -16,6 +16,8 @@
 package com.google.gwt.dev.shell.moz;
 
 import com.google.gwt.dev.shell.CompilingClassLoader;
+import com.google.gwt.dev.shell.JsValue;
+import com.google.gwt.dev.shell.JsValueGlue;
 import com.google.gwt.dev.shell.moz.LowLevelMoz.DispatchMethod;
 
 import java.lang.reflect.InvocationTargetException;
@@ -32,30 +34,45 @@
 
   private final Method method;
 
-  private final int scriptObject;
-
-  public MethodDispatch(CompilingClassLoader classLoader, Method method,
-      int scriptObject) {
-    this.scriptObject = scriptObject;
+  public MethodDispatch(CompilingClassLoader classLoader, Method method) {
     this.classLoader = classLoader;
     this.method = method;
   }
 
-  public int invoke(int jsthis, int[] jsargs) {
+  /**
+   * Invoke a Java method from JavaScript.
+   * This is called solely from native code.
+   * 
+   * @param jscontext JSContext* passed as an integer
+   * @param jsthis JavaScript reference to Java object
+   * @param jsargs array of JavaScript values for parameters
+   * @param returnValue JavaScript value to return result in
+   * @throws RuntimeException if improper arguments are supplied
+   * 
+   * TODO(jat): lift most of this interface to platform-independent code (only
+   *     exceptions still need to be made platform-independent)
+   */
+  public void invoke(int jscontext, int jsthisInt, int[] jsargsInt,
+      int returnValueInt) {
+    JsValue jsthis = new JsValueMoz(jsthisInt);
+    JsValue jsargs[] = new JsValue[jsargsInt.length];
+    for (int i = 0; i < jsargsInt.length; ++i) {
+      jsargs[i] = new JsValueMoz(jsargsInt[i]);
+    }
+    JsValue returnValue = new JsValueMoz(returnValueInt);
     Class[] paramTypes = method.getParameterTypes();
     int argc = paramTypes.length;
     Object args[] = new Object[argc];
+    // too many arguments are ok: the extra will be silently ignored
     if (jsargs.length < argc) {
       throw new RuntimeException("Not enough arguments to " + method);
     }
     Object jthis = null;
     if ((method.getModifiers() & Modifier.STATIC) == 0) {
-      jthis = SwtGeckoGlue.convertJSValToObject(scriptObject,
-          method.getDeclaringClass(), jsthis);
+      jthis = JsValueGlue.get(jsthis, method.getDeclaringClass(), "invoke this");
     }
     for (int i = 0; i < argc; ++i) {
-      args[i] = SwtGeckoGlue.convertJSValToObject(scriptObject, paramTypes[i],
-          jsargs[i]);
+      args[i] = JsValueGlue.get(jsargs[i], paramTypes[i], "invoke arguments");
     }
     try {
       Object result;
@@ -66,8 +83,7 @@
         e.printStackTrace();
         throw new RuntimeException(e);
       }
-      return SwtGeckoGlue.convertObjectToJSVal(scriptObject, classLoader,
-          method.getReturnType(), result);
+      JsValueGlue.set(returnValue, classLoader, method.getReturnType(), result);
     } catch (InvocationTargetException e) {
       // If we get here, it means an exception is being thrown from
       // Java back into JavaScript
@@ -79,9 +95,20 @@
         re = new RuntimeException("Checked exception thrown into JavaScript"
             + " (Web Mode behavior may differ)", t);
       }
+      // TODO(jat): if this was originally JavaScript exception, re-throw the
+      // original exception rather than just a null. 
       ModuleSpaceMoz.setThrownJavaException(re);
-      LowLevelMoz.raiseJavaScriptException(scriptObject, LowLevelMoz.JSVAL_NULL);
-      return LowLevelMoz.JSVAL_VOID;
+      LowLevelMoz.raiseJavaScriptException(jscontext);
+    } catch (IllegalArgumentException e) {
+      // TODO(jat): log to treelogger instead?  If so, how do I get to it?
+      System.err.println("MethodDispatch.invoke, method=" + method.toString()
+          + ": argument mismatch");
+      for (int i = 0; i < argc; ++i) {
+        System.err.println(" param " + i + " type is "
+            + paramTypes[i].toString() + " value is type "
+            + jsargs[i].getTypeString() + " = " + args[i].toString());
+      }
+      throw e;
     }
   }
 }
\ No newline at end of file
diff --git a/dev/linux/src/com/google/gwt/dev/shell/moz/ModuleSpaceMoz.java b/dev/linux/src/com/google/gwt/dev/shell/moz/ModuleSpaceMoz.java
index 8e90c1c..7cb960e 100644
--- a/dev/linux/src/com/google/gwt/dev/shell/moz/ModuleSpaceMoz.java
+++ b/dev/linux/src/com/google/gwt/dev/shell/moz/ModuleSpaceMoz.java
@@ -15,6 +15,9 @@
  */
 package com.google.gwt.dev.shell.moz;
 
+import com.google.gwt.dev.shell.CompilingClassLoader;
+import com.google.gwt.dev.shell.JsValue;
+import com.google.gwt.dev.shell.JsValueGlue;
 import com.google.gwt.dev.shell.ModuleSpace;
 import com.google.gwt.dev.shell.ModuleSpaceHost;
 import com.google.gwt.dev.shell.moz.LowLevelMoz.DispatchObject;
@@ -41,6 +44,9 @@
     SwtGeckoGlue.addRefInt(window);
   }
 
+  /* (non-Javadoc)
+   * @see com.google.gwt.dev.shell.ShellJavaScriptHost#createNative(java.lang.String, int, java.lang.String, java.lang.String[], java.lang.String)
+   */
   public void createNative(String file, int line, String jsniSignature,
       String[] paramNames, String js) {
     // Execute the function definition within the browser, which will define
@@ -50,11 +56,17 @@
     LowLevelMoz.executeScriptWithInfo(window, newScript, file, line);
   }
 
+  /* (non-Javadoc)
+   * @see com.google.gwt.dev.shell.ModuleSpace#dispose()
+   */
   public void dispose() {
     SwtGeckoGlue.releaseInt(window);
     super.dispose();
   }
 
+  /* (non-Javadoc)
+   * @see com.google.gwt.dev.shell.ShellJavaScriptHost#exceptionCaught(int, java.lang.String, java.lang.String)
+   */
   public void exceptionCaught(int number, String name, String message) {
     RuntimeException thrown = (RuntimeException) sThrownJavaExceptionObject.get();
 
@@ -71,128 +83,8 @@
         getIsolatedClassLoader(), name, message));
   }
 
-  public boolean invokeNativeBoolean(String name, Object jthis, Class[] types,
-      Object[] args) {
-    int jsval = invokeNative(name, jthis, types, args);
-    if (jsval == LowLevelMoz.JSVAL_VOID && isExceptionActive()) {
-      return false;
-    }
-    return LowLevelMoz.coerceToBoolean(window, jsval);
-  }
-
-  public byte invokeNativeByte(String name, Object jthis, Class[] types,
-      Object[] args) {
-    int jsval = invokeNative(name, jthis, types, args);
-    if (jsval == LowLevelMoz.JSVAL_VOID && isExceptionActive()) {
-      return 0;
-    }
-    return LowLevelMoz.coerceToByte(window, jsval);
-  }
-
-  public char invokeNativeChar(String name, Object jthis, Class[] types,
-      Object[] args) {
-    int jsval = invokeNative(name, jthis, types, args);
-    if (jsval == LowLevelMoz.JSVAL_VOID && isExceptionActive()) {
-      return 0;
-    }
-    return LowLevelMoz.coerceToChar(window, jsval);
-  }
-
-  public double invokeNativeDouble(String name, Object jthis, Class[] types,
-      Object[] args) {
-    int jsval = invokeNative(name, jthis, types, args);
-    if (jsval == LowLevelMoz.JSVAL_VOID && isExceptionActive()) {
-      return 0;
-    }
-    return LowLevelMoz.coerceToDouble(window, jsval);
-  }
-
-  public float invokeNativeFloat(String name, Object jthis, Class[] types,
-      Object[] args) {
-    int jsval = invokeNative(name, jthis, types, args);
-    if (jsval == LowLevelMoz.JSVAL_VOID && isExceptionActive()) {
-      return 0;
-    }
-    return LowLevelMoz.coerceToFloat(window, jsval);
-  }
-
-  public Object invokeNativeHandle(String name, Object jthis, Class returnType,
-      Class[] types, Object[] args) {
-    int jsval = invokeNative(name, jthis, types, args);
-    if (jsval == LowLevelMoz.JSVAL_VOID && isExceptionActive()) {
-      return null;
-    }
-    return SwtGeckoGlue.convertJSValToObject(window, returnType, jsval);
-  }
-
-  public int invokeNativeInt(String name, Object jthis, Class[] types,
-      Object[] args) {
-    int jsval = invokeNative(name, jthis, types, args);
-    if (jsval == LowLevelMoz.JSVAL_VOID && isExceptionActive()) {
-      return 0;
-    }
-    return LowLevelMoz.coerceToInt(window, jsval);
-  }
-
-  public long invokeNativeLong(String name, Object jthis, Class[] types,
-      Object[] args) {
-    int jsval = invokeNative(name, jthis, types, args);
-    if (jsval == LowLevelMoz.JSVAL_VOID && isExceptionActive()) {
-      return 0;
-    }
-    return LowLevelMoz.coerceToLong(window, jsval);
-  }
-
-  public Object invokeNativeObject(String name, Object jthis, Class[] types,
-      Object[] args) {
-    int jsval = invokeNative(name, jthis, types, args);
-    if (jsval == LowLevelMoz.JSVAL_VOID && isExceptionActive()) {
-      return null;
-    }
-    return SwtGeckoGlue.convertJSValToObject(window, Object.class, jsval);
-  }
-
-  public short invokeNativeShort(String name, Object jthis, Class[] types,
-      Object[] args) {
-    int jsval = invokeNative(name, jthis, types, args);
-    if (jsval == LowLevelMoz.JSVAL_VOID && isExceptionActive()) {
-      return 0;
-    }
-    return LowLevelMoz.coerceToShort(window, jsval);
-  }
-
-  public String invokeNativeString(String name, Object jthis, Class[] types,
-      Object[] args) {
-    int jsval = invokeNative(name, jthis, types, args);
-    if (jsval == LowLevelMoz.JSVAL_VOID && isExceptionActive()) {
-      return null;
-    }
-    return LowLevelMoz.coerceToString(window, jsval);
-  }
-
-  public void invokeNativeVoid(String name, Object jthis, Class[] types,
-      Object[] args) {
-    invokeNative(name, jthis, types, args);
-  }
-
-  protected void initializeStaticDispatcher() {
-    staticDispatch = new GeckoDispatchAdapter(getIsolatedClassLoader(), window);
-
-    // Define the static dispatcher for use by JavaScript.
-    //
-    createNative("initializeStaticDispatcher", 0, "__defineStatic",
-        new String[] {"__arg0"}, "window.__static = __arg0;");
-    invokeNativeVoid("__defineStatic", null, new Class[] {Object.class},
-        new Object[] {staticDispatch});
-  }
-
-  int wrapObjectAsJSObject(Object o) {
-    return SwtGeckoGlue.wrapObjectAsJSObject(getIsolatedClassLoader(), window,
-        o);
-  }
-
   /**
-   * Invokes a native javascript function.
+   * Invokes a native JavaScript function.
    * 
    * @param name the name of the function to invoke
    * @param jthis the function's 'this' context
@@ -200,24 +92,27 @@
    * @param args the arguments to be passed
    * @return the return value as a Object.
    */
-  private int invokeNative(String name, Object jthis, Class[] types,
+  protected JsValue doInvoke(String name, Object jthis, Class[] types,
       Object[] args) {
-    // Every time a native method is invoked, release any enqueued COM objects.
-    //
-    HandleMoz.releaseQueuedPtrs();
 
-    int jsthis = wrapObjectAsJSObject(jthis);
+    JsValueMoz jsthis = JsValueMoz.createUndefinedValue(window);
+    CompilingClassLoader isolatedClassLoader = getIsolatedClassLoader();
+    jsthis.setWrappedJavaObject(isolatedClassLoader, jthis);
 
     int argc = args.length;
-    int argv[] = new int[argc];
+    JsValueMoz argv[] = new JsValueMoz[argc];
+    int[] jsArgsInt = new int[argc];
     for (int i = 0; i < argc; ++i) {
-      argv[i] = SwtGeckoGlue.convertObjectToJSVal(window,
-          getIsolatedClassLoader(), types[i], args[i]);
+      argv[i] = JsValueMoz.createUndefinedValue(window);
+      JsValueGlue.set(argv[i], isolatedClassLoader, types[i], args[i]);
+      jsArgsInt[i] = argv[i].getJsRootedValue();
     }
-
-    int result = LowLevelMoz.invoke(window, name, jsthis, argv);
+    JsValueMoz returnVal = JsValueMoz.createUndefinedValue(window);
+    LowLevelMoz.invoke(window, name, jsthis.getJsRootedValue(),
+        jsArgsInt, returnVal.getJsRootedValue());
+    
     if (!isExceptionActive()) {
-      return result;
+      return returnVal;
     }
 
     /*
@@ -230,4 +125,14 @@
     throw thrown;
   }
 
+  protected void initializeStaticDispatcher() {
+    staticDispatch = new GeckoDispatchAdapter(getIsolatedClassLoader());
+
+    // Define the static dispatcher for use by JavaScript.
+    //
+    createNative("initializeStaticDispatcher", 0, "__defineStatic",
+        new String[] {"__arg0"}, "window.__static = __arg0;");
+    invokeNativeVoid("__defineStatic", null, new Class[] {Object.class},
+        new Object[] {staticDispatch});
+  }
 }
diff --git a/dev/linux/src/com/google/gwt/dev/shell/moz/SwtGeckoGlue.java b/dev/linux/src/com/google/gwt/dev/shell/moz/SwtGeckoGlue.java
index 46b8e29..3aa84c3 100644
--- a/dev/linux/src/com/google/gwt/dev/shell/moz/SwtGeckoGlue.java
+++ b/dev/linux/src/com/google/gwt/dev/shell/moz/SwtGeckoGlue.java
@@ -15,10 +15,6 @@
  */
 package com.google.gwt.dev.shell.moz;
 
-import com.google.gwt.dev.shell.CompilingClassLoader;
-import com.google.gwt.dev.shell.moz.LowLevelMoz.DispatchObject;
-import com.google.gwt.dev.util.TypeInfo;
-
 import org.eclipse.swt.internal.mozilla.XPCOM;
 
 import java.lang.reflect.InvocationTargetException;
@@ -27,6 +23,9 @@
 /**
  * A bag of static helper methods for mucking about with low-level SWT and Gecko
  * constructs.
+ * 
+ * TODO(jat): remove this class by replacing the nsISupports code with
+ * JsRootedValue references.
  */
 class SwtGeckoGlue {
 
@@ -56,124 +55,6 @@
   }
 
   /**
-   * Try to convert based on the Java method parameter type.
-   */
-  public static Object convertJSValToObject(int scriptObject, Class paramType,
-      int jsval) {
-    if (jsval == LowLevelMoz.JSVAL_VOID || jsval == LowLevelMoz.JSVAL_NULL) {
-      // It is actually a null reference.
-      return null;
-    }
-
-    if (LowLevelMoz.isJSObject(jsval)) {
-      Object translated = translateJSObject(scriptObject, paramType, jsval);
-
-      // Make sure that the method we are going to call matches on this
-      // parameter.
-      if (paramType.isAssignableFrom(translated.getClass())) {
-        return translated;
-      }
-    }
-
-    switch (TypeInfo.classifyType(paramType)) {
-      case TypeInfo.TYPE_WRAP_BOOLEAN:
-      case TypeInfo.TYPE_PRIM_BOOLEAN:
-        return Boolean.valueOf(LowLevelMoz.coerceToBoolean(scriptObject, jsval));
-
-      case TypeInfo.TYPE_WRAP_BYTE:
-      case TypeInfo.TYPE_PRIM_BYTE:
-        return new Byte(LowLevelMoz.coerceToByte(scriptObject, jsval));
-
-      case TypeInfo.TYPE_WRAP_CHAR:
-      case TypeInfo.TYPE_PRIM_CHAR:
-        return new Character(LowLevelMoz.coerceToChar(scriptObject, jsval));
-
-      case TypeInfo.TYPE_WRAP_DOUBLE:
-      case TypeInfo.TYPE_PRIM_DOUBLE:
-        return new Double(LowLevelMoz.coerceToDouble(scriptObject, jsval));
-
-      case TypeInfo.TYPE_WRAP_FLOAT:
-      case TypeInfo.TYPE_PRIM_FLOAT:
-        return new Float(LowLevelMoz.coerceToFloat(scriptObject, jsval));
-
-      case TypeInfo.TYPE_WRAP_INT:
-      case TypeInfo.TYPE_PRIM_INT:
-        return new Integer(LowLevelMoz.coerceToInt(scriptObject, jsval));
-
-      case TypeInfo.TYPE_WRAP_LONG:
-      case TypeInfo.TYPE_PRIM_LONG:
-        return new Long(LowLevelMoz.coerceToLong(scriptObject, jsval));
-
-      case TypeInfo.TYPE_WRAP_SHORT:
-      case TypeInfo.TYPE_PRIM_SHORT:
-        return new Short(LowLevelMoz.coerceToShort(scriptObject, jsval));
-
-      case TypeInfo.TYPE_WRAP_STRING:
-        return LowLevelMoz.coerceToString(scriptObject, jsval);
-
-      case TypeInfo.TYPE_USER:
-        if (LowLevelMoz.isString(jsval)) {
-          return LowLevelMoz.coerceToString(scriptObject, jsval);
-        }
-        // if it isn't a String, it's an error, break to error
-        break;
-    }
-
-    // Just don't know what do to with this.
-    throw new IllegalArgumentException("Cannot convert to type "
-        + TypeInfo.getSourceRepresentation(paramType, ""));
-  }
-
-  /**
-   * Converts a java object to its equivalent variant. A ClassLoader is passed
-   * here so that Handles can be manipulated properly.
-   */
-  public static int convertObjectToJSVal(int scriptObject,
-      CompilingClassLoader cl, Class type, Object o) {
-    if (o == null) {
-      return LowLevelMoz.JSVAL_NULL;
-    }
-
-    if (type.equals(String.class)) {
-      return LowLevelMoz.convertString(scriptObject, (String) o);
-    } else if (type.equals(boolean.class)) {
-      return LowLevelMoz.convertBoolean(scriptObject,
-          ((Boolean) o).booleanValue());
-    } else if (type.equals(byte.class)) {
-      return LowLevelMoz.convertByte(scriptObject, ((Byte) o).byteValue());
-    } else if (type.equals(short.class)) {
-      return LowLevelMoz.convertShort(scriptObject, ((Short) o).shortValue());
-    } else if (type.equals(char.class)) {
-      return LowLevelMoz.convertChar(scriptObject, ((Character) o).charValue());
-    } else if (type.equals(int.class)) {
-      return LowLevelMoz.convertInt(scriptObject, ((Integer) o).intValue());
-    } else if (type.equals(long.class)) {
-      return LowLevelMoz.convertLong(scriptObject, ((Long) o).longValue());
-    } else if (type.equals(float.class)) {
-      return LowLevelMoz.convertFloat(scriptObject, ((Float) o).floatValue());
-    } else if (type.equals(double.class)) {
-      return LowLevelMoz.convertDouble(scriptObject, ((Double) o).doubleValue());
-    }
-
-    // Handle
-    try {
-      Class jso = Class.forName(HandleMoz.HANDLE_CLASS, true, cl);
-      if (jso.isAssignableFrom(type) && jso.isAssignableFrom(o.getClass())) {
-        // Variant never AddRef's its contents.
-        //
-        return HandleMoz.getJSObjectFromHandle(o);
-      }
-    } catch (ClassNotFoundException e) {
-      // Ignore the exception, if we can't find the class then obviously we
-      // don't have to worry about o being one
-    }
-
-    // Fallthrough case: Object.
-    //
-    return wrapObjectAsJSObject(cl, scriptObject, o);
-  }
-
-  /**
    * Wrapper for XPCOM's nsISupports::Release().
    */
   public static int releaseInt(int nsISupports) {
@@ -193,24 +74,6 @@
     throw new RuntimeException(ERRMSG_CANNOT_INVOKE, rethrow);
   }
 
-  /**
-   * Wraps a Java object as a JSObject.
-   */
-  public static int wrapObjectAsJSObject(CompilingClassLoader cl,
-      int scriptObject, Object jthis) {
-    if (jthis == null) {
-      return LowLevelMoz.JSVAL_NULL;
-    }
-
-    DispatchObject dispObj;
-    if (jthis instanceof DispatchObject) {
-      dispObj = (DispatchObject) jthis;
-    } else {
-      dispObj = new GeckoDispatchAdapter(cl, scriptObject, jthis);
-    }
-    return LowLevelMoz.wrapDispatch(scriptObject, dispObj);
-  }
-
   private static void ensureMethodsInitialized() {
     if (!areMethodsInitialized) {
       Throwable rethrow = null;
@@ -232,30 +95,4 @@
       }
     }
   }
-
-  /**
-   * Decides what to do with an incoming JSObject arg. Two possibilities here:
-   * (1) We received a true javascript object (e.g. DOM object), in which case
-   * we wrap it in a Handle. (2) We received a Java object that was passed
-   * through the outside world and back, in which case we use black magic to get
-   * it back.
-   */
-  private static Object translateJSObject(int scriptObject, Class type,
-      int jsval) {
-    if (LowLevelMoz.isWrappedDispatch(scriptObject, jsval)) {
-      DispatchObject dispObj = LowLevelMoz.unwrapDispatch(scriptObject, jsval);
-      return dispObj.getTarget();
-    }
-    int wrapper = 0;
-    try {
-      wrapper = LowLevelMoz.wrapJSObject(scriptObject, jsval);
-      return HandleMoz.createHandle(type, wrapper);
-    } finally {
-      // Handle should AddRef
-      if (wrapper != 0) {
-        releaseInt(wrapper);
-      }
-    }
-  }
-
 }
diff --git a/dev/mac/src/com/google/gwt/dev/shell/mac/BrowserWidgetSaf.java b/dev/mac/src/com/google/gwt/dev/shell/mac/BrowserWidgetSaf.java
index ff732c0..be49357 100644
--- a/dev/mac/src/com/google/gwt/dev/shell/mac/BrowserWidgetSaf.java
+++ b/dev/mac/src/com/google/gwt/dev/shell/mac/BrowserWidgetSaf.java
@@ -48,7 +48,7 @@
       try {
         if (moduleName == null) {
           // Indicates the page is being unloaded.
-          //
+          // TODO(jat): add support to unload a single module
           onPageUnload();
           return true;
         }
diff --git a/dev/mac/src/com/google/gwt/dev/shell/mac/HandleSaf.java b/dev/mac/src/com/google/gwt/dev/shell/mac/HandleSaf.java
deleted file mode 100644
index 47b647a..0000000
--- a/dev/mac/src/com/google/gwt/dev/shell/mac/HandleSaf.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2006 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.shell.mac;
-
-import com.google.gwt.dev.shell.Handle;
-
-class HandleSaf extends Handle {
-
-  static {
-    // put myself in Handle's sImpl field
-    new HandleSaf();
-  }
-
-  public static Object createHandle(Class type, int ptr) {
-    return Handle.createHandle(type, ptr);
-  }
-
-  public static int getJSObjectFromHandle(Object o) {
-    return getPtrFromHandle(o);
-  }
-
-  /**
-   * Not instantiable.
-   */
-  private HandleSaf() {
-  }
-
-  protected void lockPtr(int ptr) {
-    LowLevelSaf.gcLock(ptr);
-  }
-
-  protected void unlockPtr(int ptr) {
-    LowLevelSaf.gcUnlock(ptr);
-  }
-
-}
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
new file mode 100644
index 0000000..569cb47
--- /dev/null
+++ b/dev/mac/src/com/google/gwt/dev/shell/mac/JsValueSaf.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2006 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.shell.mac;
+
+import com.google.gwt.dev.shell.CompilingClassLoader;
+import com.google.gwt.dev.shell.JsValue;
+import com.google.gwt.dev.shell.mac.LowLevelSaf.DispatchObject;
+
+/**
+ * Represents a Safari JavaScript value.
+ */
+public class JsValueSaf extends JsValue {
+
+  private static class JsCleanupSaf implements JsCleanup {
+    private final int jsval;
+
+    /**
+     * Create a cleanup object which takes care of cleaning up the underlying JS
+     * object.
+     * 
+     * @param jsval JSValue pointer as an integer
+     */
+    public JsCleanupSaf(int jsval) {
+      this.jsval = jsval;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see com.google.gwt.dev.shell.JsValue.JsCleanup#doCleanup()
+     */
+    public void doCleanup() {
+      // TODO(jat): perform cleanup operation
+    }
+  }
+
+  // pointer to underlying JSValue object as an integer
+  private int jsval;
+
+  /**
+   * Create a Java wrapper around the underlying JSValue.
+   * 
+   * @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();
+  }
+
+  public boolean getBoolean() {
+    int curExecState = LowLevelSaf.getExecState();
+    return LowLevelSaf.coerceToBoolean(curExecState, jsval);
+  }
+
+  public int getInt() {
+    int curExecState = LowLevelSaf.getExecState();
+    return LowLevelSaf.coerceToInt(curExecState, jsval);
+ }
+
+  public int getJsValue() {
+    return jsval;
+  }
+
+  public double getNumber() {
+    int curExecState = LowLevelSaf.getExecState();
+    return LowLevelSaf.coerceToDouble(curExecState, jsval);
+  }
+
+  public String getString() {
+    int curExecState = LowLevelSaf.getExecState();
+    return LowLevelSaf.coerceToString(curExecState, jsval);
+  }
+
+  public String getTypeString() {
+    return LowLevelSaf.getTypeString(jsval);
+  }
+
+  public Object getWrappedJavaObject() {
+    DispatchObject obj = LowLevelSaf.unwrapDispatch(jsval);
+    return obj.getTarget();
+  }
+
+  public boolean isBoolean() {
+    return LowLevelSaf.isBoolean(jsval);
+  }
+
+  public boolean isInt() {
+    // Safari doesn't have integers, so this is always false
+    return false;
+  }
+
+  public boolean isJavaScriptObject() {
+    return LowLevelSaf.isObject(jsval) && !LowLevelSaf.isWrappedDispatch(jsval);
+  }
+
+  public boolean isNull() {
+    return LowLevelSaf.isNull(jsval);
+  }
+
+  public boolean isNumber() {
+    return LowLevelSaf.isNumber(jsval);
+  }
+
+  public boolean isObject() {
+    return LowLevelSaf.isObject(jsval);
+  }
+
+  public boolean isString() {
+    return LowLevelSaf.isString(jsval);
+  }
+
+  public boolean isUndefined() {
+    return LowLevelSaf.isUndefined(jsval);
+  }
+
+  public boolean isWrappedJavaObject() {
+    return LowLevelSaf.isWrappedDispatch(jsval);
+  }
+
+  public void setBoolean(boolean val) {
+    jsval = LowLevelSaf.convertBoolean(val);
+  }
+
+  public void setByte(byte val) {
+    jsval = LowLevelSaf.convertDouble(val);
+  }
+
+  public void setChar(char val) {
+    jsval = LowLevelSaf.convertDouble(val);
+  }
+
+  public void setDouble(double val) {
+    jsval = LowLevelSaf.convertDouble(val);
+  }
+
+  public void setInt(int val) {
+    jsval = LowLevelSaf.convertDouble(val);
+  }
+  
+  public void setJsVal(int jsval) {
+    this.jsval = jsval;
+  }
+
+  public void setNull() {
+    jsval = LowLevelSaf.jsNull();
+  }
+
+  public void setShort(short val) {
+    jsval = LowLevelSaf.convertDouble(val);
+  }
+
+  public void setString(String val) {
+    jsval = LowLevelSaf.convertString(val);
+  }
+
+  public void setUndefined() {
+    jsval = LowLevelSaf.jsUndefined();
+  }
+
+  public void setValue(JsValue other) {
+    jsval = ((JsValueSaf)other).jsval;
+  }
+
+  public void setWrappedJavaObject(CompilingClassLoader cl, Object val) {
+    DispatchObject dispObj;
+    if (val == null) {
+      setNull();
+      return;
+    } else if (val instanceof DispatchObject) {
+      dispObj = (DispatchObject)val;
+    } else {
+      dispObj = new WebKitDispatchAdapter(cl, val);
+    }
+    jsval = LowLevelSaf.wrapDispatch(dispObj);
+  }
+
+  protected JsCleanup createCleanupObject() {
+    return new JsCleanupSaf(jsval);
+  }
+
+}
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 e49acb8..f0e3e5f 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
@@ -210,6 +210,8 @@
     return result;
   }
 
+  public static native String getTypeString(int jsval);
+  
   public static synchronized void init() {
     // Force LowLevel initialization to load gwt-ll
     LowLevel.init();
@@ -232,6 +234,7 @@
         StringBuffer sb = new StringBuffer();
         sb.append("Unable to load required native library '" + libName + "'");
         sb.append("\n\tYour GWT installation may be corrupt");
+        System.err.println(sb.toString());
         throw new UnsatisfiedLinkError(sb.toString());
       }
       sInitialized = true;
@@ -260,11 +263,23 @@
 
   /**
    * @param jsval the js value in question
+   * @return <code>true</code> if the value is a boolean value
+   */
+  public static native boolean isBoolean(int jsval);
+
+  /**
+   * @param jsval the js value in question
    * @return <code>true</code> if the value is the null value
    */
   public static native boolean isNull(int jsval);
 
   /**
+   * @param jsval the js value in question
+   * @return <code>true</code> if the value is a boolean value
+   */
+  public static native boolean isNumber(int jsval);
+
+  /**
    * Is the jsval a JSObject?
    * 
    * @param jsval the value
diff --git a/dev/mac/src/com/google/gwt/dev/shell/mac/MethodDispatch.java b/dev/mac/src/com/google/gwt/dev/shell/mac/MethodDispatch.java
index 968d958..38106dd 100644
--- a/dev/mac/src/com/google/gwt/dev/shell/mac/MethodDispatch.java
+++ b/dev/mac/src/com/google/gwt/dev/shell/mac/MethodDispatch.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
@@ -16,6 +16,8 @@
 package com.google.gwt.dev.shell.mac;
 
 import com.google.gwt.dev.shell.CompilingClassLoader;
+import com.google.gwt.dev.shell.JsValue;
+import com.google.gwt.dev.shell.JsValueGlue;
 import com.google.gwt.dev.shell.mac.LowLevelSaf.DispatchMethod;
 
 import java.lang.reflect.InvocationTargetException;
@@ -32,17 +34,23 @@
 
   private final Method method;
 
-  private final int scriptObject;
+  // TODO(jat): remove these references
+  // private final int scriptObject;
 
-  public MethodDispatch(CompilingClassLoader classLoader, Method method,
-      int scriptObject) {
-    this.scriptObject = scriptObject;
+  public MethodDispatch(CompilingClassLoader classLoader, Method method) {
+    // this.scriptObject = scriptObject;
     this.classLoader = classLoader;
     this.method = method;
   }
 
-  public int invoke(int execState, int jsthis, int[] jsargs) {
+  public int invoke(int execState, int jsthisInt, int[] jsargsInt) {
     LowLevelSaf.pushExecState(execState);
+    JsValue jsthis = new JsValueSaf(jsthisInt);
+    JsValue jsargs[] = new JsValue[jsargsInt.length];
+    for (int i = 0; i < jsargsInt.length; ++i) {
+      jsargs[i] = new JsValueSaf(jsargsInt[i]);
+    }
+    JsValueSaf returnValue = new JsValueSaf();
     try {
       Class[] paramTypes = method.getParameterTypes();
       int argc = paramTypes.length;
@@ -50,13 +58,15 @@
       if (jsargs.length < argc) {
         throw new RuntimeException("Not enough arguments to " + method);
       }
+      if (jsargs.length > argc) {
+        throw new RuntimeException("Too many arguments to " + method);
+      }
       Object jthis = null;
       if ((method.getModifiers() & Modifier.STATIC) == 0) {
-        jthis = SwtWebKitGlue.convertJSValToObject(method.getDeclaringClass(),
-            jsthis);
+        jthis = JsValueGlue.get(jsthis, method.getDeclaringClass(), "invoke this");
       }
       for (int i = 0; i < argc; ++i) {
-        args[i] = SwtWebKitGlue.convertJSValToObject(paramTypes[i], jsargs[i]);
+        args[i] = JsValueGlue.get(jsargs[i], paramTypes[i], "invoke args");
       }
       try {
         Object result;
@@ -67,8 +77,9 @@
           e.printStackTrace();
           throw new RuntimeException(e);
         }
-        return SwtWebKitGlue.convertObjectToJSVal(scriptObject, classLoader,
-            method.getReturnType(), result);
+        JsValueGlue.set(returnValue, classLoader, method.getReturnType(),
+            result);
+        return returnValue.getJsValue();
       } catch (InvocationTargetException e) {
         // If we get here, it means an exception is being thrown from
         // Java back into JavaScript
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 cee0000..7ef03aa 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
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.shell.mac;
 
+import com.google.gwt.dev.shell.JsValue;
+import com.google.gwt.dev.shell.JsValueGlue;
 import com.google.gwt.dev.shell.ModuleSpace;
 import com.google.gwt.dev.shell.ModuleSpaceHost;
 import com.google.gwt.dev.shell.mac.LowLevelSaf.DispatchObject;
@@ -29,7 +31,7 @@
   private final int window;
 
   /**
-   * Constructs a browser interface for use with a Mozilla global window object.
+   * Constructs a browser interface for use with a global window object.
    */
   public ModuleSpaceSaf(ModuleSpaceHost host, int scriptGlobalObject) {
     super(host);
@@ -71,128 +73,8 @@
         getIsolatedClassLoader(), name, message));
   }
 
-  public boolean invokeNativeBoolean(String name, Object jthis, Class[] types,
-      Object[] args) {
-    int jsval = invokeNative(name, jthis, types, args);
-    if (LowLevelSaf.isUndefined(jsval) && isExceptionActive()) {
-      return false;
-    }
-    return LowLevelSaf.coerceToBoolean(LowLevelSaf.getExecState(), jsval);
-  }
-
-  public byte invokeNativeByte(String name, Object jthis, Class[] types,
-      Object[] args) {
-    int jsval = invokeNative(name, jthis, types, args);
-    if (LowLevelSaf.isUndefined(jsval) && isExceptionActive()) {
-      return 0;
-    }
-    return LowLevelSaf.coerceToByte(LowLevelSaf.getExecState(), jsval);
-  }
-
-  public char invokeNativeChar(String name, Object jthis, Class[] types,
-      Object[] args) {
-    int jsval = invokeNative(name, jthis, types, args);
-    if (LowLevelSaf.isUndefined(jsval) && isExceptionActive()) {
-      return 0;
-    }
-    return LowLevelSaf.coerceToChar(LowLevelSaf.getExecState(), jsval);
-  }
-
-  public double invokeNativeDouble(String name, Object jthis, Class[] types,
-      Object[] args) {
-    int jsval = invokeNative(name, jthis, types, args);
-    if (LowLevelSaf.isUndefined(jsval) && isExceptionActive()) {
-      return 0;
-    }
-    return LowLevelSaf.coerceToDouble(LowLevelSaf.getExecState(), jsval);
-  }
-
-  public float invokeNativeFloat(String name, Object jthis, Class[] types,
-      Object[] args) {
-    int jsval = invokeNative(name, jthis, types, args);
-    if (LowLevelSaf.isUndefined(jsval) && isExceptionActive()) {
-      return 0;
-    }
-    return LowLevelSaf.coerceToFloat(LowLevelSaf.getExecState(), jsval);
-  }
-
-  public Object invokeNativeHandle(String name, Object jthis, Class returnType,
-      Class[] types, Object[] args) {
-    int jsval = invokeNative(name, jthis, types, args);
-    if (LowLevelSaf.isUndefined(jsval) && isExceptionActive()) {
-      return null;
-    }
-    return SwtWebKitGlue.convertJSValToObject(returnType, jsval);
-  }
-
-  public int invokeNativeInt(String name, Object jthis, Class[] types,
-      Object[] args) {
-    int jsval = invokeNative(name, jthis, types, args);
-    if (LowLevelSaf.isUndefined(jsval) && isExceptionActive()) {
-      return 0;
-    }
-    return LowLevelSaf.coerceToInt(LowLevelSaf.getExecState(), jsval);
-  }
-
-  public long invokeNativeLong(String name, Object jthis, Class[] types,
-      Object[] args) {
-    int jsval = invokeNative(name, jthis, types, args);
-    if (LowLevelSaf.isUndefined(jsval) && isExceptionActive()) {
-      return 0;
-    }
-    return LowLevelSaf.coerceToLong(LowLevelSaf.getExecState(), jsval);
-  }
-
-  public Object invokeNativeObject(String name, Object jthis, Class[] types,
-      Object[] args) {
-    int jsval = invokeNative(name, jthis, types, args);
-    if (LowLevelSaf.isUndefined(jsval) && isExceptionActive()) {
-      return null;
-    }
-    return SwtWebKitGlue.convertJSValToObject(Object.class, jsval);
-  }
-
-  public short invokeNativeShort(String name, Object jthis, Class[] types,
-      Object[] args) {
-    int jsval = invokeNative(name, jthis, types, args);
-    if (LowLevelSaf.isUndefined(jsval) && isExceptionActive()) {
-      return 0;
-    }
-    return LowLevelSaf.coerceToShort(LowLevelSaf.getExecState(), jsval);
-  }
-
-  public String invokeNativeString(String name, Object jthis, Class[] types,
-      Object[] args) {
-    int jsval = invokeNative(name, jthis, types, args);
-    if (LowLevelSaf.isUndefined(jsval) && isExceptionActive()) {
-      return null;
-    }
-    return LowLevelSaf.coerceToString(LowLevelSaf.getExecState(), jsval);
-  }
-
-  public void invokeNativeVoid(String name, Object jthis, Class[] types,
-      Object[] args) {
-    invokeNative(name, jthis, types, args);
-  }
-
-  protected void initializeStaticDispatcher() {
-    staticDispatch = new WebKitDispatchAdapter(getIsolatedClassLoader(), window);
-
-    // Define the static dispatcher for use by JavaScript.
-    //
-    createNative("initializeStaticDispatcher", 0, "__defineStatic",
-        new String[] {"__arg0"}, "window.__static = __arg0;");
-    invokeNativeVoid("__defineStatic", null, new Class[] {Object.class},
-        new Object[] {staticDispatch});
-  }
-
-  int wrapObjectAsJSObject(Object o) {
-    return SwtWebKitGlue.wrapObjectAsJSObject(getIsolatedClassLoader(), window,
-        o);
-  }
-
   /**
-   * Invokes a native javascript function.
+   * Invokes a native JavaScript function.
    * 
    * @param name the name of the function to invoke
    * @param jthis the function's 'this' context
@@ -200,24 +82,21 @@
    * @param args the arguments to be passed
    * @return the return value as a Object.
    */
-  private int invokeNative(String name, Object jthis, Class[] types,
+  protected JsValue doInvoke(String name, Object jthis, Class[] types,
       Object[] args) {
-    // Every time a native method is invoked, release any enqueued COM objects.
-    //
-    HandleSaf.releaseQueuedPtrs();
-
     int jsthis = wrapObjectAsJSObject(jthis);
     int curExecState = LowLevelSaf.getExecState();
     int argc = args.length;
     int argv[] = new int[argc];
     for (int i = 0; i < argc; ++i) {
-      argv[i] = SwtWebKitGlue.convertObjectToJSVal(curExecState,
-          getIsolatedClassLoader(), types[i], args[i]);
+      JsValueSaf jsValue = new JsValueSaf();
+      JsValueGlue.set(jsValue, getIsolatedClassLoader(), types[i], args[i]);
+      argv[i] = jsValue.getJsValue();
     }
 
     int result = LowLevelSaf.invoke(curExecState, window, name, jsthis, argv);
     if (!isExceptionActive()) {
-      return result;
+      return new JsValueSaf(result);
     }
 
     /*
@@ -230,4 +109,29 @@
     throw thrown;
   }
 
+  protected void initializeStaticDispatcher() {
+    staticDispatch = new WebKitDispatchAdapter(getIsolatedClassLoader());
+
+    // Define the static dispatcher for use by JavaScript.
+    //
+    createNative("initializeStaticDispatcher", 0, "__defineStatic",
+        new String[] {"__arg0"}, "window.__static = __arg0;");
+    invokeNativeVoid("__defineStatic", null, new Class[] {Object.class},
+        new Object[] {staticDispatch});
+  }
+
+  protected int wrapObjectAsJSObject(Object o) {
+    if (o == null) {
+      return LowLevelSaf.jsNull();
+    }
+    
+    DispatchObject dispObj;
+    if (o instanceof DispatchObject) {
+      dispObj = (DispatchObject) o;
+    } else {
+      dispObj = new WebKitDispatchAdapter(getIsolatedClassLoader(), o);
+    }
+    return LowLevelSaf.wrapDispatch(dispObj);
+  }
+
 }
diff --git a/dev/mac/src/com/google/gwt/dev/shell/mac/SwtWebKitGlue.java b/dev/mac/src/com/google/gwt/dev/shell/mac/SwtWebKitGlue.java
deleted file mode 100644
index 7063243..0000000
--- a/dev/mac/src/com/google/gwt/dev/shell/mac/SwtWebKitGlue.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright 2006 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.shell.mac;
-
-import com.google.gwt.dev.shell.CompilingClassLoader;
-import com.google.gwt.dev.shell.mac.LowLevelSaf.DispatchObject;
-import com.google.gwt.dev.util.TypeInfo;
-
-/**
- * A bag of static helper methods for mucking about with low-level SWT and Gecko
- * constructs.
- */
-class SwtWebKitGlue {
-
-  /**
-   * Try to convert based on the Java method parameter type.
-   */
-  public static Object convertJSValToObject(Class paramType,
-      int jsval) {
-    if (LowLevelSaf.isNull(jsval) || LowLevelSaf.isUndefined(jsval)) {
-      // It is actually a null reference.
-      return null;
-    }
-
-    if (LowLevelSaf.isObject(jsval)) {
-      Object translated = translateJSObject(paramType, jsval);
-
-      // Make sure that the method we are going to call matches on this
-      // parameter.
-      if (paramType.isAssignableFrom(translated.getClass())) {
-        return translated;
-      }
-    }
-
-    int curExecState = LowLevelSaf.getExecState();
-
-    switch (TypeInfo.classifyType(paramType)) {
-      case TypeInfo.TYPE_WRAP_BOOLEAN:
-      case TypeInfo.TYPE_PRIM_BOOLEAN:
-        return Boolean.valueOf(LowLevelSaf.coerceToBoolean(curExecState, jsval));
-      case TypeInfo.TYPE_WRAP_BYTE:
-      case TypeInfo.TYPE_PRIM_BYTE:
-        return new Byte(LowLevelSaf.coerceToByte(curExecState, jsval));
-      case TypeInfo.TYPE_WRAP_CHAR:
-      case TypeInfo.TYPE_PRIM_CHAR:
-        return new Character(LowLevelSaf.coerceToChar(curExecState, jsval));
-
-      case TypeInfo.TYPE_WRAP_DOUBLE:
-      case TypeInfo.TYPE_PRIM_DOUBLE:
-        return new Double(LowLevelSaf.coerceToDouble(curExecState, jsval));
-
-      case TypeInfo.TYPE_WRAP_FLOAT:
-      case TypeInfo.TYPE_PRIM_FLOAT:
-        return new Float(LowLevelSaf.coerceToFloat(curExecState, jsval));
-
-      case TypeInfo.TYPE_WRAP_INT:
-      case TypeInfo.TYPE_PRIM_INT:
-        return new Integer(LowLevelSaf.coerceToInt(curExecState, jsval));
-
-      case TypeInfo.TYPE_WRAP_LONG:
-      case TypeInfo.TYPE_PRIM_LONG:
-        return new Long(LowLevelSaf.coerceToLong(curExecState, jsval));
-
-      case TypeInfo.TYPE_WRAP_SHORT:
-      case TypeInfo.TYPE_PRIM_SHORT:
-        return new Short(LowLevelSaf.coerceToShort(curExecState, jsval));
-
-      case TypeInfo.TYPE_WRAP_STRING:
-        return LowLevelSaf.coerceToString(curExecState, jsval);
-
-      case TypeInfo.TYPE_USER:
-        if (LowLevelSaf.isString(jsval)) {
-          return LowLevelSaf.coerceToString(curExecState, jsval);
-        }
-        // if it isn't a String, it's an error, break to error
-        break;
-    }
-
-    // Just don't know what do to with this.
-    throw new IllegalArgumentException("Cannot convert to type "
-        + TypeInfo.getSourceRepresentation(paramType, ""));
-  }
-
-  /**
-   * Converts a java object to its equivalent variant. A ClassLoader is passed
-   * here so that Handles can be manipulated properly.
-   */
-  public static int convertObjectToJSVal(int scriptObject,
-      CompilingClassLoader cl, Class type, Object o) {
-    if (o == null) {
-      return LowLevelSaf.jsNull();
-    }
-
-    if (type.equals(String.class)) {
-      return LowLevelSaf.convertString((String) o);
-    } else if (type.equals(boolean.class)) {
-      return LowLevelSaf.convertBoolean(((Boolean) o).booleanValue());
-    } else if (type.equals(byte.class)) {
-      return LowLevelSaf.convertDouble(((Byte) o).byteValue());
-    } else if (type.equals(short.class)) {
-      return LowLevelSaf.convertDouble(((Short) o).shortValue());
-    } else if (type.equals(char.class)) {
-      return LowLevelSaf.convertDouble(((Character) o).charValue());
-    } else if (type.equals(int.class)) {
-      return LowLevelSaf.convertDouble(((Integer) o).intValue());
-    } else if (type.equals(long.class)) {
-      return LowLevelSaf.convertDouble(((Long) o).longValue());
-    } else if (type.equals(float.class)) {
-      return LowLevelSaf.convertDouble(((Float) o).floatValue());
-    } else if (type.equals(double.class)) {
-      return LowLevelSaf.convertDouble(((Double) o).doubleValue());
-    }
-
-    // Handle
-    try {
-      Class jso = Class.forName(HandleSaf.HANDLE_CLASS, true, cl);
-      if (jso.isAssignableFrom(type) && jso.isAssignableFrom(o.getClass())) {
-        // Variant never AddRef's its contents.
-        //
-        return HandleSaf.getJSObjectFromHandle(o);
-      }
-    } catch (ClassNotFoundException e) {
-      // Ignore the exception, if we can't find the class then obviously we
-      // don't have to worry about o being one
-    }
-
-    // Fallthrough case: Object.
-    //
-    return wrapObjectAsJSObject(cl, scriptObject, o);
-  }
-
-  /**
-   * Wraps a Java object as a JSObject.
-   */
-  public static int wrapObjectAsJSObject(CompilingClassLoader cl,
-      int scriptObject, Object jthis) {
-    if (jthis == null) {
-      return LowLevelSaf.jsNull();
-    }
-
-    DispatchObject dispObj;
-    if (jthis instanceof DispatchObject) {
-      dispObj = (DispatchObject) jthis;
-    } else {
-      dispObj = new WebKitDispatchAdapter(cl, scriptObject, jthis);
-    }
-    return LowLevelSaf.wrapDispatch(dispObj);
-  }
-
-  /**
-   * Decides what to do with an incoming JSObject arg. Two possibilities here:
-   * (1) We received a true javascript object (e.g. DOM object), in which case
-   * we wrap it in a Handle. (2) We received a Java object that was passed
-   * through the outside world and back, in which case we use black magic to get
-   * it back.
-   */
-  private static Object translateJSObject(Class type, int jsval) {
-    if (LowLevelSaf.isWrappedDispatch(jsval)) {
-      DispatchObject dispObj = LowLevelSaf.unwrapDispatch(jsval);
-      return dispObj.getTarget();
-    }
-    return HandleSaf.createHandle(type, jsval);
-  }
-
-}
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 7420ee7..9e9a587 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
@@ -18,6 +18,8 @@
 import com.google.gwt.dev.shell.CompilingClassLoader;
 import com.google.gwt.dev.shell.JavaDispatch;
 import com.google.gwt.dev.shell.JavaDispatchImpl;
+import com.google.gwt.dev.shell.JsValue;
+import com.google.gwt.dev.shell.JsValueGlue;
 import com.google.gwt.dev.shell.mac.LowLevelSaf.DispatchMethod;
 import com.google.gwt.dev.shell.mac.LowLevelSaf.DispatchObject;
 
@@ -37,7 +39,8 @@
 
   private final JavaDispatch javaDispatch;
 
-  private final int scriptObject;
+  // TODO(jat): remove these references
+  // private final int scriptObject;
 
   /**
    * This constructor initializes as the static dispatcher, which handles only
@@ -46,10 +49,10 @@
    * @param cl this class's classLoader
    * @param aScriptObject the execution iframe's window
    */
-  WebKitDispatchAdapter(CompilingClassLoader cl, int scriptObject) {
+  WebKitDispatchAdapter(CompilingClassLoader cl) {
     javaDispatch = new JavaDispatchImpl(cl);
     this.classLoader = cl;
-    this.scriptObject = scriptObject;
+    // this.scriptObject = scriptObject;
   }
 
   /**
@@ -59,10 +62,10 @@
    * @param aScriptObject the execution iframe's window
    * @param target the object being wrapped as an IDispatch
    */
-  WebKitDispatchAdapter(CompilingClassLoader cl, int scriptObject, Object target) {
+  WebKitDispatchAdapter(CompilingClassLoader cl, Object target) {
     javaDispatch = new JavaDispatchImpl(cl, target);
     this.classLoader = cl;
-    this.scriptObject = scriptObject;
+    // this.scriptObject = scriptObject;
   }
 
   public int getField(String name) {
@@ -72,14 +75,18 @@
     }
     if (javaDispatch.isField(dispId)) {
       Field field = javaDispatch.getField(dispId);
-      return SwtWebKitGlue.convertObjectToJSVal(scriptObject, classLoader,
-          field.getType(), javaDispatch.getFieldValue(dispId));
+      JsValueSaf jsValue = new JsValueSaf();
+      JsValueGlue.set(jsValue, classLoader, field.getType(),
+          javaDispatch.getFieldValue(dispId));
+      int jsval = jsValue.getJsValue();
+      LowLevelSaf.gcLock(jsval);
+      return jsval;
     } else {
       Method method = javaDispatch.getMethod(dispId);
       DispatchMethod dispMethod;
       dispMethod = (DispatchMethod) classLoader.getMethodDispatch(method);
       if (dispMethod == null) {
-        dispMethod = new MethodDispatch(classLoader, method, scriptObject);
+        dispMethod = new MethodDispatch(classLoader, method);
         classLoader.putMethodDispatch(method, dispMethod);
       }
       return LowLevelSaf.wrapFunction(method.toString(), dispMethod);
@@ -91,6 +98,7 @@
   }
 
   public void setField(String name, int value) {
+    JsValue jsValue = new JsValueSaf(value);
     int dispId = classLoader.getDispId(name);
     if (dispId < 0) {
       // TODO: expandos?
@@ -100,7 +108,7 @@
       throw new RuntimeException("Cannot reassign method " + name);
     }
     Field field = javaDispatch.getField(dispId);
-    Object val = SwtWebKitGlue.convertJSValToObject(field.getType(), value);
+    Object val = JsValueGlue.get(jsValue, field.getType(), "setField");
     javaDispatch.setFieldValue(dispId, val);
   }
 }
diff --git a/dev/windows/src/com/google/gwt/dev/shell/ie/BrowserWidgetIE6.java b/dev/windows/src/com/google/gwt/dev/shell/ie/BrowserWidgetIE6.java
index 8cefd40..70213ad 100644
--- a/dev/windows/src/com/google/gwt/dev/shell/ie/BrowserWidgetIE6.java
+++ b/dev/windows/src/com/google/gwt/dev/shell/ie/BrowserWidgetIE6.java
@@ -20,6 +20,7 @@
 import com.google.gwt.dev.shell.BrowserWidgetHost;
 import com.google.gwt.dev.shell.ModuleSpaceHost;
 
+import org.eclipse.swt.SWTException;
 import org.eclipse.swt.internal.ole.win32.COM;
 import org.eclipse.swt.internal.ole.win32.IDispatch;
 import org.eclipse.swt.ole.win32.Variant;
@@ -48,7 +49,7 @@
       try {
         if (moduleName == null) {
           // Indicates the page is being unloaded.
-          //
+          // TODO(jat): add support for unloading only a single module
           onPageUnload();
           return true;
         }
@@ -62,7 +63,7 @@
         return true;
       } catch (Throwable e) {
         // We do catch Throwable intentionally because there are a ton of things
-        // that can go wrong trying to load a module, including Error-dervied
+        // that can go wrong trying to load a module, including Error-derived
         // things like NoClassDefFoundError.
         // 
         getHost().getLogger().log(TreeLogger.ERROR,
@@ -95,18 +96,18 @@
         return new Variant(toString());
       } else if (dispId == 1) {
         if ((flags & COM.DISPATCH_METHOD) != 0) {
-          // Invoke
-          Object[] javaParams = SwtOleGlue.convertVariantsToObjects(
-              new Class[] {
-                  IDispatch.class, String.class, String.class, String.class},
-              params, "Calling method 'gwtOnLoad'");
+          try {
+            IDispatch frameWnd = (params[0].getType() == COM.VT_DISPATCH)
+                ? params[0].getDispatch() : null;
+            String moduleName = (params[1].getType() == COM.VT_BSTR)
+                ? params[1].getString() : null;
+            boolean success = gwtOnLoad(frameWnd, moduleName);
 
-          IDispatch frameWnd = (IDispatch) javaParams[0];
-          String moduleName = (String) javaParams[1];
-          boolean success = gwtOnLoad(frameWnd, moduleName);
-
-          // boolean return type
-          return new Variant(success);
+            // boolean return type
+            return new Variant(success);
+          } catch (SWTException e) {
+            throw new HResultException(COM.E_INVALIDARG);
+          }
         } else if ((flags & COM.DISPATCH_PROPERTYGET) != 0) {
           // property get on the method itself
           try {
diff --git a/dev/windows/src/com/google/gwt/dev/shell/ie/HandleIE6.java b/dev/windows/src/com/google/gwt/dev/shell/ie/HandleIE6.java
deleted file mode 100644
index 95784bd..0000000
--- a/dev/windows/src/com/google/gwt/dev/shell/ie/HandleIE6.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2006 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.shell.ie;
-
-import com.google.gwt.dev.shell.Handle;
-
-import org.eclipse.swt.internal.ole.win32.IDispatch;
-
-class HandleIE6 extends Handle {
-
-  static {
-    // put myself in Handle's sImpl field
-    new HandleIE6();
-  }
-
-  public static Object createHandle(Class type, int ptr) {
-    return Handle.createHandle(type, ptr);
-  }
-
-  public static IDispatch getDispatchFromHandle(Object handle) {
-    int ptr = Handle.getPtrFromHandle(handle);
-    return new IDispatch(ptr);
-  }
-
-  /**
-   * Not instantiable.
-   */
-  private HandleIE6() {
-  }
-
-  protected void lockPtr(int ptr) {
-    SwtOleGlue.addRefInt(ptr);
-  }
-
-  protected void unlockPtr(int ptr) {
-    SwtOleGlue.releaseInt(ptr);
-  }
-
-}
diff --git a/dev/windows/src/com/google/gwt/dev/shell/ie/IDispatchImpl.java b/dev/windows/src/com/google/gwt/dev/shell/ie/IDispatchImpl.java
index 6b4abb4..38c8220 100644
--- a/dev/windows/src/com/google/gwt/dev/shell/ie/IDispatchImpl.java
+++ b/dev/windows/src/com/google/gwt/dev/shell/ie/IDispatchImpl.java
@@ -79,7 +79,25 @@
      */
     public void fillExcepInfo(int pExcepInfo) {
       if (hr == COM.DISP_E_EXCEPTION) {
-        SwtOleGlue.setEXCEPINFO(pExcepInfo, hr, source, getMessage(), 0);
+        String desc = getMessage();
+        // 0: wCode (size = 2)
+        // 4: bstrSource (size = 4)
+        // 8: bstrDescription (size = 4)
+        // 28: scode (size = 4)
+        //
+        OS.MoveMemory(pExcepInfo + 0, new short[] {(short) hr}, 2);
+
+        if (source != null) {
+          int bstrSource = SwtOleGlue.sysAllocString(source);
+          OS.MoveMemory(pExcepInfo + 4, new int[] {bstrSource}, 4);
+        }
+
+        if (desc != null) {
+          int bstrDesc = SwtOleGlue.sysAllocString(desc);
+          OS.MoveMemory(pExcepInfo + 8, new int[] {bstrDesc}, 4);
+        }
+
+        OS.MoveMemory(pExcepInfo + 28, new int[] {0}, 4);
       }
     }
 
@@ -97,6 +115,7 @@
   protected static Variant callMethod(CompilingClassLoader cl, Object jthis,
       Variant[] params, Method method) throws InvocationTargetException,
       HResultException {
+    // TODO: make sure we have enough args! It's okay if there are too many.
     Object[] javaParams = SwtOleGlue.convertVariantsToObjects(
         method.getParameterTypes(), params, "Calling method '"
             + method.getName() + "'");
diff --git a/dev/windows/src/com/google/gwt/dev/shell/ie/IDispatchProxy.java b/dev/windows/src/com/google/gwt/dev/shell/ie/IDispatchProxy.java
index 5accc16..916609f 100644
--- a/dev/windows/src/com/google/gwt/dev/shell/ie/IDispatchProxy.java
+++ b/dev/windows/src/com/google/gwt/dev/shell/ie/IDispatchProxy.java
@@ -18,6 +18,7 @@
 import com.google.gwt.dev.shell.CompilingClassLoader;
 import com.google.gwt.dev.shell.JavaDispatch;
 import com.google.gwt.dev.shell.JavaDispatchImpl;
+import com.google.gwt.dev.shell.JsValueGlue;
 import com.google.gwt.dev.shell.LowLevel;
 
 import org.eclipse.swt.internal.ole.win32.COM;
@@ -132,13 +133,21 @@
         // Handle specially.
         //
         return new Variant(myGlobalRef);
-      } else if (dispId >= 0) {
+      } else if (dispId == 0) {
+        if ((flags & COM.DISPATCH_METHOD) != 0) {
+          // implicit call -- "m()"
+          // not supported -- fall through to unsupported failure
+        } else if ((flags & COM.DISPATCH_PROPERTYGET) != 0) {
+          // implicit toString -- "'foo' + m"
+          return new Variant(getTarget().toString());
+        }
+
+      } else if (dispId > 0) {
         if (javaDispatch.isMethod(dispId)) {
           Method method = javaDispatch.getMethod(dispId);
           if ((flags & COM.DISPATCH_METHOD) != 0) {
             // This is a method call.
-            return callMethod(classLoader, javaDispatch.getTarget(), params,
-                method);
+            return callMethod(classLoader, getTarget(), params, method);
           } else if (flags == COM.DISPATCH_PROPERTYGET) {
             // The function is being accessed as a property.
             IDispatchImpl funcObj = new MethodDispatch(classLoader, method);
@@ -152,9 +161,9 @@
             return SwtOleGlue.convertObjectToVariant(classLoader,
                 field.getType(), javaDispatch.getFieldValue(dispId));
           } else if ((flags & (COM.DISPATCH_PROPERTYPUT | COM.DISPATCH_PROPERTYPUTREF)) != 0) {
-            javaDispatch.setFieldValue(dispId,
-                SwtOleGlue.convertVariantToObject(field.getType(), params[0],
-                    "Setting field '" + field.getName() + "'"));
+            javaDispatch.setFieldValue(dispId, JsValueGlue.get(new JsValueIE6(
+                params[0]), field.getType(), "Setting field '"
+                + field.getName() + "'"));
             return new Variant();
           }
         }
diff --git a/dev/windows/src/com/google/gwt/dev/shell/ie/JsValueIE6.java b/dev/windows/src/com/google/gwt/dev/shell/ie/JsValueIE6.java
new file mode 100644
index 0000000..4474176
--- /dev/null
+++ b/dev/windows/src/com/google/gwt/dev/shell/ie/JsValueIE6.java
@@ -0,0 +1,475 @@
+/*
+ * Copyright 2006 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.shell.ie;
+
+import com.google.gwt.dev.shell.CompilingClassLoader;
+import com.google.gwt.dev.shell.JsValue;
+import com.google.gwt.dev.shell.LowLevel;
+
+import org.eclipse.swt.internal.ole.win32.COM;
+import org.eclipse.swt.internal.ole.win32.DISPPARAMS;
+import org.eclipse.swt.internal.ole.win32.EXCEPINFO;
+import org.eclipse.swt.internal.ole.win32.GUID;
+import org.eclipse.swt.internal.ole.win32.IDispatch;
+import org.eclipse.swt.internal.ole.win32.IUnknown;
+import org.eclipse.swt.internal.win32.OS;
+import org.eclipse.swt.ole.win32.OleAutomation;
+import org.eclipse.swt.ole.win32.Variant;
+
+/**
+ * Represents an IE JavaScript value.
+ */
+public class JsValueIE6 extends JsValue {
+
+  private static class JsCleanupIE6 implements JsCleanup {
+    private Variant variant;
+
+    public JsCleanupIE6(Variant variant) {
+      this.variant = variant;
+    }
+
+    public void doCleanup() {
+      variant.dispose();
+    }
+  }
+
+  private static Variant maybeCopyVariant(Variant variant) {
+    if (variant == null) {
+      return new Variant();
+    }
+    switch (variant.getType()) {
+      case COM.VT_DISPATCH: {
+        IDispatch dispatch = variant.getDispatch();
+        dispatch.AddRef();
+        return new Variant(dispatch);
+      }
+      case COM.VT_UNKNOWN: {
+        IUnknown unknown = variant.getUnknown();
+        unknown.AddRef();
+        return new Variant(unknown);
+      }
+    }
+    return variant;
+  }
+
+  // a null variant means the JsValue is undefined (void)
+  private Variant variant;
+
+  /**
+   * Create a null JsValue.
+   */
+  public JsValueIE6() {
+    this.variant = null;
+  }
+
+  /**
+   * Create a JsValue given a variant.
+   * 
+   * @param variant JS value
+   */
+  public JsValueIE6(Variant variant) {
+    this.variant = maybeCopyVariant(variant);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#getBoolean()
+   */
+  public boolean getBoolean() {
+    return variant.getBoolean();
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#getInt()
+   */
+  public int getInt() {
+    return variant.getInt();
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#getNumber()
+   */
+  public double getNumber() {
+    return variant.getDouble();
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#getString()
+   */
+  public String getString() {
+    return variant.getString();
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#getTypeString()
+   */
+  public String getTypeString() {
+    switch (variant.getType()) {
+      case COM.VT_BOOL:
+        return "boolean";
+      case COM.VT_I1:
+      case COM.VT_I2:
+      case COM.VT_I4:
+      case COM.VT_I8:
+      case COM.VT_UI1:
+      case COM.VT_UI2:
+      case COM.VT_UI4:
+      case COM.VT_R4:
+      case COM.VT_R8:
+        return "number";
+      case COM.VT_BSTR:
+        return "string";
+      case COM.VT_EMPTY:
+        return "undefined";
+      case COM.VT_NULL:
+        return "null";
+      case COM.VT_DISPATCH:
+        return isWrappedJavaObject() ? "Java Object" : "JavaScript object";
+      default:
+        return "unexpected Variant type";
+    }
+  }
+
+  /**
+   * Return the underlying Variant object. PLATFORM-SPECIFIC.
+   */
+  public Variant getVariant() {
+    return maybeCopyVariant(variant);
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#getWrappedJavaObject()
+   */
+  public Object getWrappedJavaObject() {
+    return tryToUnwrapWrappedJavaObject();
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#isBoolean()
+   */
+  public boolean isBoolean() {
+    return variant != null && variant.getType() == COM.VT_BOOL;
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#isInt()
+   */
+  public boolean isInt() {
+    if (variant == null) {
+      return false;
+    }
+    switch (variant.getType()) {
+      case COM.VT_I1:
+      case COM.VT_I2:
+      case COM.VT_I4:
+      case COM.VT_UI1:
+      case COM.VT_UI2:
+        // note that VT_UI4 is excluded since it may not fit in an int
+        return true;
+      default:
+        return false;
+    }
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#isJavaScriptObject()
+   */
+  public boolean isJavaScriptObject() {
+    if (variant == null) {
+      return false;
+    }
+    if (variant.getType() != COM.VT_DISPATCH) {
+      return false;
+    }
+    return tryToUnwrapWrappedJavaObject() == null;
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#isNull()
+   */
+  public boolean isNull() {
+    return variant != null && variant.getType() == COM.VT_NULL;
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#isNumber()
+   */
+  public boolean isNumber() {
+    if (variant == null) {
+      return false;
+    }
+    switch (variant.getType()) {
+      case COM.VT_I1:
+      case COM.VT_I2:
+      case COM.VT_I4:
+      case COM.VT_I8:
+      case COM.VT_UI1:
+      case COM.VT_UI2:
+      case COM.VT_UI4:
+      case COM.VT_R4:
+      case COM.VT_R8:
+        return true;
+      default:
+        return false;
+    }
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#isString()
+   */
+  public boolean isString() {
+    if (variant == null) {
+      return false;
+    }
+    if (variant.getType() == COM.VT_BSTR) {
+      return true;
+    }
+    if (variant.getType() != COM.VT_DISPATCH) {
+      return false;
+    }
+
+    // see if it's a String wrapper object
+    OleAutomation auto = null;
+    Variant result = null;
+    try {
+      auto = new OleAutomation(variant.getDispatch());
+      int[] ids = auto.getIDsOfNames(new String[] {"valueOf"});
+      if (ids == null) {
+        return false;
+      }
+      result = auto.invoke(ids[0]);
+      return result.getType() == COM.VT_BSTR;
+    } finally {
+      if (auto != null) {
+        auto.dispose();
+      }
+      if (result != null) {
+        result.dispose();
+      }
+    }
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#isUndefined()
+   */
+  public boolean isUndefined() {
+    return variant == null || variant.getType() == COM.VT_EMPTY;
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#isWrappedJavaObject()
+   */
+  public boolean isWrappedJavaObject() {
+    if (variant == null) {
+      return false;
+    }
+    if (variant.getType() != COM.VT_DISPATCH) {
+      return false;
+    }
+    return tryToUnwrapWrappedJavaObject() != null;
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#setBoolean(boolean)
+   */
+  public void setBoolean(boolean val) {
+    setVariant(new Variant(val));
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#setByte(byte)
+   */
+  public void setByte(byte val) {
+    setVariant(new Variant(val));
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#setChar(char)
+   */
+  public void setChar(char val) {
+    setVariant(new Variant(val));
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#setDouble(double)
+   */
+  public void setDouble(double val) {
+    setVariant(new Variant(val));
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#setInt(int)
+   */
+  public void setInt(int val) {
+    setVariant(new Variant(val));
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#setNull()
+   */
+  public void setNull() {
+    setVariant(new Variant(0, COM.VT_NULL));
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#setShort(short)
+   */
+  public void setShort(short val) {
+    setVariant(new Variant(val));
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#setString(java.lang.String)
+   */
+  public void setString(String val) {
+    setVariant(new Variant(val));
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#setUndefined()
+   */
+  public void setUndefined() {
+    setVariant(null);
+  }
+
+  public void setValue(JsValue other) {
+    setVariant(maybeCopyVariant(((JsValueIE6) other).variant));
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#setWrappedJavaObject(com.google.gwt.dev.shell.CompilingClassLoader,
+   *      java.lang.Object)
+   */
+  public void setWrappedJavaObject(CompilingClassLoader cl, Object obj) {
+    if (obj == null) {
+      setNull();
+      return;
+    }
+    IDispatch disp = new IDispatch(new IDispatchProxy(cl, obj).getAddress());
+    disp.AddRef();
+    setVariant(new Variant(disp));
+  }
+
+  /*
+   * (non-Javadoc)
+   * 
+   * @see com.google.gwt.dev.shell.JsValue#createCleanupObject()
+   */
+  protected JsCleanup createCleanupObject() {
+    return new JsCleanupIE6(variant);
+  }
+
+  /**
+   * Reset the underlying variant, freeing the old one if necessary.
+   * 
+   * @param val the new Variant to store
+   */
+  protected void setVariant(Variant val) {
+    if (variant != null) {
+      variant.dispose();
+    }
+    variant = val;
+  }
+  
+  private Object tryToUnwrapWrappedJavaObject() {
+    /*
+     * This implementation copied from OleAutomation.invoke(). We used to have a
+     * varArg.getAutomation().invoke() implementation, but it turns out the
+     * querying for typeInfo that occurs in the OleAutomation(IDispatch)
+     * constructor will cause a VM crash on some kinds of JavaScript objects,
+     * such as the window.alert function. So we do it by hand.
+     */
+    IDispatch dispatch = variant.getDispatch();
+    Variant result = new Variant();
+    int pVarResultAddress = 0;
+    int globalRef = 0;
+    try {
+      pVarResultAddress = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT,
+          Variant.sizeof);
+      int[] pArgErr = new int[1];
+      int hr = dispatch.Invoke(IDispatchProxy.DISPID_MAGIC_GETGLOBALREF,
+          new GUID(), COM.LOCALE_USER_DEFAULT, COM.DISPATCH_METHOD,
+          new DISPPARAMS(), pVarResultAddress, new EXCEPINFO(), pArgErr);
+
+      if (hr >= COM.S_OK) {
+        result = Variant.win32_new(pVarResultAddress);
+        globalRef = result.getInt();
+      }
+      if (globalRef != 0) {
+        // This is really a Java object being passed back via an IDispatchProxy.
+        IDispatchProxy proxy = (IDispatchProxy) LowLevel.objFromGlobalRefInt(globalRef);
+        return proxy.getTarget();
+      }
+      return null;
+    } finally {
+      if (result != null) {
+        result.dispose();
+      }
+      if (pVarResultAddress != 0) {
+        COM.VariantClear(pVarResultAddress);
+        OS.GlobalFree(pVarResultAddress);
+      }
+    }
+  }
+
+}
diff --git a/dev/windows/src/com/google/gwt/dev/shell/ie/MethodDispatch.java b/dev/windows/src/com/google/gwt/dev/shell/ie/MethodDispatch.java
index d84aaa8..84cc999 100644
--- a/dev/windows/src/com/google/gwt/dev/shell/ie/MethodDispatch.java
+++ b/dev/windows/src/com/google/gwt/dev/shell/ie/MethodDispatch.java
@@ -16,6 +16,7 @@
 package com.google.gwt.dev.shell.ie;
 
 import com.google.gwt.dev.shell.CompilingClassLoader;
+import com.google.gwt.dev.shell.JsValueGlue;
 
 import org.eclipse.swt.internal.ole.win32.COM;
 import org.eclipse.swt.ole.win32.Variant;
@@ -103,13 +104,17 @@
            * First param must be a this object of the correct type (for instance
            * methods). If method is static, it can be null.
            */
-          Object jthis = SwtOleGlue.convertVariantToObject(
-              method.getDeclaringClass(), params[0], "this");
+          Object jthis = JsValueGlue.get(new JsValueIE6(params[0]),
+              method.getDeclaringClass(), "this");
           Variant[] otherParams = new Variant[params.length - 1];
           System.arraycopy(params, 1, otherParams, 0, otherParams.length);
           return callMethod(classLoader, jthis, otherParams, method);
         }
         break;
+      case IDispatchProxy.DISPID_MAGIC_GETGLOBALREF:
+        // We are NOT in fact a "wrapped Java Object", but we don't want to
+        // throw an exception for being asked.
+        return new Variant(0);
       default:
         // The specified member id is out of range.
         throw new HResultException(COM.DISP_E_MEMBERNOTFOUND);
diff --git a/dev/windows/src/com/google/gwt/dev/shell/ie/ModuleSpaceIE6.java b/dev/windows/src/com/google/gwt/dev/shell/ie/ModuleSpaceIE6.java
index 984e8b0..f841b25 100644
--- a/dev/windows/src/com/google/gwt/dev/shell/ie/ModuleSpaceIE6.java
+++ b/dev/windows/src/com/google/gwt/dev/shell/ie/ModuleSpaceIE6.java
@@ -15,14 +15,12 @@
  */
 package com.google.gwt.dev.shell.ie;
 
-import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.dev.shell.JsValue;
 import com.google.gwt.dev.shell.ModuleSpace;
 import com.google.gwt.dev.shell.ModuleSpaceHost;
 import com.google.gwt.dev.shell.ie.IDispatchImpl.HResultException;
 
-import org.eclipse.swt.internal.ole.win32.COM;
 import org.eclipse.swt.internal.ole.win32.IDispatch;
-import org.eclipse.swt.ole.win32.OLE;
 import org.eclipse.swt.ole.win32.OleAutomation;
 import org.eclipse.swt.ole.win32.Variant;
 import org.eclipse.swt.widgets.Display;
@@ -37,6 +35,7 @@
   private static int CODE(int hresult) {
     return hresult & 0xFFFF;
   }
+
   // CHECKSTYLE_ON
 
   private Variant staticDispatch;
@@ -52,7 +51,7 @@
   public ModuleSpaceIE6(ModuleSpaceHost host, IDispatch scriptFrameWindow) {
     super(host);
 
-    window = SwtOleGlue.wrapIDispatch(scriptFrameWindow);
+    window = new OleAutomation(scriptFrameWindow);
   }
 
   public void createNative(String file, int line, String jsniSignature,
@@ -63,7 +62,7 @@
     String newScript = createNativeMethodInjector(jsniSignature, paramNames, js);
     try {
       // TODO: somehow insert file/line info into the script
-      Variant result = execute(window, newScript);
+      Variant result = execute(newScript);
       if (result != null) {
         result.dispose();
       }
@@ -124,237 +123,6 @@
         getIsolatedClassLoader(), name, message));
   }
 
-  public boolean invokeNativeBoolean(String name, Object jthis, Class[] types,
-      Object[] args) {
-    Variant result = null;
-    try {
-      result = invokeNative(name, jthis, types, args, true);
-      if (result.getType() == COM.VT_EMPTY && isExceptionActive()) {
-        return false;
-      }
-      return result.getBoolean();
-    } finally {
-      if (result != null) {
-        result.dispose();
-      }
-    }
-  }
-
-  public byte invokeNativeByte(String name, Object jthis, Class[] types,
-      Object[] args) {
-    Variant result = null;
-    try {
-      result = invokeNative(name, jthis, types, args, true);
-      if (result.getType() == COM.VT_EMPTY && isExceptionActive()) {
-        return 0;
-      }
-      return (byte) result.getInt();
-    } finally {
-      if (result != null) {
-        result.dispose();
-      }
-    }
-  }
-
-  public char invokeNativeChar(String name, Object jthis, Class[] types,
-      Object[] args) {
-    Variant result = null;
-    try {
-      result = invokeNative(name, jthis, types, args, true);
-      if (result.getType() == COM.VT_EMPTY && isExceptionActive()) {
-        return 0;
-      }
-      return (char) result.getInt();
-    } finally {
-      if (result != null) {
-        result.dispose();
-      }
-    }
-  }
-
-  public double invokeNativeDouble(String name, Object jthis, Class[] types,
-      Object[] args) {
-    Variant result = null;
-    try {
-      result = invokeNative(name, jthis, types, args, true);
-      if (result.getType() == COM.VT_EMPTY && isExceptionActive()) {
-        return 0;
-      }
-      return result.getDouble();
-    } finally {
-      if (result != null) {
-        result.dispose();
-      }
-    }
-  }
-
-  public float invokeNativeFloat(String name, Object jthis, Class[] types,
-      Object[] args) {
-    Variant result = null;
-    try {
-      result = invokeNative(name, jthis, types, args, true);
-      if (result.getType() == COM.VT_EMPTY && isExceptionActive()) {
-        return 0;
-      }
-      return result.getFloat();
-    } finally {
-      if (result != null) {
-        result.dispose();
-      }
-    }
-  }
-
-  public Object invokeNativeHandle(String name, Object jthis, Class returnType,
-      Class[] types, Object[] args) {
-    Variant result = null;
-    try {
-      result = invokeNative(name, jthis, types, args, true);
-      if (result.getType() == COM.VT_EMPTY && isExceptionActive()) {
-        return null;
-      }
-
-      // NULL is a legal return value when expecting a handle
-      if (result.getType() == COM.VT_NULL) {
-        return null;
-      }
-
-      return HandleIE6.createHandle(returnType,
-          result.getDispatch().getAddress());
-    } finally {
-      if (result != null) {
-        result.dispose();
-      }
-    }
-  }
-
-  public int invokeNativeInt(String name, Object jthis, Class[] types,
-      Object[] args) {
-    Variant result = null;
-    try {
-      result = invokeNative(name, jthis, types, args, true);
-      if (result.getType() == COM.VT_EMPTY && isExceptionActive()) {
-        return 0;
-      }
-      return result.getInt();
-    } finally {
-      if (result != null) {
-        result.dispose();
-      }
-    }
-  }
-
-  public long invokeNativeLong(String name, Object jthis, Class[] types,
-      Object[] args) {
-    Variant result = null;
-    try {
-      result = invokeNative(name, jthis, types, args, true);
-      if (result.getType() == COM.VT_EMPTY && isExceptionActive()) {
-        return 0;
-      }
-      return (long) result.getDouble();
-    } finally {
-      if (result != null) {
-        result.dispose();
-      }
-    }
-  }
-
-  public Object invokeNativeObject(String name, Object jthis, Class[] types,
-      Object[] args) {
-    Variant result = null;
-    try {
-      result = invokeNative(name, jthis, types, args, true);
-      if (result.getType() == COM.VT_EMPTY && isExceptionActive()) {
-        return null;
-      } else {
-        return SwtOleGlue.convertVariantToObject(Object.class, result,
-            "Returning from method '" + name + "'");
-      }
-    } finally {
-      if (result != null) {
-        result.dispose();
-      }
-    }
-  }
-
-  public short invokeNativeShort(String name, Object jthis, Class[] types,
-      Object[] args) {
-    Variant result = null;
-    try {
-      result = invokeNative(name, jthis, types, args, true);
-      if (result.getType() == COM.VT_EMPTY && isExceptionActive()) {
-        return 0;
-      }
-      return result.getShort();
-    } finally {
-      if (result != null) {
-        result.dispose();
-      }
-    }
-  }
-
-  public String invokeNativeString(String name, Object jthis, Class[] types,
-      Object[] args) {
-    Variant result = null;
-    try {
-      result = invokeNative(name, jthis, types, args, true);
-      if (result.getType() == COM.VT_EMPTY && isExceptionActive()) {
-        return null;
-      } else if (result.getType() == COM.VT_NULL) {
-        // An explicit null return value.
-        //
-        return null;
-      }
-      return result.getString();
-    } finally {
-      if (result != null) {
-        result.dispose();
-      }
-    }
-  }
-
-  public void invokeNativeVoid(String name, Object jthis, Class[] types,
-      Object[] args) {
-    Variant result = null;
-    try {
-      result = invokeNative(name, jthis, types, args, false);
-    } finally {
-      if (result != null) {
-        result.dispose();
-      }
-    }
-  }
-
-  protected void initializeStaticDispatcher() {
-    staticDispatchProxy = new IDispatchProxy(getIsolatedClassLoader());
-    IDispatch staticDisp = new IDispatch(staticDispatchProxy.getAddress());
-    staticDisp.AddRef();
-    this.staticDispatch = new Variant(staticDisp);
-
-    // Define the static dispatcher for use by JavaScript.
-    //
-    createNative("initializeStaticDispatcher", 0, "__defineStatic",
-        new String[] {"__arg0"}, "window.__static = __arg0;");
-    invokeNativeVoid("__defineStatic", null, new Class[] {Variant.class},
-        new Object[] {this.staticDispatch});
-  }
-
-  private Variant execute(OleAutomation window, String code) {
-    int[] dispIds = window.getIDsOfNames(new String[] {"execScript", "code"});
-    Variant[] vArgs = new Variant[1];
-    vArgs[0] = new Variant(code);
-    int[] namedArgs = new int[1];
-    namedArgs[0] = dispIds[1];
-    Variant result = window.invoke(dispIds[0], vArgs, namedArgs);
-    vArgs[0].dispose();
-    if (result == null) {
-      String lastError = window.getLastError();
-      throw new RuntimeException("Error (" + lastError
-          + ") executing JavaScript:\n" + code);
-    }
-    return result;
-  }
-
   /**
    * Invokes a native javascript function.
    * 
@@ -364,12 +132,8 @@
    * @param args the arguments to be passed
    * @return the return value as a Variant.
    */
-  private Variant invokeNative(String name, Object jthis, Class[] types,
-      Object[] args, boolean returnsValue) {
-    // Every time a native method is invoked, release any enqueued COM objects.
-    //
-    HandleIE6.releaseQueuedPtrs();
-
+  protected JsValue doInvoke(String name, Object jthis, Class[] types,
+      Object[] args) {
     OleAutomation funcObj = null;
     Variant funcObjVar = null;
     Variant[] vArgs = null;
@@ -378,7 +142,9 @@
       //
       int len = args.length;
       vArgs = new Variant[len + 1];
-      vArgs[0] = SwtOleGlue.wrapObjectAsVariant(getIsolatedClassLoader(), jthis);
+      JsValueIE6 jsValue = new JsValueIE6();
+      jsValue.setWrappedJavaObject(getIsolatedClassLoader(), jthis);
+      vArgs[0] = jsValue.getVariant();
 
       for (int i = 0; i < len; ++i) {
         vArgs[i + 1] = SwtOleGlue.convertObjectToVariant(
@@ -400,31 +166,13 @@
       // Invoke it and return the result.
       //
       Variant result = funcObj.invoke(callDispId, vArgs);
-      if (!isExceptionActive()) {
-        if (returnsValue) {
-          if (result.getType() == OLE.VT_EMPTY) {
-            // Function was required to return something.
-            //
-            throw new RuntimeException(
-                "JavaScript method '"
-                    + name
-                    + "' returned 'undefined'. This can happen either because of a "
-                    + "missing return statement, or explicitly returning a value "
-                    + "of 'undefined' (e.g. 'return element[nonexistent property]')");
-          }
-        } else {
-          if (result.getType() != OLE.VT_EMPTY) {
-            // Function is not allowed to return something.
-            //
-            getLogger().log(
-                TreeLogger.WARN,
-                "JavaScript method '" + name
-                    + "' is not supposed to return a value", null);
-          }
+      try {
+        if (!isExceptionActive()) {
+          return new JsValueIE6(result);
         }
-        return result;
+      } finally {
+        result.dispose();
       }
-      result.dispose();
 
       /*
        * The stack trace on the stored exception will not be very useful due to
@@ -434,7 +182,6 @@
       RuntimeException thrown = takeJavaException();
       thrown.fillInStackTrace();
       throw thrown;
-
     } finally {
       // We allocated variants for all arguments, so we must dispose them all.
       //
@@ -453,4 +200,34 @@
       }
     }
   }
+
+  protected void initializeStaticDispatcher() {
+    staticDispatchProxy = new IDispatchProxy(getIsolatedClassLoader());
+    IDispatch staticDisp = new IDispatch(staticDispatchProxy.getAddress());
+    staticDisp.AddRef();
+    this.staticDispatch = new Variant(staticDisp);
+
+    // Define the static dispatcher for use by JavaScript.
+    //
+    createNative("initializeStaticDispatcher", 0, "__defineStatic",
+        new String[] {"__arg0"}, "window.__static = __arg0;");
+    invokeNativeVoid("__defineStatic", null, new Class[] {Variant.class},
+        new Object[] {this.staticDispatch});
+  }
+
+  private Variant execute(String code) {
+    int[] dispIds = window.getIDsOfNames(new String[] {"execScript", "code"});
+    Variant[] vArgs = new Variant[1];
+    vArgs[0] = new Variant(code);
+    int[] namedArgs = new int[1];
+    namedArgs[0] = dispIds[1];
+    Variant result = window.invoke(dispIds[0], vArgs, namedArgs);
+    vArgs[0].dispose();
+    if (result == null) {
+      String lastError = window.getLastError();
+      throw new RuntimeException("Error (" + lastError
+          + ") executing JavaScript:\n" + code);
+    }
+    return result;
+  }
 }
diff --git a/dev/windows/src/com/google/gwt/dev/shell/ie/OS2.java b/dev/windows/src/com/google/gwt/dev/shell/ie/OS2.java
deleted file mode 100644
index f65f872..0000000
--- a/dev/windows/src/com/google/gwt/dev/shell/ie/OS2.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2006 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.shell.ie;
-
-/**
- * Windows constants not defined in {@link org.eclipse.swt.internal.win32.OS}.
- */
-public class OS2 {
-
-  public static final int WM_NCPAINT = 0x0085;
-  public static final int WM_NCLBUTTONUP = 0x00A2;
-
-  public static final int SWP_FRAMECHANGED = 0x0020;
-
-  public static final int LR_DEFAULTCOLOR = 0x0000;
-  public static final int LR_MONOCHROME = 0x0001;
-  public static final int LR_COLOR = 0x0002;
-  public static final int LR_COPYRETURNORG = 0x0004;
-  public static final int LR_COPYDELETEORG = 0x0008;
-  public static final int LR_LOADFROMFILE = 0x0010;
-  public static final int LR_LOADTRANSPARENT = 0x0020;
-  public static final int LR_DEFAULTSIZE = 0x0040;
-  public static final int LR_VGACOLOR = 0x0080;
-  public static final int LR_LOADMAP3DCOLORS = 0x1000;
-  public static final int LR_CREATEDIBSECTION = 0x2000;
-  public static final int LR_COPYFROMRESOURCE = 0x4000;
-  public static final int LR_SHARED = 0x8000;
-
-  public static final int DCX_WINDOW = 0x0001;
-  public static final int DCX_CACHE = 0x0002;
-  public static final int DCX_NORESETATTRS = 0x0004;
-  public static final int DCX_CLIPCHILDREN = 0x0008;
-  public static final int DCX_CLIPSIBLINGS = 0x0010;
-  public static final int DCX_PARENTCLIP = 0x0020;
-  public static final int DCX_EXCLUDERGN = 0x0040;
-  public static final int DCX_INTERSECTRGN = 0x0080;
-  public static final int DCX_EXCLUDEUPDATE = 0x0100;
-  public static final int DCX_INTERSECTUPDATE = 0x0200;
-  public static final int DCX_LOCKWINDOWUPDATE = 0x0400;
-  public static final int DCX_USESTYLE = 0x10000;
-  public static final int DCX_KEEPCLIPRGN = 0x40000;
-
-  public static final int HTERROR = -2;
-  public static final int HTTRANSPARENT = -1;
-  public static final int HTNOWHERE = 0;
-  public static final int HTCLIENT = 1;
-  public static final int HTCAPTION = 2;
-  public static final int HTSYSMENU = 3;
-  public static final int HTGROWBOX = 4;
-  public static final int HTSIZE = 4;
-  public static final int HTMENU = 5;
-  public static final int HTHSCROLL = 6;
-  public static final int HTVSCROLL = 7;
-  public static final int HTMINBUTTON = 8;
-  public static final int HTMAXBUTTON = 9;
-  public static final int HTREDUCE = 8;
-  public static final int HTZOOM = 9;
-  public static final int HTLEFT = 10;
-  public static final int HTSIZEFIRST = 10;
-  public static final int HTRIGHT = 11;
-  public static final int HTTOP = 12;
-  public static final int HTTOPLEFT = 13;
-  public static final int HTTOPRIGHT = 14;
-  public static final int HTBOTTOM = 15;
-  public static final int HTBOTTOMLEFT = 16;
-  public static final int HTBOTTOMRIGHT = 17;
-  public static final int HTSIZELAST = 17;
-  public static final int HTBORDER = 18;
-  public static final int HTOBJECT = 19;
-  public static final int HTCLOSE = 20;
-  public static final int HTHELP = 21;
-}
diff --git a/dev/windows/src/com/google/gwt/dev/shell/ie/SwtOleGlue.java b/dev/windows/src/com/google/gwt/dev/shell/ie/SwtOleGlue.java
index 09f74e3..f5a9378 100644
--- a/dev/windows/src/com/google/gwt/dev/shell/ie/SwtOleGlue.java
+++ b/dev/windows/src/com/google/gwt/dev/shell/ie/SwtOleGlue.java
@@ -16,18 +16,13 @@
 package com.google.gwt.dev.shell.ie;
 
 import com.google.gwt.dev.shell.CompilingClassLoader;
+import com.google.gwt.dev.shell.JsValueGlue;
 import com.google.gwt.dev.shell.LowLevel;
-import com.google.gwt.dev.util.TypeInfo;
 
 import org.eclipse.swt.browser.Browser;
 import org.eclipse.swt.internal.ole.win32.COM;
 import org.eclipse.swt.internal.ole.win32.COMObject;
-import org.eclipse.swt.internal.ole.win32.DISPPARAMS;
-import org.eclipse.swt.internal.ole.win32.EXCEPINFO;
-import org.eclipse.swt.internal.ole.win32.GUID;
-import org.eclipse.swt.internal.ole.win32.IDispatch;
 import org.eclipse.swt.internal.win32.OS;
-import org.eclipse.swt.ole.win32.OleAutomation;
 import org.eclipse.swt.ole.win32.Variant;
 
 /**
@@ -38,63 +33,17 @@
 class SwtOleGlue {
 
   /**
-   * Wrapper for the OS' IUnknown::AddRef().
-   */
-  public static int addRefInt(int iUnknown) {
-    return OS.VtblCall(1, iUnknown);
-  }
-
-  /**
    * Converts a java object to its equivalent variant. A ClassLoader is passed
    * here so that Handles can be manipulated properly.
    */
   public static Variant convertObjectToVariant(CompilingClassLoader cl,
       Class type, Object o) {
-
-    if (o == null) {
-      return new Variant(0, COM.VT_NULL);
-    }
-
-    if (type.equals(String.class)) {
-      return new Variant((String) o);
-    } else if (type.equals(boolean.class)) {
-      return new Variant(((Boolean) o).booleanValue());
-    } else if (type.equals(byte.class)) {
-      return new Variant(((Byte) o).byteValue());
-    } else if (type.equals(short.class)) {
-      return new Variant(((Short) o).shortValue());
-    } else if (type.equals(char.class)) {
-      return new Variant(((Character) o).charValue());
-    } else if (type.equals(int.class)) {
-      return new Variant(((Integer) o).intValue());
-    } else if (type.equals(long.class)) {
-      return new Variant((double) ((Long) o).longValue());
-    } else if (type.equals(float.class)) {
-      return new Variant(((Float) o).floatValue());
-    } else if (type.equals(double.class)) {
-      return new Variant(((Double) o).doubleValue());
-    } else if (type.equals(Variant.class)) {
+    if (type.equals(Variant.class)) {
       return (Variant) o;
     }
-
-    // Handle
-    try {
-      Class jso = Class.forName(HandleIE6.HANDLE_CLASS, true, cl);
-      if (jso.isAssignableFrom(type) && jso.isAssignableFrom(o.getClass())) {
-        // Variant never AddRef's its contents.
-        //
-        IDispatch iDispatch = HandleIE6.getDispatchFromHandle(o);
-        iDispatch.AddRef();
-        return new Variant(iDispatch);
-      }
-    } catch (ClassNotFoundException e) {
-      // Ignore the exception, if we can't find the class then obviously we
-      // don't have to worry about o being one
-    }
-
-    // Fallthrough case: Object.
-    //
-    return wrapObjectAsVariant(cl, o);
+    JsValueIE6 jsValue = new JsValueIE6();
+    JsValueGlue.set(jsValue, cl, type, o);
+    return jsValue.getVariant();
   }
 
   /**
@@ -102,11 +51,11 @@
    */
   public static Object[] convertVariantsToObjects(Class[] argTypes,
       Variant[] varArgs, String msgPrefix) {
-    Object[] javaArgs = new Object[varArgs.length];
+    Object[] javaArgs = new Object[Math.min(varArgs.length, argTypes.length)];
     for (int i = 0; i < javaArgs.length; i++) {
       try {
-        Object javaArg = convertVariantToObject(argTypes[i], varArgs[i],
-            msgPrefix);
+        Object javaArg = JsValueGlue.get(new JsValueIE6(varArgs[i]),
+            argTypes[i], msgPrefix);
         javaArgs[i] = javaArg;
       } catch (IllegalArgumentException e) {
         throw new IllegalArgumentException("Error converting argument "
@@ -118,87 +67,6 @@
   }
 
   /**
-   * Try to convert based on the Java method parameter type. Note that we try to
-   * be more strict than is typical for automation. It would be just make a mess
-   * to let sloppy conversions happen.
-   */
-  public static Object convertVariantToObject(Class paramType, Variant varArg,
-      String msgPrefix) {
-
-    short vt = varArg.getType();
-    if ((vt == COM.VT_NULL) || (vt == COM.VT_EMPTY)) {
-      // It is actually a null reference.
-      return null;
-    }
-
-    if (vt == COM.VT_DISPATCH) {
-      Object translated = translateDispatchArg(paramType, varArg);
-
-      // Make sure that the method we are going to call matches on this
-      // parameter.
-      if (paramType.isAssignableFrom(translated.getClass())) {
-        return translated;
-      }
-    }
-
-    switch (TypeInfo.classifyType(paramType)) {
-      case TypeInfo.TYPE_WRAP_BOOLEAN:
-      case TypeInfo.TYPE_PRIM_BOOLEAN:
-        return Boolean.valueOf(varArg.getBoolean());
-
-      case TypeInfo.TYPE_WRAP_BYTE:
-      case TypeInfo.TYPE_PRIM_BYTE:
-        return new Byte(varArg.getByte());
-
-      case TypeInfo.TYPE_WRAP_CHAR:
-      case TypeInfo.TYPE_PRIM_CHAR:
-        return new Character(varArg.getChar());
-
-      case TypeInfo.TYPE_WRAP_DOUBLE:
-      case TypeInfo.TYPE_PRIM_DOUBLE:
-        return new Double(varArg.getDouble());
-
-      case TypeInfo.TYPE_WRAP_FLOAT:
-      case TypeInfo.TYPE_PRIM_FLOAT:
-        return new Float(varArg.getFloat());
-
-      case TypeInfo.TYPE_WRAP_INT:
-      case TypeInfo.TYPE_PRIM_INT:
-        return new Integer(varArg.getInt());
-
-      case TypeInfo.TYPE_WRAP_LONG:
-      case TypeInfo.TYPE_PRIM_LONG:
-        return new Long((long) varArg.getDouble());
-
-      case TypeInfo.TYPE_WRAP_SHORT:
-      case TypeInfo.TYPE_PRIM_SHORT:
-        return new Short(varArg.getShort());
-
-      case TypeInfo.TYPE_WRAP_STRING:
-        return varArg.getString();
-
-      case TypeInfo.TYPE_USER:
-        if (varArg.getType() == COM.VT_BSTR) {
-          return varArg.getString();
-        }
-        // if it isn't a String, it's an error, break to error
-        break;
-    }
-
-    // Just don't know what do to with this.
-    throw new IllegalArgumentException(msgPrefix + ": Cannot convert to type "
-        + TypeInfo.getSourceRepresentation(paramType, ""));
-  }
-
-  /**
-   * Copies a IDispatchImpl into an (IDispatch**).
-   */
-  public static void copyIDispatchImpl(IDispatchImpl o, int ppDispatch) {
-    OS.MoveMemory(ppDispatch, new int[] {o.getAddress()}, 4);
-    o.AddRef();
-  }
-
-  /**
    * Extracts an array of strings from an (OLECHAR**) type (useful for
    * implementing GetIDsOfNames()).
    */
@@ -234,13 +102,6 @@
   }
 
   /**
-   * Gets the browser's OleAutomation object.
-   */
-  public static OleAutomation getBrowserAutomationObject(Browser browser) {
-    return (OleAutomation) LowLevel.snatchFieldObjectValue(browser, "auto");
-  }
-
-  /**
    * Injects an object into the Browser class that resolves to IE's
    * 'window.external' object.
    */
@@ -283,7 +144,8 @@
           try {
             // Return the 'external' object.
             //
-            copyIDispatchImpl(external, ppDispatch);
+            external.AddRef();
+            OS.MoveMemory(ppDispatch, new int[] {external.getAddress()}, 4);
             return COM.S_OK;
           } catch (Throwable e) {
             e.printStackTrace();
@@ -303,38 +165,6 @@
   }
 
   /**
-   * Wrapper for the OS' IUnknown::Release().
-   */
-  public static int releaseInt(int iUnknown) {
-    return OS.VtblCall(2, iUnknown);
-  }
-
-  /**
-   * Builds an EXCEPINFO structure.
-   */
-  public static void setEXCEPINFO(int pEXCEPINFO, int wcode, String source,
-      String desc, int scode) {
-    // 0: wCode (size = 2)
-    // 4: bstrSource (size = 4)
-    // 8: bstrDescription (size = 4)
-    // 28: scode (size = 4)
-    //
-    OS.MoveMemory(pEXCEPINFO + 0, new short[] {(short) wcode}, 2);
-
-    if (source != null) {
-      int bstrSource = sysAllocString(source);
-      OS.MoveMemory(pEXCEPINFO + 4, new int[] {bstrSource}, 4);
-    }
-
-    if (desc != null) {
-      int bstrDesc = sysAllocString(desc);
-      OS.MoveMemory(pEXCEPINFO + 8, new int[] {bstrDesc}, 4);
-    }
-
-    OS.MoveMemory(pEXCEPINFO + 28, new int[] {scode}, 4);
-  }
-
-  /**
    * Wrapper for the OS' SysAllocString().
    */
   public static int sysAllocString(String s) {
@@ -343,86 +173,5 @@
     s.getChars(0, len, chars, 0);
     return COM.SysAllocString(chars);
   }
-
-  /**
-   * Gets an OleAutomation interface from an IDispatch wrapper.
-   */
-  public static OleAutomation wrapIDispatch(IDispatch disp) {
-    return new OleAutomation(disp);
-  }
-
-  /**
-   * Wraps a Java object in an IDispatchProxy and packages it in a variant.
-   */
-  public static Variant wrapObjectAsVariant(CompilingClassLoader cl, Object o) {
-    if (o == null) {
-      return new Variant(0, COM.VT_NULL);
-    }
-
-    IDispatch disp = new IDispatch(new IDispatchProxy(cl, o).getAddress());
-    disp.AddRef();
-    return new Variant(disp);
-  }
-
-  /**
-   * Decides what to do with an incoming IDispatch arg. Two possibilities here:
-   * (1) We received a true javascript object (e.g. DOM object), in which case
-   * we wrap it in a Handle. (2) We received a Java object that was passed
-   * through the outside world and back, in which case we use black magic to get
-   * it back.
-   */
-  private static Object translateDispatchArg(Class type, Variant varArg) {
-    assert (varArg.getType() == COM.VT_DISPATCH);
-
-    Variant result = null;
-    try {
-      /*
-       * This implemention copied from OleAutomation.invoke(). We used to have a
-       * varArg.getAutomation().invoke() implementation, but it turns out the
-       * querying for typeInfo that occurs in the OleAutomation(IDispatch)
-       * constructor will cause a VM crash on some kinds of JavaScript objects,
-       * such as the window.alert function. So we do it by hand.
-       */
-      IDispatch dispatch = varArg.getDispatch();
-      result = new Variant();
-      int pVarResultAddress = 0;
-      int globalRef = 0;
-      try {
-        pVarResultAddress = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT,
-            Variant.sizeof);
-        int[] pArgErr = new int[1];
-        int hr = dispatch.Invoke(IDispatchProxy.DISPID_MAGIC_GETGLOBALREF,
-            new GUID(), COM.LOCALE_USER_DEFAULT, COM.DISPATCH_METHOD,
-            new DISPPARAMS(), pVarResultAddress, new EXCEPINFO(), pArgErr);
-
-        if (hr >= COM.S_OK) {
-          result = Variant.win32_new(pVarResultAddress);
-          globalRef = result.getInt();
-        }
-      } finally {
-        if (pVarResultAddress != 0) {
-          COM.VariantClear(pVarResultAddress);
-          OS.GlobalFree(pVarResultAddress);
-        }
-      }
-
-      // Result will be null if the dispid wasn't found.
-      if (globalRef != 0) {
-        // This is really a Java object being passed back via an IDispatchProxy.
-        IDispatchProxy proxy = (IDispatchProxy) LowLevel.objFromGlobalRefInt(globalRef);
-        return proxy.getTarget();
-      } else if (type == OleAutomation.class) {
-        // return the automation object
-        return varArg.getAutomation();
-      } else {
-        // This is a true JavaScript object, so wrap it.
-        return HandleIE6.createHandle(type, dispatch.getAddress());
-      }
-    } finally {
-
-      if (result != null) {
-        result.dispose();
-      }
-    }
-  }
+ 
 }
diff --git a/eclipse/jni/linux/.cdtbuild b/eclipse/jni/linux/.cdtbuild
new file mode 100644
index 0000000..3cd73be
--- /dev/null
+++ b/eclipse/jni/linux/.cdtbuild
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?fileVersion 3.1.0?>
+
+<ManagedProjectBuildInfo>
+<project id="JNI.cdt.managedbuild.target.gnu.so.1993024758" name="Shared Library (Gnu)" projectType="cdt.managedbuild.target.gnu.so">
+<configuration artifactExtension="so" artifactName="gwt-ll" cleanCommand="rm -rf" description="" errorParsers="org.eclipse.cdt.core.MakeErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GLDErrorParser;org.eclipse.cdt.core.GASErrorParser" id="cdt.managedbuild.config.gnu.so.debug.398050485" name="Debug" parent="cdt.managedbuild.config.gnu.so.debug">
+<toolChain id="cdt.managedbuild.toolchain.gnu.so.debug.1364127860" name="GCC Tool Chain" superClass="cdt.managedbuild.toolchain.gnu.so.debug">
+<tool command="gcc" id="cdt.managedbuild.tool.gnu.cpp.compiler.so.debug.1760328332" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.so.debug">
+<option id="gnu.cpp.compiler.option.include.paths.1542704606" superClass="gnu.cpp.compiler.option.include.paths" valueType="includePath">
+<listOptionValue builtIn="false" value="/usr/lib/j2sdk1.5-sun/include"/>
+<listOptionValue builtIn="false" value="/usr/lib/j2sdk1.5-sun/include/linux"/>
+<listOptionValue builtIn="false" value="&quot;${workspace_loc:/gwt-tools/sdk/mozilla-1.7.12/include}&quot;"/>
+<listOptionValue builtIn="false" value="&quot;${workspace_loc:/gwt-tools/sdk/mozilla-1.7.12/include/extra}&quot;"/>
+<listOptionValue builtIn="false" value="/usr/local/google/home/jat/src/gwt-trunk/build/out/jni/linux"/>
+</option>
+<option id="gnu.cpp.compiler.option.preprocessor.def.1306763452" superClass="gnu.cpp.compiler.option.preprocessor.def" valueType="definedSymbols">
+<listOptionValue builtIn="false" value="_REENTRANT"/>
+<listOptionValue builtIn="false" value="NO_NSPR_10_SUPPORT"/>
+</option>
+<option id="gnu.cpp.compiler.option.optimization.flags.1738733922" superClass="gnu.cpp.compiler.option.optimization.flags" value="-fno-omit-frame-pointer -fno-strict-aliasing" valueType="string"/>
+<option id="gnu.cpp.compiler.option.other.other.284396253" superClass="gnu.cpp.compiler.option.other.other" value="-c -fmessage-length=0 -fPIC -MMD -MP -Wno-system-headers -Os" valueType="string"/>
+<option id="gnu.cpp.compiler.so.debug.option.optimization.level.1987818635" superClass="gnu.cpp.compiler.so.debug.option.optimization.level" value="gnu.cpp.compiler.optimization.level.optimize" valueType="enumerated"/>
+<option id="gnu.cpp.compiler.option.warnings.allwarn.1524758494" superClass="gnu.cpp.compiler.option.warnings.allwarn" value="false" valueType="boolean"/>
+</tool>
+<tool id="cdt.managedbuild.tool.gnu.c.compiler.so.debug.363855699" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.so.debug"/>
+<tool id="cdt.managedbuild.tool.gnu.c.linker.so.debug.44069761" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.so.debug"/>
+<tool id="cdt.managedbuild.tool.gnu.cpp.linker.so.debug.127620842" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.so.debug">
+<option id="gnu.cpp.link.option.strip.1852166367" superClass="gnu.cpp.link.option.strip" value="true" valueType="boolean"/>
+<option id="gnu.cpp.link.option.libs.842521175" superClass="gnu.cpp.link.option.libs" valueType="libs">
+<listOptionValue builtIn="false" value="xpcomglue_s"/>
+</option>
+<option id="gnu.cpp.link.option.paths.1454970074" superClass="gnu.cpp.link.option.paths" valueType="stringList">
+<listOptionValue builtIn="false" value="&quot;${workspace_loc:/gwt-tools/sdk/mozilla-1.7.12/lib}&quot;"/>
+</option>
+</tool>
+<tool id="cdt.managedbuild.tool.gnu.assembler.so.debug.455913220" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.so.debug"/>
+<macros/>
+</toolChain>
+</configuration>
+<configuration artifactExtension="so" artifactName="libgwt-ll" cleanCommand="rm -rf" description="" errorParsers="org.eclipse.cdt.core.MakeErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GLDErrorParser;org.eclipse.cdt.core.GASErrorParser" id="cdt.managedbuild.config.gnu.so.release.196519812" name="Release" parent="cdt.managedbuild.config.gnu.so.release">
+<toolChain id="cdt.managedbuild.toolchain.gnu.so.release.309670679" name="GCC Tool Chain" superClass="cdt.managedbuild.toolchain.gnu.so.release">
+<tool id="cdt.managedbuild.tool.gnu.cpp.compiler.so.release.639871433" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.so.release">
+<option id="gnu.cpp.compiler.option.preprocessor.def.225251886" superClass="gnu.cpp.compiler.option.preprocessor.def" valueType="definedSymbols">
+<listOptionValue builtIn="false" value="_REENTRANT"/>
+</option>
+<option id="gnu.cpp.compiler.option.include.paths.2037074199" superClass="gnu.cpp.compiler.option.include.paths" valueType="includePath">
+<listOptionValue builtIn="false" value="/usr/lib/j2sdk1.5-sun/include"/>
+<listOptionValue builtIn="false" value="/usr/lib/j2sdk1.5-sun/include/linux"/>
+<listOptionValue builtIn="false" value="/usr/local/google/home/jat/src/gwt-tools/sdk/mozilla-1.7.12/include"/>
+<listOptionValue builtIn="false" value="/usr/local/google/home/jat/src/gwt-tools/sdk/mozilla-1.7.12/include/extra"/>
+</option>
+<option id="gnu.cpp.compiler.option.optimization.flags.324816514" superClass="gnu.cpp.compiler.option.optimization.flags" value="-Os -fPIC -fno-omit-frame-pointer -fno-strict-aliasing" valueType="string"/>
+<option id="gnu.cpp.compiler.option.warnings.allwarn.98479430" superClass="gnu.cpp.compiler.option.warnings.allwarn" value="false" valueType="boolean"/>
+<option id="gnu.cpp.compiler.option.other.other.1762086408" superClass="gnu.cpp.compiler.option.other.other" value="-c -fmessage-length=0 -Wno-system-headers" valueType="string"/>
+</tool>
+<tool id="cdt.managedbuild.tool.gnu.c.compiler.so.release.861896716" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.so.release"/>
+<tool id="cdt.managedbuild.tool.gnu.c.linker.so.release.1242435463" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.so.release"/>
+<tool id="cdt.managedbuild.tool.gnu.cpp.linker.so.release.696243428" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.so.release">
+<option id="gnu.cpp.link.option.strip.1678101635" superClass="gnu.cpp.link.option.strip" value="true" valueType="boolean"/>
+<option id="gnu.cpp.link.option.libs.1391450429" superClass="gnu.cpp.link.option.libs" valueType="libs">
+<listOptionValue builtIn="false" value="xpcomglue_s"/>
+</option>
+<option id="gnu.cpp.link.option.paths.1828386123" superClass="gnu.cpp.link.option.paths" valueType="stringList">
+<listOptionValue builtIn="false" value="/usr/local/google/home/jat/src/gwt-tools/sdk/mozilla-1.7.12/lib"/>
+</option>
+<option id="gnu.cpp.link.option.flags.1381564345" superClass="gnu.cpp.link.option.flags" value="-fPIC -Wl,shared-gcc" valueType="string"/>
+</tool>
+<tool id="cdt.managedbuild.tool.gnu.assembler.so.release.1277116837" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.so.release"/>
+<macros/>
+</toolChain>
+</configuration>
+<macros/>
+</project>
+</ManagedProjectBuildInfo>
diff --git a/eclipse/jni/linux/.cdtproject b/eclipse/jni/linux/.cdtproject
new file mode 100644
index 0000000..fcb39a2
--- /dev/null
+++ b/eclipse/jni/linux/.cdtproject
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse-cdt version="2.0"?>
+
+<cdtproject id="org.eclipse.cdt.managedbuilder.core.managedMake">
+<extension id="org.eclipse.cdt.managedbuilder.core.ManagedBuildManager" point="org.eclipse.cdt.core.ScannerInfoProvider"/>
+<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+<extension id="org.eclipse.cdt.core.GNU_ELF" point="org.eclipse.cdt.core.BinaryParser">
+<attribute key="addr2line" value="addr2line"/>
+<attribute key="c++filt" value="c++filt"/>
+</extension>
+<data>
+<item id="org.eclipse.cdt.core.pathentry">
+<pathentry kind="src" path=""/>
+<pathentry kind="out" path=""/>
+<pathentry kind="con" path="org.eclipse.cdt.managedbuilder.MANAGED_CONTAINER"/>
+</item>
+</data>
+</cdtproject>
diff --git a/eclipse/jni/linux/.checkstyle b/eclipse/jni/linux/.checkstyle
new file mode 100644
index 0000000..2e8139b
--- /dev/null
+++ b/eclipse/jni/linux/.checkstyle
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<fileset-config file-format-version="1.2.0" simple-config="true">
+    <fileset name="all" enabled="true" check-config-name="Sun Checks" local="false">
+        <file-match-pattern match-pattern="." include-pattern="true"/>
+    </fileset>
+</fileset-config>
diff --git a/eclipse/jni/linux/.project b/eclipse/jni/linux/.project
new file mode 100644
index 0000000..bd0a579
--- /dev/null
+++ b/eclipse/jni/linux/.project
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>jni-linux</name>
+	<comment></comment>
+	<projects>
+		<project>gwt-dev-linux</project>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.cdt.core.cnature</nature>
+		<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
+		<nature>org.eclipse.cdt.core.ccnature</nature>
+	</natures>
+	<linkedResources>
+		<link>
+			<name>linux</name>
+			<type>2</type>
+			<locationURI>GWT_ROOT/jni/linux</locationURI>
+		</link>
+		<link>
+			<name>core</name>
+			<type>2</type>
+			<locationURI>GWT_ROOT/jni/core</locationURI>
+		</link>
+	</linkedResources>
+</projectDescription>
diff --git a/eclipse/jni/linux/.settings/org.eclipse.cdt.core.prefs b/eclipse/jni/linux/.settings/org.eclipse.cdt.core.prefs
new file mode 100644
index 0000000..cbbb52e
--- /dev/null
+++ b/eclipse/jni/linux/.settings/org.eclipse.cdt.core.prefs
@@ -0,0 +1,3 @@
+#Thu Jan 25 19:11:27 GMT-05:00 2007
+eclipse.preferences.version=1
+indexerId=org.eclipse.cdt.core.fastIndexer
diff --git a/eclipse/jni/linux/.settings/org.eclipse.cdt.managedbuilder.core.prefs b/eclipse/jni/linux/.settings/org.eclipse.cdt.managedbuilder.core.prefs
new file mode 100644
index 0000000..7ee397d
--- /dev/null
+++ b/eclipse/jni/linux/.settings/org.eclipse.cdt.managedbuilder.core.prefs
@@ -0,0 +1,13 @@
+#Thu Jan 25 19:30:13 GMT-05:00 2007
+cdt.managedbuild.config.gnu.so.debug.398050485/internalBuilder/enabled=false
+cdt.managedbuild.config.gnu.so.debug.398050485/internalBuilder/ignoreErr=true
+cdt.managedbuild.config.gnu.so.release.196519812/internalBuilder/enabled=false
+cdt.managedbuild.config.gnu.so.release.196519812/internalBuilder/ignoreErr=true
+eclipse.preferences.version=1
+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.so.debug.398050485=<?xml version\="1.0" encoding\="UTF-8"?>\n<environment>\n<variable name\="CPATH" operation\="remove"/>\n<variable name\="C_INCLUDE_PATH" operation\="remove"/>\n<variable name\="CPLUS_INCLUDE_PATH" operation\="remove"/>\n</environment>\n
+environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.so.release.196519812=<?xml version\="1.0" encoding\="UTF-8"?>\n<environment>\n<variable name\="CPATH" operation\="remove"/>\n<variable name\="C_INCLUDE_PATH" operation\="remove"/>\n<variable name\="CPLUS_INCLUDE_PATH" operation\="remove"/>\n</environment>\n
+environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.so.debug.398050485=<?xml version\="1.0" encoding\="UTF-8"?>\n<environment>\n<variable name\="LIBRARY_PATH" operation\="remove"/>\n</environment>\n
+environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.so.release.196519812=<?xml version\="1.0" encoding\="UTF-8"?>\n<environment>\n<variable name\="LIBRARY_PATH" operation\="remove"/>\n</environment>\n
+environment/project=<?xml version\="1.0" encoding\="UTF-8"?>\n<environment/>\n
+environment/project/cdt.managedbuild.config.gnu.so.debug.398050485=<?xml version\="1.0" encoding\="UTF-8"?>\n<environment/>\n
+environment/project/cdt.managedbuild.config.gnu.so.release.196519812=<?xml version\="1.0" encoding\="UTF-8"?>\n<environment/>\n
diff --git a/eclipse/jni/mac/.cdtproject b/eclipse/jni/mac/.cdtproject
new file mode 100644
index 0000000..50ee475
--- /dev/null
+++ b/eclipse/jni/mac/.cdtproject
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?eclipse-cdt version="2.0"?>
+
+<cdtproject id="org.eclipse.cdt.make.core.make">
+<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+<data>
+<item id="scannerConfiguration">
+<autodiscovery enabled="false" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"/>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+</item>
+</data>
+</cdtproject>
diff --git a/eclipse/jni/mac/.project b/eclipse/jni/mac/.project
new file mode 100644
index 0000000..c5d60e9
--- /dev/null
+++ b/eclipse/jni/mac/.project
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>jni-mac</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.cdt.make.core.makeBuilder</name>
+			<triggers>clean,full,incremental,</triggers>
+			<arguments>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.build.arguments</key>
+					<value></value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.core.errorOutputParser</key>
+					<value>org.eclipse.cdt.core.MakeErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GLDErrorParser;org.eclipse.cdt.core.VCErrorParser;</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.environment</key>
+					<value></value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.enableAutoBuild</key>
+					<value>false</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.build.target.inc</key>
+					<value>all</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.enableFullBuild</key>
+					<value>true</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.enabledIncrementalBuild</key>
+					<value>true</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.build.command</key>
+					<value>make</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.build.target.clean</key>
+					<value>clean</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.enableCleanBuild</key>
+					<value>true</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.append_environment</key>
+					<value>true</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.useDefaultBuildCmd</key>
+					<value>true</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.build.target.auto</key>
+					<value>all</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.stopOnError</key>
+					<value>false</value>
+				</dictionary>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.cdt.make.core.ScannerConfigBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.cdt.core.cnature</nature>
+		<nature>org.eclipse.cdt.make.core.makeNature</nature>
+		<nature>org.eclipse.cdt.make.core.ScannerConfigNature</nature>
+		<nature>org.eclipse.cdt.core.ccnature</nature>
+	</natures>
+	<linkedResources>
+		<link>
+			<name>mac</name>
+			<type>2</type>
+			<locationURI>GWT_ROOT/jni/mac</locationURI>
+		</link>
+		<link>
+			<name>core</name>
+			<type>2</type>
+			<locationURI>GWT_ROOT/jni/core</locationURI>
+		</link>
+	</linkedResources>
+</projectDescription>
diff --git a/eclipse/jni/mac/.settings/org.eclipse.cdt.core.prefs b/eclipse/jni/mac/.settings/org.eclipse.cdt.core.prefs
new file mode 100644
index 0000000..5f60cef
--- /dev/null
+++ b/eclipse/jni/mac/.settings/org.eclipse.cdt.core.prefs
@@ -0,0 +1,3 @@
+#Sun Feb 25 23:11:44 EST 2007
+eclipse.preferences.version=1
+indexerId=org.eclipse.cdt.core.fastIndexer
diff --git a/jni/linux/ExternalWrapper.cpp b/jni/linux/ExternalWrapper.cpp
new file mode 100644
index 0000000..4f0ae72
--- /dev/null
+++ b/jni/linux/ExternalWrapper.cpp
@@ -0,0 +1,305 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/*
+ * Defines the JavaScript class gwt_external_class, which interface via JNI
+ * to Java objects.
+ */
+
+#include <jni.h>
+#include "JsRootedValue.h"
+#include "gwt-jni.h"
+#include "ExternalWrapper.h"
+#include "Tracer.h"
+#include "JsStringWrap.h"
+
+// note that this does not use the NS_DEFINE_CID macro because it defines
+// the variable as const, which by default has internal linkage.  I could
+// work around it by putting extern in front of the macro, but that seems
+// fragile if the macro changes.
+nsCID kGwtExternalCID = GWT_EXTERNAL_FACTORY_CID;
+
+/*
+ * definition of JavaScript gwtOnLoad method which maps to the Java
+ * gwtOnLoad method.
+ */
+static JSBool gwtOnLoad(JSContext *cx, JSObject *obj, uintN argc,
+    jsval *argv, jsval *rval)
+{
+  Tracer tracer("gwtOnLoad");
+  if (argc < 2) {
+    tracer.setFail("less than 2 args");
+    return JS_FALSE;
+  }
+
+  JSObject* scriptWindow = 0;
+  if (argv[0] != JSVAL_NULL && argv[0] != JSVAL_VOID) {
+    if (!JS_ValueToObject(cx, argv[0], &scriptWindow)) {
+      tracer.setFail("can't get script window object");
+      return JS_FALSE;
+    }
+  }
+
+  JSString* moduleName = 0;
+  if (argv[1] != JSVAL_NULL && argv[1] != JSVAL_VOID) {
+    moduleName = JS_ValueToString(cx, argv[1]);
+  }
+
+  nsCOMPtr<nsIScriptGlobalObject> scriptGlobal(0);
+  if (scriptWindow) {
+    nsCOMPtr<nsIXPConnect> xpConnect = do_GetService(nsIXPConnect::GetCID());
+    nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
+    xpConnect->GetWrappedNativeOfJSObject(cx, scriptWindow,
+        getter_AddRefs(wrappedNative));
+    if (wrappedNative) {
+        nsCOMPtr<nsISupports> native;
+        wrappedNative->GetNative(getter_AddRefs(native));
+        scriptGlobal = do_QueryInterface(native);
+    }
+  }
+  jstring jModuleName(0);
+  if (moduleName) {
+    jModuleName = savedJNIEnv->NewString(JS_GetStringChars(moduleName),
+        JS_GetStringLength(moduleName));
+    if (!jModuleName || savedJNIEnv->ExceptionCheck()) {
+      tracer.setFail("can't get module name in Java string");
+      return JS_FALSE;
+    }
+  }
+  
+  jobject externalObject = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
+  jclass objClass = savedJNIEnv->GetObjectClass(externalObject);
+  if (!objClass || savedJNIEnv->ExceptionCheck()) {
+    tracer.setFail("can't get LowLevelMoz.ExternalObject class");
+    return JS_FALSE;
+  }
+  
+  jmethodID methodID = savedJNIEnv->GetMethodID(objClass, "gwtOnLoad",
+      "(ILjava/lang/String;)Z");
+  if (!methodID || savedJNIEnv->ExceptionCheck()) {
+    tracer.setFail("can't get gwtOnLoad method");
+    return JS_FALSE;
+  }
+  
+  jboolean result = savedJNIEnv->CallBooleanMethod(externalObject, methodID,
+      NS_REINTERPRET_CAST(jint, scriptGlobal.get()), jModuleName);
+  if (savedJNIEnv->ExceptionCheck()) {
+    tracer.setFail("LowLevelMoz.ExternalObject.gwtOnLoad() threw an exception");
+    return JS_FALSE;
+  }
+  *rval = BOOLEAN_TO_JSVAL((result == JNI_FALSE) ? JS_FALSE : JS_TRUE);
+  return JS_TRUE;
+}
+
+/*=======================================================================*/
+/* Implementation of the getProperty, setProperty, and finalize methods  */
+/* for the JavaScript class gwt_external_class.                          */
+/*=======================================================================*/
+
+static JSBool JS_DLL_CALLBACK gwt_external_getProperty(JSContext *cx,
+    JSObject *obj, jsval id, jsval *vp)
+{
+  Tracer tracer("gwt_external_getProperty");
+  if (*vp != JSVAL_VOID)
+    return JS_TRUE;
+
+  if (!JSVAL_IS_STRING(id)) {
+    tracer.setFail("not a string");
+    return JS_FALSE;
+  }
+  JsStringWrap jsStr(cx, id);
+  tracer.log("obj=%08x, property=%.*s", obj, jsStr.length(), jsStr.bytes());
+
+// All this is for calling resolveReference which only returns void.  So,
+// just replace this code to return void for now.  TODO(jat): revisit when
+// merging in Toby's code. 
+#if 0
+  jstring jident = savedJNIEnv->NewString(jsStr.chars(), jsStr.length());
+  if (!jident || savedJNIEnv->ExceptionCheck()) {
+    tracer.setFail("can't create Java string ");
+    return JS_FALSE;
+  }
+  jobject externalObject = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
+  jclass objClass = savedJNIEnv->GetObjectClass(externalObject);
+  if (!objClass || savedJNIEnv->ExceptionCheck()) {
+    tracer.setFail("can't find Java class for JS object");
+    return JS_FALSE;
+  }
+
+  jmethodID methodID = savedJNIEnv->GetMethodID(objClass, "resolveReference",
+      "(Ljava/lang/String;)I");
+  if (!methodID || savedJNIEnv->ExceptionCheck()) {
+    tracer.setFail("exception getting method for int resolveReference(String)");
+    return JS_FALSE;
+  }
+  int retval = savedJNIEnv->CallIntMethod(externalObject, methodID, jident);
+  if (savedJNIEnv->ExceptionCheck()) {
+    tracer.setFail("exception calling int resolveReference(String)");
+    return JS_FALSE;
+  }
+  *vp = retval;
+#else
+  *vp = JSVAL_VOID;
+#endif
+  return JS_TRUE;
+}
+
+static void JS_DLL_CALLBACK gwt_external_finalize(JSContext *cx, JSObject *obj)
+{
+  jobject externalObject = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
+  if (externalObject)
+    savedJNIEnv->DeleteGlobalRef(externalObject);
+  JS_FinalizeStub(cx,obj);
+}
+
+static JSBool JS_DLL_CALLBACK gwt_external_setProperty(JSContext *cx,
+    JSObject *obj, jsval id, jsval *vp)
+{
+  return JS_FALSE;
+}
+ 
+static JSClass gwt_external_class = {
+    "gwt_external_class", JSCLASS_HAS_PRIVATE,
+    JS_PropertyStub, JS_PropertyStub, gwt_external_getProperty,
+    gwt_external_setProperty, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,
+    gwt_external_finalize,
+    JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+NS_IMPL_ISUPPORTS1(ExternalWrapper, nsIScriptObjectOwner)
+
+/*
+ * Create a new LowLevelMoz.ExternalObject instance and a JavaScript object
+ * that refers to it, mapping the gwtOnLoad member function mapping to
+ * a Javascript method.
+ */
+NS_IMETHODIMP ExternalWrapper::GetScriptObject(nsIScriptContext *aContext,
+    void** aScriptObject)
+{
+  Tracer tracer("ExternalWrapper::GetScriptObject"); 
+  if (!aScriptObject) {
+    tracer.setFail("null script object pointer");
+    return NS_ERROR_INVALID_POINTER;
+  }
+  if (!mScriptObject) {
+    *aScriptObject = 0;
+
+    nsIScriptGlobalObject* globalObject = aContext->GetGlobalObject();
+    nsCOMPtr<nsIDOMWindow> domWindow(do_QueryInterface(globalObject));
+    if (!domWindow) {
+      tracer.setFail("can't get DOM window");
+      return NS_ERROR_UNEXPECTED;
+    }
+    nsCOMPtr<nsIDOMWindow> topWindow;
+    domWindow->GetTop(getter_AddRefs(topWindow));
+    if (!topWindow) {
+      tracer.setFail("Can't get top window");
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    tracer.log("savedJNIEnv=%08x, llClass=%08x", unsigned(savedJNIEnv),
+        lowLevelMozClass);
+    
+    jmethodID methodID = savedJNIEnv->GetStaticMethodID(lowLevelMozClass,
+        "createExternalObjectForDOMWindow",
+        "(I)Lcom/google/gwt/dev/shell/moz/LowLevelMoz$ExternalObject;");
+    if (!methodID || savedJNIEnv->ExceptionCheck()) {
+      tracer.setFail("Can't get createExternalObjectForDOMWindow method");
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    jobject externalObject = savedJNIEnv->CallStaticObjectMethod(
+        lowLevelMozClass, methodID, NS_REINTERPRET_CAST(jint, topWindow.get()));
+    if (!externalObject || savedJNIEnv->ExceptionCheck()) {
+      tracer.setFail("createExternalObjectForDOMWindow failed");
+      return NS_ERROR_UNEXPECTED;
+    }
+    externalObject = savedJNIEnv->NewGlobalRef(externalObject);
+    if (!externalObject || savedJNIEnv->ExceptionCheck()) {
+      tracer.setFail("can't get GlobalRef for external object");
+      return NS_ERROR_UNEXPECTED;
+    }
+    JSContext* cx = NS_REINTERPRET_CAST(JSContext*,
+        aContext->GetNativeContext());
+    if (!cx) {
+      tracer.setFail("can't get JSContext");
+      return NS_ERROR_UNEXPECTED;
+    }
+    JSObject* newObj = JS_NewObject(cx, &gwt_external_class, 0,
+        globalObject->GetGlobalJSObject());
+    if (!newObj) {
+      tracer.setFail("can't create new gwt_external_class object");
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    if (!JS_SetPrivate(cx, newObj, externalObject)) {
+      savedJNIEnv->DeleteGlobalRef(externalObject);
+      tracer.setFail("can't store external object private reference");
+      return NS_ERROR_UNEXPECTED;
+    }
+  
+    if (!JS_DefineFunction(cx, newObj, "gwtOnLoad", gwtOnLoad, 3,
+        JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) {
+      tracer.setFail("can't define gwtOnLoad function on JavaScript object");
+      return NS_ERROR_UNEXPECTED;
+    }
+    mScriptObject = newObj;
+  }
+
+  *aScriptObject = mScriptObject;
+  return NS_OK;
+}
+
+NS_IMETHODIMP ExternalWrapper::SetScriptObject(void* aScriptObject)
+{
+    mScriptObject = aScriptObject;
+    return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS1(nsRpExternalFactory, nsIFactory)
+
+NS_IMETHODIMP nsRpExternalFactory::CreateInstance(nsISupports *aOuter,
+    const nsIID& aIID, void** aResult)
+{
+  Tracer tracer("nsRpExternalFactory::CreateInstance");
+  if (!aResult) {
+    tracer.setFail("null pointer for return value");
+    return NS_ERROR_INVALID_POINTER;
+  }
+  *aResult  = NULL;
+
+  if (aOuter) {
+    tracer.setFail("aOuter is not null");
+    return NS_ERROR_NO_AGGREGATION;
+  }
+
+  nsISupports* object = new ExternalWrapper();
+  if (!object) {
+    tracer.setFail("can't create a new ExternalWrapper");
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+      
+  nsresult result = object->QueryInterface(aIID, aResult);
+  if (!*aResult || NS_FAILED(result)) {
+    tracer.setFail("ExternalWrapper::QueryInterface failed");
+    delete object;
+  }
+  return result;
+}
+
+NS_IMETHODIMP nsRpExternalFactory::LockFactory(PRBool lock)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
diff --git a/jni/linux/ExternalWrapper.h b/jni/linux/ExternalWrapper.h
new file mode 100644
index 0000000..7cabc23
--- /dev/null
+++ b/jni/linux/ExternalWrapper.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2006 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#ifndef EXTERNALWRAPPER_H_
+#define EXTERNALWRAPPER_H_
+
+class ExternalWrapper : public nsIScriptObjectOwner {
+public:
+  NS_DECL_ISUPPORTS
+  NS_IMETHOD GetScriptObject(nsIScriptContext *aContext, void** aScriptObject);
+  NS_IMETHOD SetScriptObject(void* aScriptObject);
+  ExternalWrapper(): mScriptObject(0) { }
+private:
+  ~ExternalWrapper() { }
+  void *mScriptObject;
+};
+
+class nsRpExternalFactory : public nsIFactory {
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIFACTORY
+  nsRpExternalFactory() { }
+private:
+  ~nsRpExternalFactory() { }
+};
+
+#define GWT_EXTERNAL_FACTORY_CID \
+{ 0xF56E23F8, 0x5D06, 0x47F9, \
+{ 0x88, 0x5A, 0xD9, 0xCA, 0xC3, 0x38, 0x41, 0x7F } }
+#define GWT_EXTERNAL_CONTRACTID "@com.google/GWT/external;1"
+
+#endif /*EXTERNALWRAPPER_H_*/
diff --git a/jni/linux/JStringWrap.h b/jni/linux/JStringWrap.h
new file mode 100644
index 0000000..5243776
--- /dev/null
+++ b/jni/linux/JStringWrap.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#ifndef JNI_LINUX_JSTRINGWRAP_H_
+#define JNI_LINUX_JSTRINGWRAP_H_
+
+/*
+ * Wrapper arouond Java Strings, keeps pointers to unpacked strings
+ * and makes sure they are cleaned up. 
+ */
+class JStringWrap {
+  public:
+    JStringWrap(JNIEnv* env, jstring str): env(env), s(str), p(0), jp(0) { }
+    ~JStringWrap() {
+      if (p) env->ReleaseStringUTFChars(s, p);
+      if (jp) env->ReleaseStringChars(s, jp);
+    }
+    const char* str() {
+      if (!p) p = env->GetStringUTFChars(s, 0);
+      return p;
+    }
+    const jchar* jstr() {
+      if (!jp) jp = env->GetStringChars(s, 0);
+      return jp;
+    }
+  private:
+    JNIEnv* env;
+    jstring s;
+    const char* p;
+    const jchar* jp;
+};
+
+
+#endif // JNI_LINUX_JSTRINGWRAP_H_
diff --git a/jni/linux/JsRootedValue.cpp b/jni/linux/JsRootedValue.cpp
new file mode 100644
index 0000000..08dd7cb
--- /dev/null
+++ b/jni/linux/JsRootedValue.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include "JsRootedValue.h"
+
+// intialize static value used to hold the JavaScript String class.
+JSClass* JsRootedValue::stringClass = 0;
+
+/*
+ * Actually get the stringClass pointer from JavaScript.
+ */
+void JsRootedValue::fetchStringClass() const {
+  Tracer tracer("JsRootedValue::fetchStringClass");
+  jsval val = JS_GetEmptyStringValue(context_);
+  JSObject* obj;
+  // on error, leave stringClass null
+  if (!JS_ValueToObject(context_, val, &obj)) return;
+  if (!obj) {
+    tracer.log("ensureStringClass: null object");
+    return;
+  }
+  stringClass = JS_GET_CLASS(context_, obj);
+  tracer.log("stringClass=%08x", unsigned(stringClass));
+}
diff --git a/jni/linux/JsRootedValue.h b/jni/linux/JsRootedValue.h
new file mode 100644
index 0000000..663200e
--- /dev/null
+++ b/jni/linux/JsRootedValue.h
@@ -0,0 +1,385 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+#ifndef JNI_LINUX_JSROOTEDVALUE_H_
+#define JNI_LINUX_JSROOTEDVALUE_H_
+
+// Mozilla header files
+#include "mozilla-headers.h"
+
+#include "Tracer.h"
+
+extern JSClass gwt_nativewrapper_class;
+
+/*
+ * Holds a root for Javascript objects, so the JS interpreter knows not to
+ * garbage-collect the underlying object as long as this object exists.
+ * Java code will pass a pointer to this object around (as an int/long) for
+ * referring to the underlying Javascript object.
+ *
+ * There are also convenience routines for manipulating the underlying value.
+ * Note that all get* methods assume the type is correct, so the corresponding
+ * is* method should be called first if you aren't sure of the type.
+ *
+ * See http://developer.mozilla.org/en/docs/JS_AddRoot for details.
+ *
+ * TODO(jat): handle unboxing Javascript objects like Boolean/etc.
+ * TODO(jat): rewrite this to minimize the number of roots held and to
+ *    improve 64-bit compatibility.
+ */
+class JsRootedValue
+{
+private:
+  // the JavaScript String class
+  static JSClass* stringClass;
+  
+  // Javascript context
+  JSContext*    context_;
+  // underlying Javascript value
+  jsval         value_;
+
+protected:
+  /*
+   * Fetch the JavaScript String class.
+   * Not inlined to minimize code bloat since it should only be called once.
+   */
+  void fetchStringClass() const;
+  
+  /*
+   * Make sure we have the JS code to identify String objects installed.
+   */
+  void ensureStringClass() const {
+    if(stringClass) return;
+    fetchStringClass();
+  }
+  
+  /*
+   * Make this value rooted if the current value is a pointer type
+   * 
+   * Returns false if an error occurred.
+   */
+  bool setRoot() {
+    Tracer tracer("JsRootedValue::setRoot", this);
+    tracer.log("context=%08x, value=%08x", context_, value_);
+    if(JSVAL_IS_GCTHING(value_)) {
+      tracer.log("value is GC - %s", JS_GetTypeName(context_,
+          JS_TypeOfValue(context_, value_)));
+      bool returnVal = JS_AddRoot(context_, &value_);
+      if (!returnVal) {
+        tracer.log("*** JS_AddRoot failed");
+      }
+      return returnVal;
+    }
+    return true;
+  }
+  /*
+   * Remove this value from the roots list if it was there
+   * 
+   * Returns false if an error occurred (note that currently JS_RemoveRoot
+   * is documented to always return true, so this is not currently possible).
+   */
+  bool removeRoot() {
+    Tracer tracer("JsRootedValue::removeRoot", this);
+    tracer.log("context=%08x, value=%08x", context_, value_);
+    if(JSVAL_IS_GCTHING(value_)) {
+      tracer.log("value is GC - %s", JS_GetTypeName(context_,
+          JS_TypeOfValue(context_, value_)));
+      bool returnVal = JS_RemoveRoot(context_, &value_);
+      if (!returnVal) {
+        tracer.log("*** JS_RemoveRoot failed");
+      }
+      return returnVal;
+    }
+    return true;
+  }
+  
+public:
+  /*
+   * Copy constructor - make another rooted value that refers to the same
+   * JavaScript object (or has the same value if a primitive)
+   */
+  JsRootedValue(const JsRootedValue& rooted_value)
+      : context_(rooted_value.context_), value_(rooted_value.value_)
+  {
+    Tracer tracer("JsRootedValue copy constr", this);
+    tracer.log("other jsRootedVal=%08x", reinterpret_cast<unsigned>(&rooted_value));
+    if (!JS_AddRoot(context_, &value_)) {
+      tracer.log("JsRootedValue copy constructor: JS_AddRoot failed");
+      // TODO(jat): handle errors
+    }
+  }
+  
+  JsRootedValue(JSContext* context, jsval value) : context_(context),
+      value_(value)
+  {
+    Tracer tracer("JsRootedValue jsval constr", this);
+    tracer.log("jsval=%08x", value);
+    if (!JS_AddRoot(context_, &value_)) {
+      tracer.log("JsRootedValue jsval constructor: JS_AddRoot failed");
+      // TODO(jat): handle errors
+    }
+  }
+  
+  /*
+   * Create a void value - safe since no errors can occur
+   */
+  JsRootedValue(JSContext* context) : context_(context), value_(JSVAL_VOID) {  }
+  
+  /*
+   * Destroy this object.
+   */
+  virtual ~JsRootedValue() {
+    Tracer tracer("~JsRootedValue", this);
+    // ignore error since currently it is not possible to fail
+    JS_RemoveRoot(context_, &value_);
+  }
+
+  /*
+   * Return the JSContext* pointer.
+   */
+  JSContext* getContext() const { return context_; }
+  
+  /*
+   * Return the global object for this value's context.
+   */
+  JSObject* getGlobalObject() const { return JS_GetGlobalObject(context_); }
+  
+  /* 
+   * Return the underlying JS object
+   */
+  jsval getValue() const { return value_; }
+  
+  /*
+   * Sets the value of the underlying JS object and its context.
+   * 
+   * Returns false if an error occurred.
+   */
+  bool setContextValue(JSContext* new_context, jsval new_value) {
+    context_ = new_context;
+    value_ = new_value;
+    return true;
+  }
+  
+  /*
+   * Sets the value of the underlying JS object.
+   * 
+   * Returns false if an error occurred.
+   */
+  bool setValue(jsval new_value) {
+    value_ = new_value;
+    return true;
+  }
+  
+   /*
+   * Returns true if the underlying value is of some number type.
+   */
+  bool isNumber() const {
+    return JSVAL_IS_NUMBER(value_);
+  }
+  /*
+   * Returns the underlying value as a double.
+   * Result is 0.0 if the underlying value is not a number
+   * type.
+   */
+  double getDouble() const {
+    jsdouble return_value=0.0;
+    void(JS_ValueToNumber(context_, value_, &return_value)); 
+    return double(return_value);
+  }
+  /*
+   * Set the underlying value to a double value.
+   * 
+   * Returns false on failure.
+   */
+  bool setDouble(double val) {
+    jsval js_double;
+    if(!JS_NewDoubleValue(context_, jsdouble(val), &js_double)) {
+      return false;
+    }
+    return setValue(js_double);
+  }
+
+  /*
+   * Returns the underlying value as an integer value.  Note that the result
+   * is undefined if isInt() does not return true.
+   */
+  int getInt() {
+    return JSVAL_TO_INT(value_);
+  }
+  
+  /*
+   * Set the underlying value to an integer value.
+   * 
+   * Returns false on failure.
+   */
+  bool setInt(int val) {
+    return setValue(INT_TO_JSVAL(val));
+  }
+  
+  /*
+   * Returns true if the underlying value is a boolean.
+   */
+  bool isBoolean() const {
+    return JSVAL_IS_BOOLEAN(value_);
+  }
+  /*
+   * Returns the underlying value as a boolean.
+   * Result is undefined if the value is not actually
+   * a boolean.
+   */
+  bool getBoolean() const {
+    return value_ != JSVAL_FALSE;
+  }
+  /*
+   * Set the underlying value to a boolean value.
+   * 
+   * Returns false on failure (impossible?).
+   */
+  bool setBoolean(bool val) {
+    return setValue(val ? JSVAL_TRUE : JSVAL_FALSE);
+  }
+
+  /*
+   * Returns true if the underlying value is a string.
+   */
+  bool isInt() const {
+    return JSVAL_IS_INT(value_);
+  }
+  
+  /*
+   * Returns true if the underlying value is a string.
+   */
+  bool isString() const {
+    return JSVAL_IS_STRING(value_);
+  }
+
+  /*
+   * Check if the value is a JavaScript String object.
+   */
+  bool isJavaScriptStringObject() const {
+    if (!isObject()) return false;
+    ensureStringClass();
+    return getObjectClass() == stringClass;
+  }
+  
+  /*
+   * Return this value as a string, converting as necessary.
+   */
+  JSString* asString() const {
+    return JS_ValueToString(context_, value_);
+  }
+  
+  /* Returns the string as a JSString pointer.
+   * Result is undefined if the value is not actually a string or String object.
+   */
+  const JSString* getString() const {
+    if (JSVAL_IS_STRING(value_)) {
+      return JSVAL_TO_STRING(value_);
+    }
+    return asString();
+  }
+  /*
+   * Returns the string as a zero-terminated array of UTF16 characters.
+   * Note that this pointer may become invalid when JS performs GC, so it
+   * may only be used without calling other JS functions.  Result is
+   * undefined if the value is not actually a string.
+   */
+  const wchar_t* getStringChars() const {
+    return reinterpret_cast<const wchar_t*>(JS_GetStringChars(
+        const_cast<JSString*>(getString())));
+  }
+  
+  /*
+   * Returns the length of the underlying string.  Result is undefined
+   * if the value is not actually a string.
+   */
+  int getStringLength() const {
+    return JS_GetStringLength(const_cast<JSString*>(getString()));
+  } 
+  
+  /*
+   * Sets the underlying value, defined by a null-terminated array of UTF16 chars.
+   * 
+   * Returns false on failure.
+   */
+  bool setString(const wchar_t* utf16) {
+    JSString* str = JS_NewUCStringCopyZ(context_, reinterpret_cast<const jschar*>(utf16));
+    return setValue(STRING_TO_JSVAL(str));
+  }
+  
+  /*
+   * Returns true if the underlying value is an object.
+   */
+  bool isObject() const {
+    return JSVAL_IS_OBJECT(value_);
+  }
+  
+  /*
+   * Returns the underlying value as an object.
+   * Result is undefined if it is not actually an object.
+   */
+  JSObject* getObject() const {
+    return isObject() ? JSVAL_TO_OBJECT(value_) : 0;
+  }
+  
+  /*
+   * Returns the class name of the underlying value.
+   * Result is undefined if it is not actually an object.
+   */
+  const JSClass* getObjectClass() const {
+    return isObject() ? JS_GET_CLASS(context_, getObject()) : 0;
+  }
+  
+  /*
+   * Sets the underlying value to be an object.
+   * Returns false on failure.
+   */
+  bool setObject(JSObject* obj) {
+    return setValue(OBJECT_TO_JSVAL(obj));
+  }
+
+  /*
+   * Returns true if the underlying value is undefined (void).
+   */
+  bool isUndefined() const {
+    return JSVAL_IS_VOID(value_);
+  }
+  
+  /*
+   * Sets the underlying value to be undefined (void).
+   * 
+   * Returns false on failure (impossible?)
+   */
+  bool setUndefined() {
+    return setValue(JSVAL_VOID);
+  }
+  
+  /*
+   * Returns true if the underlying value is null.
+   */
+  bool isNull() const {
+    return JSVAL_IS_NULL(value_);
+  }
+  /*
+   * Sets the underlying value to be null.
+   * 
+   * Returns false on failure (impossible?)
+   */
+  bool setNull() {
+    return setValue(JSVAL_NULL);
+  }
+};
+
+#endif /*JNI_LINUX_JSROOTEDVALUE_H_*/
diff --git a/jni/linux/JsStringWrap.h b/jni/linux/JsStringWrap.h
new file mode 100644
index 0000000..7ab0ce6
--- /dev/null
+++ b/jni/linux/JsStringWrap.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#ifndef JNI_LINUX_JSSTRINGWRAP_H_
+#define JNI_LINUX_JSSTRINGWRAP_H_
+
+/*
+ * Wrapper arouond JavaScript Strings, keeps pointers to unpacked strings
+ * and makes sure they are not cleaned up early. 
+ */
+class JsStringWrap {
+  private:
+    JSContext* context_;
+    JSString* string_;
+    const char* bytes_;
+    const wchar_t* chars_;
+
+  public:
+    JsStringWrap(JSContext* context, JSString* str)
+        : context_(context), string_(str), bytes_(0), chars_(0) {
+      JS_AddRoot(context_, &string_);
+      JS_AddRoot(context_, &bytes_);
+      JS_AddRoot(context_, &chars_);
+    }
+    JsStringWrap(JSContext* context, jsval str)
+        : context_(context), string_(JSVAL_TO_STRING(str)), bytes_(0), chars_(0) {
+      JS_AddRoot(context_, &string_);
+      JS_AddRoot(context_, &bytes_);
+      JS_AddRoot(context_, &chars_);
+    }
+    ~JsStringWrap() {
+      JS_RemoveRoot(context_, &string_);
+      JS_RemoveRoot(context_, &bytes_);
+      JS_RemoveRoot(context_, &chars_);
+    }
+    const char* bytes() {
+      if (!bytes_) bytes_ = JS_GetStringBytes(string_);
+      return bytes_;
+    }
+    const wchar_t* chars() {
+      if (!chars_) {
+        chars_ = reinterpret_cast<wchar_t*>(JS_GetStringChars(string_));
+      }
+      return chars_;
+    }
+    int length() {
+      return JS_GetStringLength(string_);
+    }
+};
+
+#endif // JNI_LINUX_JSSTRINGWRAP_H_
diff --git a/jni/linux/JsValueMoz.cpp b/jni/linux/JsValueMoz.cpp
new file mode 100644
index 0000000..b383a37
--- /dev/null
+++ b/jni/linux/JsValueMoz.cpp
@@ -0,0 +1,814 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include <jni.h>
+#include "JsRootedValue.h"
+#include "gwt-jni.h"
+#include "ExternalWrapper.h"
+#include "Tracer.h"
+
+// include javah-generated header to make sure things match
+#include "JsValueMoz.h"
+
+/*
+ * Returns the value of a field on a Java object.
+ * 
+ * context - JavaScript context
+ * clazz - class of obj
+ * obj - Java object to retreive field from
+ * fieldName - name of field on Java object to retrieve
+ * 
+ * Returns null on failure.  Caller is responsible for deleting
+ * returned JsRootedValue when done with it.
+ */
+JsRootedValue* GetFieldAsRootedValue(JSContext* context, jclass clazz,
+    jobject obj, jstring fieldName)
+{
+  jmethodID getFieldMeth = savedJNIEnv->GetMethodID(clazz, "getField",
+      "(Ljava/lang/String;I)V");
+  if (!getFieldMeth || savedJNIEnv->ExceptionCheck())
+    return 0;
+
+  JsRootedValue* jsRootedValue = new JsRootedValue(context);
+  savedJNIEnv->CallVoidMethod(obj, getFieldMeth, fieldName,
+  	 reinterpret_cast<jint>(jsRootedValue));
+  if (savedJNIEnv->ExceptionCheck()) {
+  	 delete jsRootedValue;
+    return 0;
+  }
+  return jsRootedValue;
+}
+  
+/*
+ * Sets the value of a field on a Java object.
+ * 
+ * context - JavaScript context
+ * clazz - class of obj
+ * obj - Java object to store into field
+ * fieldName - name of field on Java object to store into
+ * jsRootedValue - the value to store in the field
+ * 
+ * returns true on success, false on failure
+ */
+bool SetFieldFromRootedValue(JSContext* context, jclass clazz,
+    jobject obj, jstring fieldName, JsRootedValue* jsRootedValue)
+{
+  jmethodID getFieldMeth = savedJNIEnv->GetMethodID(clazz, "setField",
+      "(Ljava/lang/String;I)V");
+  if (!getFieldMeth || savedJNIEnv->ExceptionCheck())
+    return false;
+
+  savedJNIEnv->CallVoidMethod(obj, getFieldMeth, fieldName,
+  	 reinterpret_cast<jint>(jsRootedValue));
+  if (savedJNIEnv->ExceptionCheck())
+    return false;
+
+  return true;
+}
+
+/*
+ * Throws a HostedModeException with the specified message.
+ */
+static void ThrowHostedModeException(JNIEnv* jniEnv, const char* msg) {
+  jclass exceptionClass
+      = jniEnv->FindClass("com/google/gwt/dev/shell/HostedModeException");
+  jniEnv->ThrowNew(exceptionClass, msg);
+}
+
+
+enum JsValueType {
+  JSVAL_TYPE_VOID=0,
+  JSVAL_TYPE_NULL,
+  JSVAL_TYPE_BOOLEAN,
+  JSVAL_TYPE_NUMBER,
+  JSVAL_TYPE_STRING,
+  JSVAL_TYPE_OBJECT,
+  JSVAL_TYPE_UNKNOWN,
+};
+
+static const char* JsValueTypeStrings[]={
+  "undefined",
+  "null",
+  "boolean",
+  "number",
+  "string",
+  "object",
+  "unknown",
+};
+
+static JsValueType GetValueType(jsval val) {
+  if(JSVAL_IS_VOID(val)) {
+    return JSVAL_TYPE_VOID;
+  } else if(JSVAL_IS_NULL(val)) {
+    return JSVAL_TYPE_NULL;
+  } else if(JSVAL_IS_BOOLEAN(val)) {
+    return JSVAL_TYPE_BOOLEAN;
+  } else if(JSVAL_IS_NUMBER(val)) {
+    return JSVAL_TYPE_NUMBER;
+  } else if(JSVAL_IS_STRING(val)) {
+    return JSVAL_TYPE_STRING;
+  } else if(JSVAL_IS_OBJECT(val)) {
+    return JSVAL_TYPE_OBJECT;
+  } else {
+    return JSVAL_TYPE_UNKNOWN;
+  } 
+}
+
+/*
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _createJsRootedValue()
+ * Signature: (II)I
+ */
+extern "C" JNIEXPORT jint JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1createJsRootedValue
+  (JNIEnv* jniEnv, jclass, jint scriptObjInt, jint jsval)
+{
+  Tracer tracer("JsValueMoz._createJsRootedValue");
+  nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
+  nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
+  if (!scriptContext) {
+    ThrowHostedModeException(jniEnv, "Unable to get script context");
+    tracer.setFail("Unable to get script context");
+    return 0;
+  }
+  JSContext* context = NS_REINTERPRET_CAST(JSContext*, scriptContext->GetNativeContext());
+  JsRootedValue* jsRootedValue = new JsRootedValue(context, jsval);
+  return NS_REINTERPRET_CAST(jint, jsRootedValue);
+}
+
+/*
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _copyJsRootedValue()
+ * Signature: (I)I
+ */
+extern "C" JNIEXPORT jint JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1copyJsRootedValue
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  const JsRootedValue* jsRootedValue = reinterpret_cast<const JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._copyJsRootedValue", jsRootedValue);
+  JsRootedValue* newRootedValue = new JsRootedValue(*jsRootedValue);
+  return NS_REINTERPRET_CAST(jint, newRootedValue);
+}
+
+/*
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _destroyJsRootedValue()
+ * Signature: (I)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1destroyJsRootedValue
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._destroyJsRootedValue", jsRootedValue);
+  delete jsRootedValue;
+}
+
+/*
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _getBoolean()
+ * Signature: (I)Z
+ * 
+ * TODO(jat): unboxing Javascript Boolean type?
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getBoolean
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._getBoolean", jsRootedValue);
+  return jsRootedValue->getBoolean();
+}
+
+/**
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _getInt()
+ * Signature: (I)I
+ * 
+ * @see com.google.gwt.dev.shell.moz.JsValueMoz#getInt()
+ * 
+ * TODO(jat): unboxing Javascript Integer type?
+ */
+extern "C" JNIEXPORT jint JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getInt
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._getInt", jsRootedValue);
+  int val = jsRootedValue->getInt();
+  tracer.log("value=%d", val);
+  return val;
+}
+
+/**
+ * Return a Javascript number as a Java double.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _getNumber()
+ * Signature: (I)D
+ */
+extern "C" JNIEXPORT jdouble JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getNumber
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._getNumber", jsRootedValue);
+  return jsRootedValue->getDouble();
+}
+
+/**
+ * Return a Javascript string as a Java string.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _getString()
+ * Signature: (I)Ljava/lang/String;
+ * 
+ * Note that this relies on jschar being assignment compatible with jchar
+ */
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getString
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._getString", jsRootedValue);
+  const JSString* str = jsRootedValue->getString();
+  int len = JS_GetStringLength(const_cast<JSString*>(str));
+  jstring javaStr =jniEnv->NewString(reinterpret_cast<const jchar*>(
+      JS_GetStringChars(const_cast<JSString*>(str))), len);
+  return javaStr;
+}
+
+/*
+ * Returns a human-readable Java string describing the type of a
+ * JavaScript object.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _getTypeString
+ * Signature: (I)Ljava/lang/String;
+ */
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getTypeString
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._getTypeString", jsRootedValue);
+  jsval val = jsRootedValue->getValue();
+  JSContext* context = jsRootedValue->getContext();
+  JsValueType valueType = GetValueType(val);
+  const char* typeString = 0;
+  char buf[256];
+  if(valueType == JSVAL_TYPE_OBJECT) {
+    JSObject* jsObject = JSVAL_TO_OBJECT(val);
+    JSClass* objClass = JS_GET_CLASS(context, jsObject);
+    if (JS_InstanceOf(context, jsObject,
+        &gwt_nativewrapper_class, 0)) {
+      typeString = "Java object";
+    } else {
+      snprintf(buf, sizeof(buf), "class %s", objClass->name);
+      typeString = buf;
+    }
+  } else {
+    typeString = JsValueTypeStrings[valueType];
+  }
+  jstring returnValue = jniEnv->NewStringUTF(typeString);
+  return returnValue;
+}
+
+/*
+ * Unwraps a wrapped Java object from a JS object.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _getWrappedJavaObject
+ * Signature: (I)Ljava/lang/Object;
+ */
+extern "C" JNIEXPORT jobject JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getWrappedJavaObject
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._getWrappedJavaObject", jsRootedValue);
+  jsval val = jsRootedValue->getValue();
+  if(!JSVAL_IS_OBJECT(val)) {
+    tracer.throwHostedModeException(jniEnv, "Javascript value not an object");
+    return 0;
+  }
+  JSObject* jsObject = JSVAL_TO_OBJECT(val);
+  JSContext* context = jsRootedValue->getContext();
+  if(!JS_InstanceOf(context, jsObject, &gwt_nativewrapper_class, 0)) {
+    tracer.throwHostedModeException(jniEnv,
+      "Javascript object not a Java object");
+    return 0;
+  } 
+  jobject javaObject
+      = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(context, jsObject));
+  return javaObject;
+} 
+
+/*
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _isBoolean()
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isBoolean
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._isBoolean", jsRootedValue);
+  return jsRootedValue->isBoolean();
+}
+
+/*
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _isInt()
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isInt
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._isBoolean", jsRootedValue);
+  return jsRootedValue->isInt();
+}
+
+/*
+ * Checks if a JS object is a JavaScript object.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _isJavaScriptObject
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isJavaScriptObject
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._isJavaScriptObject", jsRootedValue);
+  jsval val = jsRootedValue->getValue();
+  bool returnValue = false;
+  if(JSVAL_IS_OBJECT(val)) {
+    JSObject* jsObject = JSVAL_TO_OBJECT(val);
+    returnValue = !JS_InstanceOf(jsRootedValue->getContext(), jsObject,
+        &gwt_nativewrapper_class, 0);
+    tracer.log("jsobject=%08x, isJSObject=%s", unsigned(jsObject),
+        returnValue ? "true" : "false");
+  } else {
+    tracer.log("not an object");
+  }
+  return returnValue;
+} 
+
+/*
+ * Checks if a JS object is a JavaScript String object.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _isJavaScriptString
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isJavaScriptString
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._isJavaScriptString", jsRootedValue);
+  bool returnValue = jsRootedValue->isJavaScriptStringObject();
+  tracer.log("value=%s", returnValue ? "true" : "false");
+  return returnValue;
+} 
+
+/*
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _isNull()
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isNull
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._isNull", jsRootedValue);
+  return jsRootedValue->isNull();
+}
+
+/*
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _isNumber()
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isNumber
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._isNumber", jsRootedValue);
+  return jsRootedValue->isNumber();
+}
+
+/*
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _isString()
+ * Signature: (I)Z
+ * 
+ * Handles the case of JavaScript String objects as well
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isString
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._isString", jsRootedValue);
+  return jsRootedValue->isString();
+}
+
+/*
+ * Checks if a JS object is undefined (void)
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _isUndefined
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isUndefined
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._isUndefined", jsRootedValue);
+  return jsRootedValue->isUndefined();
+} 
+
+/*
+ * Checks if a JS object is a wrapped Java object.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _isWrappedJavaObject
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1isWrappedJavaObject
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._isWrappedJavaObject", jsRootedValue);
+  jsval val = jsRootedValue->getValue();
+  bool returnValue = false;
+  if(JSVAL_IS_OBJECT(val)) {
+    JSObject* jsObject = JSVAL_TO_OBJECT(val);
+    returnValue = JS_InstanceOf(jsRootedValue->getContext(), jsObject,
+        &gwt_nativewrapper_class, 0);
+    tracer.log("jsobject=%08x, wrappedJava=%s", unsigned(jsObject),
+        returnValue ? "true" : "false");
+  } else {
+    tracer.log("not an object");
+  }
+  return returnValue;
+} 
+
+/*
+ * Set the JavaScript value to be a boolean of the supplied value.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _setBoolean()
+ * Signature: (IZ)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setBoolean
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jboolean val)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._setBoolean", jsRootedValue);
+  jsRootedValue->setBoolean(val == JNI_TRUE);
+  return;
+}
+
+/*
+ * Set the JavaScript value to be a double of the supplied value.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _setDouble()
+ * Signature: (ID)V
+ */
+extern "C" JNIEXPORT void
+JNICALL Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setDouble
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jdouble val)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._setDouble", jsRootedValue);
+  if(!jsRootedValue->setDouble(val)) {
+    tracer.throwHostedModeException(jniEnv, "Unable to allocate JS double");
+    return;
+  }
+}
+
+/*
+ * Set the Javascript value to be an integer.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _setInt()
+ * Signature: (II)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setInt
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jint val)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._setInt", jsRootedValue);
+  tracer.log("val=%d", val);
+  jsRootedValue->setInt(val);
+}
+
+/*
+ * Set the Javascript value to be another JsRootedValue's value.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _setJsRootedValue()
+ * Signature: (II)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setJsRootedValue
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jint jsOtherRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  JsRootedValue* jsOtherRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsOtherRootedValueInt);
+  Tracer tracer("JsValueMoz._setJsRootedValue", jsRootedValue);
+  jsRootedValue->setContextValue(jsOtherRootedValue->getContext(),
+      jsOtherRootedValue->getValue());
+}
+
+/*
+ * Set the JavaScript value to be null.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _setNull()
+ * Signature: (I)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setNull
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._setNull", jsRootedValue);
+  jsRootedValue->setNull();
+}
+
+/*
+ * Set the JavaScript value to be a string of the supplied value.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _setString()
+ * Signature: (ILjava/lang/String;)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setString
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jstring val)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._setString", jsRootedValue);
+  JStringWrap strVal(jniEnv, val);
+  const jchar* stringUTF16 = strVal.jstr();
+  if(!stringUTF16) {
+    tracer.throwHostedModeException(jniEnv, "Unable to retrieve Java string");
+    return;
+  }
+  tracer.log("string=%s", strVal.str());
+  if(!jsRootedValue->setString(reinterpret_cast<const wchar_t*>(stringUTF16))) {
+    tracer.throwHostedModeException(jniEnv, "Unable to allocate JS string");
+    return;
+  }
+}
+
+/*
+ * Set the JavaScript value to be undefined (void).
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _setUndefined()
+ * Signature: (I)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setUndefined
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._setUndefined", jsRootedValue);
+  jsRootedValue->setUndefined();
+}
+
+/*
+ * Wraps a Java object in a JS object.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _setWrappedJavaObject
+ * Signature: (ILjava/lang/Object)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setWrappedJavaObject
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jobject obj)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._setWrappedJavaObject", jsRootedValue);
+  JSContext* context = jsRootedValue->getContext();
+  JSObject* scriptWindow = JS_GetGlobalObject(context);
+  JSObject* newObj = JS_NewObject(context, &gwt_nativewrapper_class, 0,
+      scriptWindow);
+  if (!newObj) {
+    tracer.throwHostedModeException(jniEnv,
+        "Unable to allocate JS object to wrap Java object");
+    return;
+  }
+  tracer.log("jsobject=%08x", unsigned(newObj));
+  
+  // TODO(jat): how does this globalref get freed?
+  jobject dispObjRef = jniEnv->NewGlobalRef(obj);
+  if (!dispObjRef || jniEnv->ExceptionCheck()) {
+    tracer.throwHostedModeException(jniEnv,
+        "Unable to allocate global reference for JS wrapper");
+    return;
+  } 
+  if (!JS_SetPrivate(context, newObj, dispObjRef)) {
+    jniEnv->DeleteGlobalRef(dispObjRef);
+    tracer.throwHostedModeException(jniEnv,
+        "Unable to allocate global reference for JS wrapper");
+    return;
+  } 
+  // forcibly setup a "toString" method to override the default
+  jclass dispClass = jniEnv->GetObjectClass(obj);
+  if (jniEnv->ExceptionCheck()) {
+    jniEnv->DeleteGlobalRef(dispObjRef);
+    tracer.setFail("can't get object class");
+    return;
+  } 
+  jmethodID getFieldMeth = jniEnv->GetMethodID(dispClass, "getField",
+      "(Ljava/lang/String;I)V");
+  if (!getFieldMeth || jniEnv->ExceptionCheck()) {
+    jniEnv->DeleteGlobalRef(dispObjRef);
+    tracer.setFail("can't get getField method");
+    return;
+  } 
+  jstring ident = jniEnv->NewStringUTF("@java.lang.Object::toString()");
+  if (!ident || jniEnv->ExceptionCheck()) {
+    jniEnv->DeleteGlobalRef(dispObjRef);
+    tracer.setFail("can't create Java string for toString method name");
+    return;
+  }
+  // allocate a new root to hold the result of the getField call
+  JsRootedValue* toStringFunc = new JsRootedValue(context, JSVAL_VOID); 
+  jniEnv->CallVoidMethod(obj, getFieldMeth, ident,
+      NS_REINTERPRET_CAST(jint, toStringFunc));
+  if (toStringFunc->isUndefined() || jniEnv->ExceptionCheck()) {
+    jniEnv->DeleteGlobalRef(dispObjRef);
+    tracer.setFail("getField(toString) failed");
+    return;
+  } 
+  if (!JS_DefineProperty(context, newObj, "toString", toStringFunc->getValue(),
+      JS_PropertyStub, JS_PropertyStub, JSPROP_READONLY | JSPROP_PERMANENT)) {
+    jniEnv->DeleteGlobalRef(dispObjRef);
+    tracer.setFail("can't define JS toString method");
+    return;
+  }
+  jsRootedValue->setObject(newObj); 
+} 
+
+/*
+ * Wraps a Java function in a JS object.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _setWrappedFunction
+ * Signature: (ILjava/lang/String;Lcom/google/gwt/dev/shell/moz/DispatchMethod;)V
+ */
+extern "C" JNIEXPORT void JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1setWrappedFunction
+    (JNIEnv* jniEnv, jclass, jint jsRootedValueInt, jstring methodName,
+     jobject dispatchMethod)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._setWrappedFunction", jsRootedValue);
+  JSContext* context = jsRootedValue->getContext();
+  JSObject* scriptWindow = JS_GetGlobalObject(context);
+  JStringWrap nameStr(jniEnv, methodName);
+  if (!nameStr.str()) {
+    tracer.throwHostedModeException(jniEnv,
+       "null method name passed to setWrappedFunction");
+    return;
+  }
+  tracer.log("JsRootedValue=%08x, method=%s, obj=%08x", jsRootedValueInt,
+      nameStr.str(), unsigned(dispatchMethod));
+  JSFunction* function = JS_NewFunction(context, invokeJavaMethod, 0, JSFUN_LAMBDA, 0,
+      nameStr.str());
+  if (!function) {
+    tracer.throwHostedModeException(jniEnv, "JS_NewFunction failed");
+    return;
+  }
+  JSObject* funObj = JS_GetFunctionObject(function);
+  if (!funObj) {
+    tracer.throwHostedModeException(jniEnv, "JS_GetFunctionObject failed");
+    return;
+  }
+  // Create a wrapper object to hold and clean up dispMeth
+  JSObject* cleanupObj = JS_NewObject(context, &gwt_functionwrapper_class, 0,
+      scriptWindow);
+  if (!cleanupObj) {
+    tracer.throwHostedModeException(jniEnv, "JS_NewObject failed");
+    return;
+  }
+  jobject dispMethRef = jniEnv->NewGlobalRef(dispatchMethod);
+  if (!dispMethRef || jniEnv->ExceptionCheck()) {
+    return;
+  }
+
+  // Store our global ref in the wrapper object
+  if (!JS_SetPrivate(context, cleanupObj, dispMethRef)) {
+    jniEnv->DeleteGlobalRef(dispMethRef);
+    tracer.throwHostedModeException(jniEnv, "JS_SetPrivate(cleanupObj) failed");
+    return;
+  }
+  // Store the wrapper object in funObj's reserved slot
+  if(!JS_SetReservedSlot(context, funObj, 0, OBJECT_TO_JSVAL(cleanupObj))) {
+    // TODO(jat): what to do with global ref?
+    tracer.throwHostedModeException(jniEnv, "JS_SetReservedSlot failed");
+    return;
+  }
+  jsRootedValue->setObject(funObj);
+}      
+
+/*
+ * Returns a JavaScript value as a string.
+ * 
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _toString
+ * Signature: (I)Ljava/lang/String;
+ */
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1toString
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._toString", jsRootedValue);
+  jsval val = jsRootedValue->getValue();
+  JSContext* cx = jsRootedValue->getContext();
+
+  // if it is a JavaScript object that has a toString member function
+  // call that, otherwise call JS_ValueToString
+  if(JSVAL_IS_OBJECT(val)) {
+    JSObject* jsObject = JSVAL_TO_OBJECT(val);
+    jsval fval;
+    jsval rval;
+    if (!JS_InstanceOf(cx, jsObject, &gwt_nativewrapper_class, 0)
+        && JS_GetProperty(cx, jsObject, "toString", &fval)
+        && JS_ValueToFunction(cx, fval)
+        && JS_CallFunctionValue(cx, jsObject, fval, 0, 0, &rval)) {
+      // all the steps succeeded, so use the result of toString() instead
+      // of the value for JS_ValueToString below
+      val = rval;
+    }
+  }
+  JSString* str = JS_ValueToString(cx, val);
+  if (!str) {
+    return 0;
+  }
+  int len = JS_GetStringLength(str);
+  jstring javaStr =jniEnv->NewString(reinterpret_cast<const jchar*>(
+      JS_GetStringChars(str)), len);
+  return javaStr;
+} 
diff --git a/jni/linux/Makefile b/jni/linux/Makefile
index f5e2993..ede5520 100644
--- a/jni/linux/Makefile
+++ b/jni/linux/Makefile
@@ -1,4 +1,4 @@
-# Copyright 2006 Google Inc.
+# Copyright 2007 Google Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License"); you may not
 # use this file except in compliance with the License. You may obtain a copy of
@@ -15,25 +15,40 @@
 ##
 # Target settings
 ##
+MOZ_SRC=/usr/local/google/home/jat/src/mozilla
+JAVA_HOME=/usr/lib/j2sdk1.5-sun
+GWT_TOOLS=../../../gwt-tools
+
 GWT_ROOT = ../../
 OBJDIR  := $(GWT_ROOT)build/out/jni/linux/
 OUTDIR  := $(GWT_ROOT)build/jni/linux/
+STAGING := $(GWT_ROOT)build/staging/gwt-linux-0.0.0/
 OUT     := $(OUTDIR)libgwt-ll.so
 
 ##
+# The location to get .class files from for javah
+##
+CLASSDIR := $(GWT_ROOT)build/out/dev/linux/bin
+# use this if you want to use eclipse to build the class files during
+# development
+#CLASSDIR := $(GWT_ROOT)eclipse/dev/linux/bin
+
+##
 # Tools
 ##
-CXX      := gcc
+CXX      := g++
 AR       := ar
 STRIP    := strip
 LD       := $(CXX)
+JAVAH    := javah
 
 ##
 # List of source, object, and dependency paths plus the path to them
 ##
 SRCDIRS := ./:../core/
 VPATH   := .:../core
-SRCS    := gwt-ll.cpp Moz.cpp
+SRCS    := gwt-ll.cpp Moz.cpp JsValueMoz.cpp Tracer.cpp \
+	ExternalWrapper.cpp NativeWrapper.cpp JsRootedValue.cpp
 OBJS    := $(addprefix $(OBJDIR),$(SRCS:.cpp=.o))
 DEPS    := $(addprefix $(OBJDIR),$(SRCS:.cpp=.d))
 
@@ -46,7 +61,8 @@
 	$(GWT_TOOLS)/sdk/mozilla-1.7.12/include \
 	$(GWT_TOOLS)/sdk/mozilla-1.7.12/include/extra
 
-INCS := $(addprefix -i ,$(INCS)) $(addprefix -isystem ,$(SYSINCS))
+INCS := $(OBJDIR)
+INCS := $(addprefix -I ,$(INCS)) $(addprefix -isystem ,$(SYSINCS))
 
 ##
 # Libraries and library path
@@ -58,8 +74,8 @@
 # for notes on auto-dependency generation, see
 #   http://make.paulandlesley.org/autodep.html
 # -MP obviates the need for sed hackery
-CFLAGS   := -Os -fPIC -fno-omit-frame-pointer -fno-strict-aliasing -D_REENTRANT -c -MMD -MP -Wno-system-headers $(CFLAGS)
-LDFLAGS  := -s -fPIC -fno-omit-frame-pointer -fno-strict-aliasing -D_REENTRANT -Wl,-shared-gcc $(LDFLAGS)
+CFLAGS   := -ggdb -m32 -Os -fPIC -fno-omit-frame-pointer -fno-strict-aliasing -D_REENTRANT -MMD -MP -Wno-system-headers $(CFLAGS)
+LDFLAGS  := -ggdb -m32 -s -fPIC -fno-omit-frame-pointer -fno-strict-aliasing -D_REENTRANT -Wl,-shared-gcc $(LDFLAGS)
 
 #-------------------------------------------------------------------------------
 # Rules
@@ -69,6 +85,11 @@
 # default rule
 ##
 all: $(OUT)
+	@[ -d $(STAGING) ] || mkdir -p $(STAGING)
+	cp $(OUT) $(STAGING)
+
+install: $(OUT)
+	cp $(OUT) prebuilt/
 
 ##
 # Include the dependency rules
@@ -76,11 +97,33 @@
 -include $(DEPS)
 
 ##
+# javah-generated headers for native methods
+##
+$(OBJDIR)LowLevelMoz.h : $(CLASSDIR)/com/google/gwt/dev/shell/moz/LowLevelMoz.class
+	$(JAVAH) -classpath $(CLASSDIR) -o $(OBJDIR)LowLevelMoz.h com.google.gwt.dev.shell.moz.LowLevelMoz
+
+$(OBJDIR)JsValueMoz.h : $(CLASSDIR)/com/google/gwt/dev/shell/moz/JsValueMoz.class
+	$(JAVAH) -classpath $(CLASSDIR) -o $(OBJDIR)JsValueMoz.h com.google.gwt.dev.shell.moz.JsValueMoz
+
+##
+# Dependency rules for generated headers
+# TODO(jat): autogenerate these and others
+##
+$(OBJDIR)Moz.o: $(OBJDIR)LowLevelMoz.h
+$(OBJDIR)JsValueMoz.o: $(OBJDIR)JsValueMoz.h
+
+##
 # Compilation rule for cpp files
 ##
 $(OBJDIR)%.o : $(SRCDIR)%.cpp
 	@[ -d $(OBJDIR) ] || mkdir -p $(OBJDIR)
-	$(CXX) $(CFLAGS) $(INCS) -o $@ $<
+	$(CXX) -c $(CFLAGS) $(INCS) -o $@ $<
+
+%.i : $(SRCDIR)%.cpp
+	$(CXX) -E $(CFLAGS) $(INCS) -o $@ $<
+
+%.I : $(SRCDIR)%.cpp
+	$(CXX) -E -dDI $(CFLAGS) $(INCS) -o $@ $<
 
 ##
 # Actual output file
@@ -88,7 +131,7 @@
 $(OUT): $(OBJS)
 	@[ -d $(OUTDIR) ] || mkdir -p $(OUTDIR)
 	$(LD) -shared $(LDFLAGS) $(LIBPATH) -o $@ $^ $(LIBS)
-	$(STRIP) --strip-unneeded $@
+#	$(STRIP) --strip-unneeded $@
 
 ##
 # Clean rule
diff --git a/jni/linux/Moz.cpp b/jni/linux/Moz.cpp
index 6a174a6..424218b 100644
--- a/jni/linux/Moz.cpp
+++ b/jni/linux/Moz.cpp
@@ -1,719 +1,296 @@
-// Copyright 2005 Google Inc.
-// All Rights Reserved.
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
 
 // Mozilla-specific hosted-mode methods
 
+#define DEBUG
+
+#include <cstdio>
+
+//#define JS_GetClass JS_GetClassOld
+
 // Mozilla header files
-#include "nsIServiceManagerUtils.h"
-#include "nsComponentManagerUtils.h"
-#include "nsICategoryManager.h"
-#include "nsIScriptNameSpaceManager.h"
-#include "nsIScriptObjectOwner.h"
-#include "nsIScriptGlobalObject.h"
-#include "nsIScriptContext.h"
-#include "nsIDOMWindow.h"
-#include "nsIXPConnect.h"
-#include "nsCOMPtr.h"
-#include "nsAutoPtr.h"
+#include "mozilla-headers.h"
 
 #include <jni.h>
+#include "gwt-jni.h"
+#include "JsRootedValue.h"
+#include "ExternalWrapper.h"
+#include "Tracer.h"
+#include "JsStringWrap.h"
 
-static JNIEnv* gEnv = 0;
-static jclass gClass = 0;
+// include javah-generated header to make sure we match
+#include "LowLevelMoz.h"
 
 //#define FILETRACE
 //#define JAVATRACE
-#if defined(FILETRACE) && defined(JAVATRACE)
-#define TRACE(s) filetrace(s),javatrace(s)
-#elif defined(FILETRACE)
-#define TRACE(s) filetrace(s)
-#elif defined(JAVATRACE)
-#define TRACE(s) javatrace(s)
-#else
-#define TRACE(s) ((void)0)
+
+// TODO(jat) should be in a header
+extern nsCID kGwtExternalCID;
+
+JNIEnv* savedJNIEnv = 0;
+jclass lowLevelMozClass;
+
+static void PrintJSValue(JSContext* cx, jsval val, char* prefix="") {
+  JSType type = JS_TypeOfValue(cx, val);
+  const char* typeString=JS_GetTypeName(cx, type);
+  char buf[256];
+  char* p = buf;
+  p += snprintf(p, sizeof(buf)-(p-buf), "%s%s", prefix, typeString);
+  switch(type) {
+    case JSTYPE_VOID:
+      break;
+    case JSTYPE_BOOLEAN:
+      p += snprintf(p, sizeof(buf)-(p-buf), ": %s",
+          JSVAL_TO_BOOLEAN(val) ? "true" : "false");
+      break;
+    case JSTYPE_NUMBER:
+      if (JSVAL_IS_INT(val)) {
+        p += snprintf(p, sizeof(buf)-(p-buf), ": %d", JSVAL_TO_INT(val));
+      } else {
+        p += snprintf(p, sizeof(buf)-(p-buf), ": %lf",
+            (double)*JSVAL_TO_DOUBLE(val));
+      }
+      break;
+    case JSTYPE_OBJECT: {
+      JSObject* obj = JSVAL_TO_OBJECT(val);
+      if (!JSVAL_IS_OBJECT(val)) break;
+      JSClass* clazz = obj ? JS_GET_CLASS(cx, obj) : 0;
+      p += snprintf(p, sizeof(buf)-(p-buf), " @ %08x, class %s",
+          (unsigned)obj, clazz ? clazz->name : "<null>");
+      break;
+    }
+    case JSTYPE_FUNCTION:
+    case JSTYPE_LIMIT:
+      break;
+    case JSTYPE_STRING: {
+      JsStringWrap str(cx, JSVAL_TO_STRING(val));
+      p += snprintf(p, sizeof(buf)-(p-buf), ": %.*s", str.length(), str.bytes());
+      break;
+    }
+  }
+  Tracer::log("%s", buf);
+}
+
+
+static bool InitGlobals(JNIEnv* env, jclass llClass) {
+  if (savedJNIEnv)
+    return false;
+
+#ifdef FILETRACE
+  Tracer::setFile("gwt-ll.log");
+#endif // FILETRACE
+
+#ifdef JAVATRACE
+  Tracer::setJava(env, llClass);
+#endif // JAVATRACE
+
+#ifdef DEBUG
+  Tracer::setLevel(Tracer::LEVEL_DEBUG);
 #endif
 
-#ifdef FILETRACE
-static FILE* gout = 0;
-static void filetrace(const char* s)
-{
-    fprintf(gout, s);
-    fprintf(gout, "\n");
-    fflush(gout);
-}
-#endif // FILETRACE
-
-#ifdef JAVATRACE
-static jmethodID gTraceMethod = 0;
-static void javatrace(const char* s)
-{
-    if (!gEnv->ExceptionCheck())
-    {
-        jstring out = gEnv->NewStringUTF(s);
-        if (!gEnv->ExceptionCheck())
-            gEnv->CallStaticVoidMethod(gClass, gTraceMethod, out);
-        else
-            gEnv->ExceptionClear();
-    }
-}
-#endif // JAVATRACE
-
-static bool InitGlobals(JNIEnv* env, jclass llClass)
-{
-    if (gEnv)
-        return true;
-
-#ifdef FILETRACE
-    gout = fopen("gwt-ll.log", "w");
-    filetrace("LOG STARTED");
-#endif // FILETRACE
-
-    gClass = static_cast<jclass>(env->NewGlobalRef(llClass));
-    if (!gClass || env->ExceptionCheck())
-        return false;
-
-#ifdef JAVATRACE
-    gTraceMethod = env->GetStaticMethodID(gClass, "trace", "(Ljava/lang/String;)V");
-    if (!gTraceMethod || env->ExceptionCheck())
-        return false;
-#endif // JAVATRACE
-
-    gEnv = env;
-    return true;
+  savedJNIEnv = env;
+  lowLevelMozClass = static_cast<jclass>(env->NewGlobalRef(llClass));
+  return true;
 }
 
-struct JStringWrap
-{
-    JStringWrap(JNIEnv* env, jstring str): env(env), s(str), p(0), jp(0) { }
-    ~JStringWrap() { if (p) env->ReleaseStringUTFChars(s, p); if (jp) env->ReleaseStringChars(s, jp); }
-    const char* str() { if (!p) p = env->GetStringUTFChars(s, 0); return p; }
-    const jchar* jstr() { if (!jp) jp = env->GetStringChars(s, 0); return jp; }
-private:
-    JNIEnv* env;
-    jstring s;
-    const char* p;
-    const jchar* jp;
-};
-
-static void hextrace(int val) {
-    char buf[20];
-    const char* hex = "0123456789ABCDEF";
-    for (int i = 7; i >= 0; --i) {
-        buf[i] = hex[val & 0xF];
-        val >>= 4;
-    }
-    buf[8] = 0;
-    TRACE(buf);
+/*
+ * Print the current Java exception.
+ */
+static void PrintJavaException(JNIEnv* env) {
+  jobject exception = env->ExceptionOccurred();
+  if (!exception) return;
+  fprintf(stderr, "Exception occurred:\n");
+  env->ExceptionDescribe();
+  env->DeleteLocalRef(exception);
 }
 
-static JSBool gwt_invoke(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+/* Called from JavaScript to call a Java method that has previously been
+ * wrapped. 
+ */ 
+JSBool invokeJavaMethod(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+    jsval *rval)
 {
-    TRACE("ENTER gwt_invoke");
+  Tracer tracer("invokeJavaMethod");
 
-    // I kid you not; this is how XPConnect gets their function object so they can
-    // multiplex dispatch the call from a common site.  See XPCDispObject.cpp(466)
-    //
-    // I now have a secondary confirmation that this trick is legit.  brandon@mozilla.org
-    // writes:
-    //
-    // argv[-2] is part of the JS API, unabstracted.  Just as argv[0] is the
-    // first argument (if argc != 0), argv[-1] is the |this| parameter (equal
-    // to OBJECT_TO_JSVAL(obj) in a native method with the standard |obj|
-    // second formal parameter name), and argv[-2] is the callee object, tagged
-    // as a jsval.
-    if (JS_TypeOfValue(cx, argv[-2]) != JSTYPE_FUNCTION)
-        return TRACE("FAIL gwt_invoke: JSTYPE_FUNCTION"), JS_FALSE;
-
-    JSObject* funObj = JSVAL_TO_OBJECT(argv[-2]);
-    jsval jsCleanupObj;
-
-    // Pull the wrapper object out of the funObj's reserved slot
-    if (!JS_GetReservedSlot(cx, funObj, 0, &jsCleanupObj))
-        return TRACE("FAIL gwt_invoke: JS_GetReservedSlot"), JS_FALSE;
-
-    JSObject* cleanupObj = JSVAL_TO_OBJECT(jsCleanupObj);
-    if (!cleanupObj)
-        return TRACE("FAIL gwt_invoke: cleanupObj"), JS_FALSE;
-
-    // Get dispMeth global ref out of the wrapper object
-    jobject dispMeth = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, cleanupObj));
-    if (!dispMeth)
-        return TRACE("FAIL gwt_invoke: dispMeth"), JS_FALSE;
-
-    jclass dispClass = gEnv->GetObjectClass(dispMeth);
-    if (!dispClass || gEnv->ExceptionCheck())
-        return TRACE("FAIL gwt_invoke: GetObjectClass"), JS_FALSE;
-
-    jmethodID invokeID = gEnv->GetMethodID(dispClass, "invoke", "(I[I)I");
-    if (!invokeID || gEnv->ExceptionCheck())
-        return TRACE("FAIL gwt_invoke: GetMethodID"), JS_FALSE;
-
-    jintArray jsargs = gEnv->NewIntArray(argc);
-    if (!jsargs || gEnv->ExceptionCheck())
-        return TRACE("FAIL gwt_invoke: NewIntArray"), JS_FALSE;
-
-    gEnv->SetIntArrayRegion(jsargs, 0, argc, (jint*)argv);
-    if (gEnv->ExceptionCheck())
-        return TRACE("FAIL gwt_invoke: SetIntArrayRegion"), JS_FALSE;
-
-    *rval = gEnv->CallIntMethod(dispMeth, invokeID, argv[-1], jsargs);
-    if (gEnv->ExceptionCheck())
-        return TRACE("FAIL gwt_invoke: java exception is active"), JS_FALSE;
-
-    if (JS_IsExceptionPending(cx))
-        return TRACE("FAIL gwt_invoke: js exception is active"), JS_FALSE;
-
-    TRACE("SUCCESS gwt_invoke");
-    return JS_TRUE;
-}
-
-static JSBool getJavaPropertyStats(JSContext *cx, JSObject *obj, jsval id, jclass& dispClass, jobject& dispObj, jstring& jident)
-{
-    if (!JSVAL_IS_STRING(id))
-        return JS_FALSE;
-
-    jident = gEnv->NewString(JS_GetStringChars(JSVAL_TO_STRING(id)), JS_GetStringLength(JSVAL_TO_STRING(id)));
-    if (!jident || gEnv->ExceptionCheck())
-        return JS_FALSE;
-
-    dispObj = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
-    if (!dispObj)
-        return JS_FALSE;
-
-    dispClass = gEnv->GetObjectClass(dispObj);
-    if (gEnv->ExceptionCheck())
-        return JS_FALSE;
-
-    return JS_TRUE;
-}
-
-static JSBool JS_DLL_CALLBACK gwt_nativewrapper_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
-{
-    TRACE("ENTER gwt_nativewrapper_setProperty");
-
-    jclass dispClass;
-    jobject dispObj;
-    jstring ident;
-    if (!getJavaPropertyStats(cx,obj,id,dispClass,dispObj,ident))
-        return JS_FALSE;
-
-    jmethodID setFieldMeth = gEnv->GetMethodID(dispClass, "setField", "(Ljava/lang/String;I)V");
-    if (!setFieldMeth || gEnv->ExceptionCheck())
-        return JS_FALSE;
-
-    gEnv->CallVoidMethod(dispObj, setFieldMeth, ident, *vp);
-    if (gEnv->ExceptionCheck())
-        return JS_FALSE;
-
-    TRACE("SUCCESS gwt_nativewrapper_setProperty");
-    return JS_TRUE;
-}
-
-static JSBool JS_DLL_CALLBACK gwt_nativewrapper_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
-{
-    TRACE("ENTER gwt_nativewrapper_getProperty");
-
-    if (*vp != JSVAL_VOID)
-        return TRACE("SUCCESS, already defined"), JS_TRUE;
-
-    jclass dispClass;
-    jobject dispObj;
-    jstring ident;
-    if (!getJavaPropertyStats(cx,obj,id,dispClass,dispObj,ident))
-        return JS_FALSE;
-
-    jmethodID getFieldMeth = gEnv->GetMethodID(dispClass, "getField", "(Ljava/lang/String;)I");
-    if (!getFieldMeth || gEnv->ExceptionCheck())
-        return JS_FALSE;
-
-    *vp = gEnv->CallIntMethod(dispObj, getFieldMeth, ident);
-    if (gEnv->ExceptionCheck())
-        return JS_FALSE;
-
-    TRACE("SUCCESS gwt_nativewrapper_getProperty");
-    return JS_TRUE;
-}
-
-static void JS_DLL_CALLBACK gwt_nativewrapper_finalize(JSContext *cx, JSObject *obj)
-{
-    jobject dispObj = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
-    if (dispObj)
-        gEnv->DeleteGlobalRef(dispObj);
-}
-
-static JSClass gwt_functionwrapper_class = {
-    "gwt_functionwrapper_class", JSCLASS_HAS_PRIVATE,
-    JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
-    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, gwt_nativewrapper_finalize,
-    JSCLASS_NO_OPTIONAL_MEMBERS
-};
-
-static JSClass gwt_nativewrapper_class = {
-    "gwt_nativewrapper_class", JSCLASS_HAS_PRIVATE,
-    JS_PropertyStub, JS_PropertyStub, gwt_nativewrapper_getProperty, gwt_nativewrapper_setProperty,
-    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, gwt_nativewrapper_finalize,
-    JSCLASS_NO_OPTIONAL_MEMBERS
-};
-
-static JSBool gwtOnLoad(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
-{
-    TRACE("ENTER gwtOnLoad");
-
-    if (argc < 2)
-        return JS_FALSE;
-
-    JSObject* scriptWindow = 0;
-    if (argv[0] != JSVAL_NULL && argv[0] != JSVAL_VOID) {
-        if (!JS_ValueToObject(cx, argv[0], &scriptWindow))
-            return JS_FALSE;
-    }
-
-    JSString* moduleName = 0;
-    if (argv[1] != JSVAL_NULL && argv[1] != JSVAL_VOID) {
-        moduleName = JS_ValueToString(cx, argv[1]);
-    }
-
-    nsCOMPtr<nsIScriptGlobalObject> scriptGlobal(0);
-    if (scriptWindow) {
-        nsCOMPtr<nsIXPConnect> xpConnect = do_GetService(nsIXPConnect::GetCID());
-        nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
-        xpConnect->GetWrappedNativeOfJSObject(cx, scriptWindow, getter_AddRefs(wrappedNative));
-        if (wrappedNative) {
-            nsCOMPtr<nsISupports> native;
-            wrappedNative->GetNative(getter_AddRefs(native));
-            scriptGlobal = do_QueryInterface(native);
-        }
-    }
-
-    jstring jModuleName(0);
-    if (moduleName) {
-        jModuleName = gEnv->NewString(JS_GetStringChars(moduleName), JS_GetStringLength(moduleName));
-        if (!jModuleName || gEnv->ExceptionCheck())
-            return JS_FALSE;
-    }
-    
-    jobject externalObject = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
-    jclass objClass = gEnv->GetObjectClass(externalObject);
-    if (!objClass || gEnv->ExceptionCheck())
-        return JS_FALSE;
-    
-    jmethodID methodID = gEnv->GetMethodID(objClass, "gwtOnLoad", "(ILjava/lang/String;)Z");
-    if (!methodID || gEnv->ExceptionCheck())
-        return JS_FALSE;
-    
-    jboolean result = gEnv->CallBooleanMethod(externalObject, methodID, NS_REINTERPRET_CAST(jint, scriptGlobal.get()), jModuleName);
-    if (gEnv->ExceptionCheck())
-        return JS_FALSE;
-
-    *rval = BOOLEAN_TO_JSVAL((result == JNI_FALSE) ? JS_FALSE : JS_TRUE);
-    TRACE("SUCCESS gwtOnLoad");
-    return JS_TRUE;
-}
-
-class ExternalWrapper : public nsIScriptObjectOwner
-{
-public:
-    NS_DECL_ISUPPORTS
-    NS_IMETHOD GetScriptObject(nsIScriptContext *aContext, void** aScriptObject);
-    NS_IMETHOD SetScriptObject(void* aScriptObject);
-    ExternalWrapper(): mScriptObject(0) { }
-private:
-    ~ExternalWrapper() { }
-    void *mScriptObject;
-};
-
-NS_IMPL_ISUPPORTS1(ExternalWrapper, nsIScriptObjectOwner)
-
-static JSBool JS_DLL_CALLBACK gwt_external_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
-{
+  // I kid you not; this is how XPConnect gets their function object so they can
+  // multiplex dispatch the call from a common site.  See XPCDispObject.cpp(466)
+  //
+  // I now have a secondary confirmation that this trick is legit.
+  // brandon@mozilla.org writes:
+  //
+  // argv[-2] is part of the JS API, unabstracted.  Just as argv[0] is the
+  // first argument (if argc != 0), argv[-1] is the |this| parameter (equal
+  // to OBJECT_TO_JSVAL(obj) in a native method with the standard |obj|
+  // second formal parameter name), and argv[-2] is the callee object, tagged
+  // as a jsval.
+  if (JS_TypeOfValue(cx, argv[-2]) != JSTYPE_FUNCTION) {
+    tracer.setFail("not a function type");
     return JS_FALSE;
-}
+  }
+  JSObject* funObj = JSVAL_TO_OBJECT(argv[-2]);
 
-static JSBool JS_DLL_CALLBACK gwt_external_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
-{
-    TRACE("ENTER gwt_external_getProperty");
+  // Pull the wrapper object out of the funObj's reserved slot
+  jsval jsCleanupObj;
+  if (!JS_GetReservedSlot(cx, funObj, 0, &jsCleanupObj)) {
+    tracer.setFail("JS_GetReservedSlot failed");
+    return JS_FALSE;
+  }
+  JSObject* cleanupObj = JSVAL_TO_OBJECT(jsCleanupObj);
+  if (!cleanupObj) {
+    tracer.setFail("cleanupObj is null");
+    return JS_FALSE;
+  }
 
-    if (*vp != JSVAL_VOID)
-        return TRACE("SUCCESS, already defined"), JS_TRUE;
+  // Get DispatchMethod instance out of the wrapper object
+  jobject dispMeth =
+      NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, cleanupObj));
+  if (!dispMeth) {
+    tracer.setFail("dispMeth is null");
+    return JS_FALSE;
+  }
+  jclass dispClass = savedJNIEnv->GetObjectClass(dispMeth);
+  if (!dispClass || savedJNIEnv->ExceptionCheck()) {
+      tracer.setFail("GetObjectClass returns null");
+      return JS_FALSE;
+  }
 
-    if (!JSVAL_IS_STRING(id))
-        return TRACE("FAIL 1"), JS_FALSE;
+  // lookup the invoke method on the dispatch object
+  jmethodID invokeID =
+      savedJNIEnv->GetMethodID(dispClass, "invoke", "(II[II)V");
+  if (!invokeID || savedJNIEnv->ExceptionCheck()) {
+    tracer.setFail("GetMethodID failed");
+    return JS_FALSE;
+  }
 
-    jstring jident = gEnv->NewString(JS_GetStringChars(JSVAL_TO_STRING(id)), JS_GetStringLength(JSVAL_TO_STRING(id)));
-    if (!jident || gEnv->ExceptionCheck())
-        return TRACE("FAIL 2"), JS_FALSE;
+  // create an array of integers to hold the JsRootedValue pointers passed
+  // to the invoke method
+  jintArray args = savedJNIEnv->NewIntArray(argc);
+  if (!args || savedJNIEnv->ExceptionCheck()) {
+    tracer.setFail("NewIntArray failed");
+    return JS_FALSE;
+  }
 
-    jobject externalObject = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
-    jclass objClass = gEnv->GetObjectClass(externalObject);
-    if (!objClass || gEnv->ExceptionCheck())
-        return TRACE("FAIL 4"), JS_FALSE;
+  // these arguments are already rooted by the JS interpreter, but we
+  // can't easily take advantage of that without complicating the JsRootedValue
+  // interface.
+  
+  // argv[-1] is OBJECT_TO_JSVAL(this)
+  JsRootedValue* jsThis = new JsRootedValue(cx, argv[-1]);
+  tracer.log("jsthis=%08x, RV=%08x", unsigned(argv[-1]), unsigned(jsThis));
 
-    jmethodID methodID = gEnv->GetMethodID(objClass, "resolveReference", "(Ljava/lang/String;)I");
-    if (!methodID || gEnv->ExceptionCheck())
-        return TRACE("FAIL 5"), JS_FALSE;
-    int retval = gEnv->CallIntMethod(externalObject, methodID, jident);
-    if (gEnv->ExceptionCheck())
-        return TRACE("FAIL 6"), JS_FALSE;
-    *vp = retval;
+  // create JsRootedValues for arguments  
+  JsRootedValue *jsArgs[argc]; 
+  for (uintN i = 0; i < argc; ++i) {
+    jsArgs[i] = new JsRootedValue(cx, argv[i]);
+  }
+  savedJNIEnv->SetIntArrayRegion(args, 0, argc,
+      reinterpret_cast<jint*>(jsArgs));
+  if (savedJNIEnv->ExceptionCheck()) {
+    tracer.setFail("SetIntArrayRegion failed");
+    return JS_FALSE;
+  }
+  
+  // slot for return value
+  JsRootedValue* jsReturnVal = new JsRootedValue(cx);
 
-    TRACE("SUCCESS gwt_external_getProperty");
-    return JS_TRUE;
-}
+  // TODO(jat): small window here where invocation may fail before Java
+  // takes ownership of the JsRootedValue objects.  One solution would be
+  // to reference-count them between Java and C++ (so the reference count
+  // would always be 0, 1, or 2).  Also setField has a similar problem.
+  // I plan to fix this when switching away from Java holding pointers to
+  // C++ objects as part of the fix for 64-bit support (which we could
+  // accomplish inefficiently by changing int to long everywhere, but there
+  // are other 64-bit issues to resolve and we need to reduce the number of
+  // roots the JS interpreter has to search.
+  
+  // call Java method
+  savedJNIEnv->CallVoidMethod(dispMeth, invokeID, reinterpret_cast<int>(cx),
+      reinterpret_cast<int>(jsThis), args,
+      reinterpret_cast<int>(jsReturnVal));
+  
+  JSBool returnValue = JS_TRUE;
+  
+  if (savedJNIEnv->ExceptionCheck()) {
+    tracer.log("dispMeth=%08x", unsigned(dispMeth));
+    tracer.setFail("java exception is active:");
+    PrintJavaException(savedJNIEnv);
+    returnValue = JS_FALSE;
+  } else if (JS_IsExceptionPending(cx)) {
+    tracer.setFail("js exception is active");
+    returnValue = JS_FALSE;
+  }
 
-static void JS_DLL_CALLBACK gwt_external_finalize(JSContext *cx, JSObject *obj)
-{
-    jobject externalObject = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
-    if (externalObject)
-        gEnv->DeleteGlobalRef(externalObject);
-    JS_FinalizeStub(cx,obj);
-}
+  // extract return value
+  *rval = jsReturnVal->getValue();
 
-static JSClass gwt_external_class = {
-    "gwt_external_class", JSCLASS_HAS_PRIVATE,
-    JS_PropertyStub, JS_PropertyStub, gwt_external_getProperty, gwt_external_setProperty,
-    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, gwt_external_finalize,
-    JSCLASS_NO_OPTIONAL_MEMBERS
-};
+#if 0
+  // NOTE: C++ objects are not cleaned up here because Java now owns them.
+  // TODO(jat): if reference-counted, they *do* need to be Released here.
+  
+  // free JsRootedValues
+  for (uintN i = 0; i < argc; ++i) {
+    delete jsArgs[i];
+  }
+  delete jsThis;
+  delete jsReturnVal;
+#endif
 
-class nsJSObjectLocker : public nsISupports
-{
-public:
-    NS_DECL_ISUPPORTS
-    nsJSObjectLocker(JSContext* cx, jsval val): mCx(cx), mVal(val) { if (mCx && mVal) JSVAL_LOCK(mCx,mVal); }
-
-    JSContext* const mCx;
-    const jsval mVal;
-
-    // Major hack; compare other object's vtable ptrs to this one to do crude RTTI
-    static nsJSObjectLocker sJSObjectLocker;
-
-private:
-    ~nsJSObjectLocker() { if (mCx && mVal) JSVAL_UNLOCK(mCx,mVal); }
-};
-NS_IMPL_ISUPPORTS0(nsJSObjectLocker)
-
-// Major hack; compare other object's vtable ptrs to this one to do crude RTTI
-nsJSObjectLocker nsJSObjectLocker::sJSObjectLocker(0,0);
-
-NS_IMETHODIMP ExternalWrapper::GetScriptObject(nsIScriptContext *aContext, void** aScriptObject)
-{
-    TRACE("ENTER ExternalWrapper::GetScriptObject");
-
-    if (!aScriptObject)
-        return TRACE("FAIL 0"), NS_ERROR_INVALID_POINTER;
-
-    if (!mScriptObject)
-    {
-        *aScriptObject = 0;
-
-        nsIScriptGlobalObject* globalObject = aContext->GetGlobalObject();
-        nsCOMPtr<nsIDOMWindow> domWindow(do_QueryInterface(globalObject));
-        if (!domWindow)
-            return TRACE("FAIL 1"), NS_ERROR_UNEXPECTED;
-        
-        nsCOMPtr<nsIDOMWindow> topWindow;
-        domWindow->GetTop(getter_AddRefs(topWindow));
-        if (!topWindow)
-            return TRACE("FAIL 2"), NS_ERROR_UNEXPECTED;
-
-        jmethodID methodID = gEnv->GetStaticMethodID(gClass, "createExternalObjectForDOMWindow", "(I)Lcom/google/gwt/dev/shell/moz/LowLevelMoz$ExternalObject;");
-        if (!methodID || gEnv->ExceptionCheck())
-            return TRACE("FAIL 3"), NS_ERROR_UNEXPECTED;
-
-        jobject externalObject = gEnv->CallStaticObjectMethod(gClass, methodID, NS_REINTERPRET_CAST(jint, topWindow.get()));
-        if (!externalObject || gEnv->ExceptionCheck())
-            return TRACE("FAIL 4"), NS_ERROR_UNEXPECTED;
-        externalObject = gEnv->NewGlobalRef(externalObject);
-        if (!externalObject || gEnv->ExceptionCheck())
-            return TRACE("FAIL 5"), NS_ERROR_UNEXPECTED;
-
-        JSContext* cx = NS_REINTERPRET_CAST(JSContext*,aContext->GetNativeContext());
-        if (!cx)
-            return TRACE("FAIL 6"), NS_ERROR_UNEXPECTED;
-        JSObject* newObj = JS_NewObject(cx, &gwt_external_class, 0, globalObject->GetGlobalJSObject());
-        if (!newObj)
-            return TRACE("FAIL 7"), NS_ERROR_OUT_OF_MEMORY;
-        if (!JS_SetPrivate(cx, newObj, externalObject)) {
-            gEnv->DeleteGlobalRef(externalObject);
-            return TRACE("FAIL 8"), NS_ERROR_UNEXPECTED;
-        }
-        if (!JS_DefineFunction(cx, newObj, "gwtOnLoad", gwtOnLoad, 3,
-                JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
-            return TRACE("FAIL 9"), NS_ERROR_UNEXPECTED;
-
-        mScriptObject = newObj;
-    }
-
-    *aScriptObject = mScriptObject;
-    TRACE("SUCCESS ExternalWrapper::GetScriptObject");
-    return NS_OK;
-}
-
-NS_IMETHODIMP ExternalWrapper::SetScriptObject(void* aScriptObject)
-{
-    mScriptObject = aScriptObject;
-    return NS_OK;
-}
-
-class nsRpExternalFactory : public nsIFactory
-{
-public:
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIFACTORY
-    nsRpExternalFactory() { }
-private:
-    ~nsRpExternalFactory() { }
-};
-
-NS_IMPL_ISUPPORTS1(nsRpExternalFactory, nsIFactory)
-
-NS_IMETHODIMP nsRpExternalFactory::CreateInstance(nsISupports *aOuter, const nsIID & aIID, void** aResult)
-{
-    TRACE("ENTER nsRpExternalFactory::CreateInstance");
-
-    if (!aResult)
-        return NS_ERROR_INVALID_POINTER;
-
-    *aResult  = NULL;
-
-    if (aOuter)
-        return NS_ERROR_NO_AGGREGATION;
-
-    nsISupports* object = new ExternalWrapper();
-    if (!object)
-        return NS_ERROR_OUT_OF_MEMORY;
-        
-    nsresult result = object->QueryInterface(aIID, aResult);
-    if (!*aResult || NS_FAILED(result))
-        delete object;
-    else
-        TRACE("SUCCESS nsRpExternalFactory::CreateInstance");
-    return result;
-}
-
-NS_IMETHODIMP nsRpExternalFactory::LockFactory(PRBool lock)
-{
-    return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-#define GWT_EXTERNAL_FACTORY_CID \
-{ 0xF56E23F8, 0x5D06, 0x47F9, \
-{ 0x88, 0x5A, 0xD9, 0xCA, 0xC3, 0x38, 0x41, 0x7F } }
-#define GWT_EXTERNAL_CONTRACTID "@com.google/GWT/external;1"
-
-static NS_DEFINE_CID(kGwtExternalCID, GWT_EXTERNAL_FACTORY_CID);
-
-extern "C" {
-
-/*
- * Class:     com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method:    _coerceTo31Bits
- * Signature: (II[I)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1coerceTo31Bits
-  (JNIEnv* env, jclass, jint scriptObjInt, jint v, jintArray rval)
-{
-    nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
-    nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
-    if (!scriptContext)
-       return JNI_FALSE;
-    JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
-
-    jint r;
-    if (!JS_ValueToECMAInt32(cx, v, &r))
-        return JNI_FALSE;
-    env->SetIntArrayRegion(rval, 0, 1, &r);
-    if (env->ExceptionCheck())
-       return JNI_FALSE;
-    return JNI_TRUE;
+  return returnValue;
 }
 
 /*
- * Class:     com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method:    _coerceToBoolean
- * Signature: (II[Z)Z
+ * Helper function to get reference Java attributes from Javascript.
+ * 
+ * cx - JSContext pointer
+ * obj - JavaScript object which is a wrapped Java object
+ * id - property name, as a jsval string
+ * dispClass - output parameter of DispatchMethod subclass
+ * dispObj - output parameter of Java object
+ * jident - output parameter of property name as a Java string
  */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1coerceToBoolean
-  (JNIEnv* env, jclass, jint scriptObjInt, jint v, jbooleanArray rval)
+JSBool getJavaPropertyStats(JSContext *cx, JSObject *obj, jsval id,
+    jclass& dispClass, jobject& dispObj, jstring& jident)
 {
-    nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
-    nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
-    if (!scriptContext)
-       return JNI_FALSE;
-    JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
+  Tracer tracer("getJavaPropertyStats");
+  if (!JSVAL_IS_STRING(id)) {
+    tracer.setFail("id is not a string");
+    return JS_FALSE;
+  }
 
-    JSBool r;
-    if (!JS_ValueToBoolean(cx, v, &r))
-        return JNI_FALSE;
-    jboolean jr = (r == JS_FALSE) ? JNI_FALSE : JNI_TRUE;
-    env->SetBooleanArrayRegion(rval, 0, 1, &jr);
-    if (env->ExceptionCheck())
-       return JNI_FALSE;
-    return JNI_TRUE;
-}
+  jident = savedJNIEnv->NewString(JS_GetStringChars(JSVAL_TO_STRING(id)),
+      JS_GetStringLength(JSVAL_TO_STRING(id)));
+  if (!jident || savedJNIEnv->ExceptionCheck()) {
+    tracer.setFail("unable to create Java string");
+    return JS_FALSE;
+  }
 
-/*
- * Class:     com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method:    _coerceToDouble
- * Signature: (II[D)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1coerceToDouble
-  (JNIEnv* env, jclass, jint scriptObjInt, jint v, jdoubleArray rval)
-{
-    nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
-    nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
-    if (!scriptContext)
-       return JNI_FALSE;
-    JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
+  dispObj = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
+  if (!dispObj) {
+    tracer.setFail("can't get dispatch object");
+    return JS_FALSE;
+  }
 
-    jdouble r;
-    if (!JS_ValueToNumber(cx, v, &r))
-        return JNI_FALSE;
-    env->SetDoubleArrayRegion(rval, 0, 1, &r);
-    if (env->ExceptionCheck())
-       return JNI_FALSE;
-    return JNI_TRUE;
-}
+  dispClass = savedJNIEnv->GetObjectClass(dispObj);
+  if (savedJNIEnv->ExceptionCheck()) {
+    tracer.setFail("can't get class of dispatch object");
+    return JS_FALSE;
+  }
 
-/*
- * Class:     com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method:    _coerceToString
- * Signature: (II[Ljava/lang/String;)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1coerceToString
-  (JNIEnv* env, jclass, jint scriptObjInt, jint v, jobjectArray rval)
-{
-    jstring jr(0);
-    if (v != JSVAL_NULL && v != JSVAL_VOID) {
-        nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
-        nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
-        if (!scriptContext)
-            return JNI_FALSE;
-        JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
-
-        JSString* str = JS_ValueToString(cx, v);
-        if (!str)
-            return JNI_FALSE;
-        jr = env->NewString(JS_GetStringChars(str), JS_GetStringLength(str));
-        if (env->ExceptionCheck())
-            return JNI_FALSE;
-    }
-    env->SetObjectArrayElement(rval, 0, jr);
-    if (env->ExceptionCheck())
-       return JNI_FALSE;
-    return JNI_TRUE;
-}
-
-/*
- * Class:     com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method:    _convert31Bits
- * Signature: (II[I)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1convert31Bits
-  (JNIEnv* env, jclass, int scriptObjInt, jint v, jintArray rval)
-{
-    jint r = INT_TO_JSVAL(v);
-    env->SetIntArrayRegion(rval, 0, 1, &r);
-    if (env->ExceptionCheck())
-       return JNI_FALSE;
-    return JNI_TRUE;
-}
-
-/*
- * Class:     com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method:    _convertBoolean
- * Signature: (IZ[I)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1convertBoolean
-  (JNIEnv* env, jclass, int scriptObjInt, jboolean v, jintArray rval)
-{
-    jint r = BOOLEAN_TO_JSVAL((v == JNI_FALSE) ? JS_FALSE : JS_TRUE);
-    env->SetIntArrayRegion(rval, 0, 1, &r);
-    if (env->ExceptionCheck())
-       return JNI_FALSE;
-    return JNI_TRUE;
-}
-
-/*
- * Class:     com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method:    _convertDouble
- * Signature: (ID[I)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1convertDouble
-  (JNIEnv* env, jclass, int scriptObjInt, jdouble v, jintArray rval)
-{
-    nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
-    nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
-    if (!scriptContext)
-       return JNI_FALSE;
-    JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
-
-    jsval rv;
-    if (!JS_NewDoubleValue(cx, jsdouble(v), &rv))
-       return JNI_FALSE;
-    jint r = rv;
-    env->SetIntArrayRegion(rval, 0, 1, &r);
-    if (env->ExceptionCheck())
-       return JNI_FALSE;
-    return JNI_TRUE;
-}
-
-/*
- * Class:     com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method:    _convertString
- * Signature: (ILjava/lang/String;[I)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1convertString
-  (JNIEnv* env, jclass, int scriptObjInt, jstring v, jintArray rval)
-{
-    jint r = 0;
-    if (v) {
-        JStringWrap jv(env, v);
-        if (!jv.jstr())
-            return JNI_FALSE;
-
-        nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
-        nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
-        if (!scriptContext)
-           return JNI_FALSE;
-        JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
-
-        JSString* str = JS_NewUCStringCopyZ(cx, jv.jstr());
-        if (!str)
-           return JNI_FALSE;
-
-        r = STRING_TO_JSVAL(str);
-    }
-
-    env->SetIntArrayRegion(rval, 0, 1, &r);
-    if (env->ExceptionCheck())
-       return JNI_FALSE;
-    return JNI_TRUE;
-}
-
-/*
- * Class:     com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method:    _executeScript
- * Signature: (ILjava/lang/String;)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1executeScript
-  (JNIEnv* env, jclass llClass, jint scriptObject, jstring code)
-{
-    JStringWrap jcode(env, code);
-    if (!jcode.jstr())
-        return JNI_FALSE;
-
-    nsIScriptGlobalObject* globalObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObject);
-    nsCOMPtr<nsIScriptContext> scriptContext(globalObject->GetContext());
-    nsXPIDLString scriptString;
-    scriptString = jcode.jstr();
-
-    nsXPIDLString aRetValue;
-    PRBool aIsUndefined;
-    if (NS_FAILED(scriptContext->EvaluateString(scriptString, globalObject->GetGlobalJSObject(),
-            0, __FILE__, __LINE__, 0, aRetValue, &aIsUndefined)))
-        return JNI_FALSE;
-    return JNI_TRUE;
+  return JS_TRUE;
 }
 
 /*
@@ -721,129 +298,143 @@
  * Method:    _executeScriptWithInfo
  * Signature: (ILjava/lang/String;Ljava/lang/String;I)Z
  */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1executeScriptWithInfo
-  (JNIEnv* env, jclass llClass, jint scriptObject, jstring code, jstring file, jint line)
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1executeScriptWithInfo
+    (JNIEnv* env, jclass llClass, jint scriptObject, jstring code,
+     jstring file, jint line)
 {
-    JStringWrap jcode(env, code);
-    if (!jcode.jstr())
-        return JNI_FALSE;
+  Tracer tracer("LowLevelMoz._executeScriptWithInfo");
+  JStringWrap jcode(env, code);
+  if (!jcode.jstr()) {
+    tracer.setFail("null code string");
+    return JNI_FALSE;
+  }
+  JStringWrap jfile(env, file);
+  if (!jfile.str()) {
+    tracer.setFail("null file name");
+    return JNI_FALSE;
+  }
+  tracer.log("code=%s, file=%s, line=%d", jcode.str(), jfile.str(), line);
 
-    JStringWrap jfile(env, file);
-    if (!jfile.str())
-        return JNI_FALSE;
+  nsIScriptGlobalObject* globalObject =
+      NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObject);
+  nsCOMPtr<nsIScriptContext> scriptContext(globalObject->GetContext());
+  nsXPIDLString scriptString;
+  scriptString = jcode.jstr();
 
-    nsIScriptGlobalObject* globalObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObject);
-    nsCOMPtr<nsIScriptContext> scriptContext(globalObject->GetContext());
-    nsXPIDLString scriptString;
-    scriptString = jcode.jstr();
-
-    nsXPIDLString aRetValue;
-    PRBool aIsUndefined;
-    if (NS_FAILED(scriptContext->EvaluateString(scriptString, globalObject->GetGlobalJSObject(),
-            0, jfile.str(), line, 0, aRetValue, &aIsUndefined)))
-        return JNI_FALSE;
-    return JNI_TRUE;
+  nsXPIDLString aRetValue;
+  PRBool aIsUndefined;
+  if (NS_FAILED(scriptContext->EvaluateString(scriptString,
+      globalObject->GetGlobalJSObject(), 0, jfile.str(), line, 0,
+      aRetValue, &aIsUndefined))) {
+    tracer.setFail("EvaluateString failed");
+    return JNI_FALSE;
+  }
+  return JNI_TRUE;
 }
 
 /*
  * Class:     com_google_gwt_dev_shell_moz_LowLevelMoz
  * Method:    _invoke
- * Signature: (ILjava/lang/String;II[I[I)Z
+ * Signature: (ILjava/lang/String;I[I)I
  */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1invoke
-  (JNIEnv* env, jclass, int scriptObjInt, jstring methodName, jint jsthisval, jint jsargc, jintArray jsargs, jintArray rval)
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1invoke
+    (JNIEnv* env, jclass, int scriptObjInt, jstring methodName, jint jsThisInt,
+     jintArray jsArgsInt, jint jsRetValInt)
 {
-    TRACE("ENTER Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1invoke");
+  Tracer tracer("LowLevelMoz._invoke");
 
-    JStringWrap methodStr(env,methodName);
-    if (!methodStr.str())
-       return TRACE("FAIL 1"), JNI_FALSE;
+  JStringWrap methodStr(env, methodName);
+  if (!methodStr.str()) {
+    tracer.setFail("null method name");
+    return JNI_FALSE;
+  }
+  JsRootedValue* jsThisRV = reinterpret_cast<JsRootedValue*>(jsThisInt);
+  jint jsArgc = env->GetArrayLength(jsArgsInt);
+  tracer.log("method=%s, jsthis=%08x, #args=%d", methodStr.str(), jsThisInt,
+     jsArgc);
 
-    nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
-    nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
-    if (!scriptContext)
-       return TRACE("FAIL 2"), JNI_FALSE;
-    JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
-    JSObject* scriptWindow = (JSObject*)scriptObject->GetGlobalJSObject();
+  nsIScriptGlobalObject* scriptObject =
+      NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
+  nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
+  if (!scriptContext) {
+    tracer.setFail("can't get script context");
+    return JNI_FALSE;
+  }
+  JSContext* cx
+      = reinterpret_cast<JSContext*>(scriptContext->GetNativeContext());
+  JSObject* scriptWindow
+      = reinterpret_cast<JSObject*>(scriptObject->GetGlobalJSObject());
 
-    jsval fval;
-    if (!JS_GetProperty(cx, scriptWindow, methodStr.str(), &fval))
-       return TRACE("FAIL 3"), JNI_FALSE;
-    if (!JS_ValueToFunction(cx, fval))
-       return TRACE("FAIL 4"), JNI_FALSE;
+  jsval fval;
+  if (!JS_GetProperty(cx, scriptWindow, methodStr.str(), &fval)) {
+    tracer.setFail("JS_GetProperty(method) failed");
+    return JNI_FALSE;
+  }
+  JSFunction* jsFunction = JS_ValueToFunction(cx, fval);
+  if (!jsFunction) {
+    tracer.setFail("JS_ValueToFunction failed");
+    return JNI_FALSE;
+  }
+  
+  // extract arguments in jsval form
+  nsAutoArrayPtr<jint> jsargvals(new jint[jsArgc]);
+  if (!jsargvals) {
+    tracer.setFail("failed to allocate arg array");
+    return JNI_FALSE;
+  }
+  env->GetIntArrayRegion(jsArgsInt, 0, jsArgc, jsargvals);
+  if (env->ExceptionCheck()) {
+    tracer.setFail("copy from Java array failed");
+    return JNI_FALSE;
+  }
+  nsAutoArrayPtr<jsval> jsargs(new jsval[jsArgc]);
+  for (int i = 0; i < jsArgc; ++i) {
+    JsRootedValue* arg = reinterpret_cast<JsRootedValue*>(jsargvals[i]);
+    jsargs[i] = arg->getValue();
+  }
 
-    nsAutoArrayPtr<jint> jsargvals(new jint[jsargc]);
-    if (!jsargvals)
-       return TRACE("FAIL 5"), JNI_FALSE;
+  jsval jsrval;
+  JSObject* jsThis;
+  if (jsThisRV->isNull()) {
+    jsThis = scriptWindow;
+  } else {
+    jsThis = jsThisRV->getObject();
+  }
+  
+  PrintJSValue(cx, OBJECT_TO_JSVAL(jsThis), "jsThis=");
+  for (int i = 0; i < jsArgc; ++i) {
+    char buf[256];
+    snprintf(buf, sizeof(buf), "arg[%d]=", i);
+    PrintJSValue(cx, jsargs[i], buf);
+  }
+  //tracer.log("fval = %08x, args=%08x", fval, jsargs.get());
+  if (!JS_CallFunctionValue(cx, jsThis, fval, jsArgc, jsargs.get(), &jsrval)) {
+    tracer.setFail("JS_CallFunctionValue failed");
+    return JNI_FALSE;
+  }
 
-    env->GetIntArrayRegion(jsargs, 0, jsargc, jsargvals);
-    if (env->ExceptionCheck())
-       return TRACE("FAIL 6"), JNI_FALSE;
-
-    jsval jsrval;
-    JSObject* jsthis = (jsthisval == JSVAL_NULL) ? scriptWindow : JSVAL_TO_OBJECT(jsthisval);
-    if (!JS_CallFunctionValue(cx, jsthis, fval, jsargc, (jsval*)jsargvals.get(), &jsrval))
-       return TRACE("FAIL 7"), JNI_FALSE;
-
-    env->SetIntArrayRegion(rval, 0, 1, (jint*)&jsrval);
-    if (env->ExceptionCheck())
-       return TRACE("FAIL 8"), JNI_FALSE;
-
-    TRACE("SUCCESS Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1invoke");
-    return JNI_TRUE;
+  PrintJSValue(cx, jsrval, "return value=");
+  JsRootedValue* returnVal = reinterpret_cast<JsRootedValue*>(jsRetValInt);
+  returnVal->setValue(jsrval);
+  return JNI_TRUE;
 }
 
-/*
- * Class:     com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method:    _isWrappedDispatch
- * Signature: (II[Z)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1isWrappedDispatch
-  (JNIEnv* env, jclass, jint scriptObjInt, jint jsobjval, jbooleanArray rval)
-{
-    TRACE("ENTER Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1isWrappedDispatch");
-
-    nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
-    nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
-    if (!scriptContext)
-       return JNI_FALSE;
-    JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
-
-    jboolean r = JNI_FALSE;
-    if (JSVAL_IS_OBJECT(jsobjval))
-    {
-        JSObject* jsobj = JSVAL_TO_OBJECT(jsobjval);
-        if (JS_InstanceOf(cx, jsobj, &gwt_nativewrapper_class, 0))
-            r = JNI_TRUE;
-    }
-
-    env->SetBooleanArrayRegion(rval, 0, 1, &r);
-    if (env->ExceptionCheck())
-       return JNI_FALSE;
-
-    TRACE("SUCCESS Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1isWrappedDispatch");
-    return JNI_TRUE;
-}
 
 /*
  * Class:     com_google_gwt_dev_shell_moz_LowLevelMoz
  * Method:    _raiseJavaScriptException
- * Signature: (II)Z
+ * Signature: (I)Z
  */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1raiseJavaScriptException
-  (JNIEnv* env, jclass, jint scriptObjInt, jint jsarg)
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1raiseJavaScriptException
+    (JNIEnv* env, jclass, jint jscontext)
 {
-    TRACE("ENTER Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1raiseJavaScriptException");
-
-    nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
-    nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
-    if (!scriptContext)
-       return JNI_FALSE;
-    JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
-    JS_SetPendingException(cx, jsarg);
-
-    TRACE("SUCCESS Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1raiseJavaScriptException");
-    return JNI_TRUE;
+  Tracer tracer("LowLevelMoz._raiseJavaScriptException");
+  JSContext* cx = reinterpret_cast<JSContext*>(jscontext);
+  JS_SetPendingException(cx, JSVAL_NULL);
+  return JNI_TRUE;
 }
 
 /*
@@ -851,247 +442,42 @@
  * Method:    _registerExternalFactoryHandler
  * Signature: ()Z
  */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1registerExternalFactoryHandler
-  (JNIEnv* env, jclass llClass)
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1registerExternalFactoryHandler
+    (JNIEnv* env, jclass llClass)
 {
-    if (!InitGlobals(env, llClass))
-        return JNI_FALSE;
+  if (!InitGlobals(env, llClass))
+    return JNI_FALSE;
 
-    TRACE("ENTER Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1registerExternalFactoryHandler");
+  // tracing isn't setup until after InitGlobals is called
+  Tracer tracer("LowLevelMoz._registerExternalFactoryHandler");
 
-    // Register "window.external" as our own class
-    if (NS_FAILED(nsComponentManager::RegisterFactory(
-            kGwtExternalCID, "externalFactory", GWT_EXTERNAL_CONTRACTID,
-            new nsRpExternalFactory(), PR_TRUE)))
-        return JNI_FALSE;
+  char buf[256];
+  sprintf(buf, " jniEnv=%08x, llClass=%08x", (unsigned)env, (unsigned)llClass);
+  tracer.log(buf);
+  
+  // Register "window.external" as our own class
+  if (NS_FAILED(nsComponentManager::RegisterFactory(
+      kGwtExternalCID, "externalFactory", GWT_EXTERNAL_CONTRACTID,
+      new nsRpExternalFactory(), PR_TRUE))) {
+    tracer.setFail("RegisterFactory failed");
+    return JNI_FALSE;
+  }
 
-    nsCOMPtr<nsICategoryManager> categoryManager = do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
-    if (!categoryManager)
-        return JNI_FALSE;
+  nsCOMPtr<nsICategoryManager> categoryManager =
+      do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
+  if (!categoryManager) {
+    tracer.setFail("unable to get category manager");
+    return JNI_FALSE;
+  }
 
-    nsXPIDLCString previous;
-    if (NS_FAILED(categoryManager->AddCategoryEntry(JAVASCRIPT_GLOBAL_PROPERTY_CATEGORY,
-                            "external", GWT_EXTERNAL_CONTRACTID,
-                            PR_TRUE, PR_TRUE, getter_Copies(previous))))
-        return JNI_FALSE;
+  nsXPIDLCString previous;
+  if (NS_FAILED(categoryManager->AddCategoryEntry(
+      JAVASCRIPT_GLOBAL_PROPERTY_CATEGORY, "external", GWT_EXTERNAL_CONTRACTID,
+      PR_TRUE, PR_TRUE, getter_Copies(previous)))) {
+    tracer.setFail("AddCategoryEntry failed");
+    return JNI_FALSE;
+  }
 
-    TRACE("SUCCESS Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1registerExternalFactoryHandler");
-    return JNI_TRUE;
+  return JNI_TRUE;
 }
-
-/*
- * Class:     com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method:    _unwrapDispatch
- * Signature: (II[Lcom/google/gwt/dev/shell/moz/LowLevelMoz/DispatchObject;)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1unwrapDispatch
-  (JNIEnv* env, jclass, jint scriptObjInt, jint jsobjval, jobjectArray rval)
-{
-    TRACE("ENTER Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1unwrapDispatch");
-
-    nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
-    nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
-    if (!scriptContext)
-       return JNI_FALSE;
-    JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
-
-    if (!JSVAL_IS_OBJECT(jsobjval))
-        return JNI_FALSE;
-
-    JSObject* jsobj = JSVAL_TO_OBJECT(jsobjval);
-    if (!JS_InstanceOf(cx, jsobj, &gwt_nativewrapper_class, 0))
-        return JNI_FALSE;
-
-    jobject dispObj = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, jsobj));
-    if (!dispObj)
-        return JNI_FALSE;
-
-    env->SetObjectArrayElement(rval, 0, dispObj);
-    if (env->ExceptionCheck())
-       return JNI_FALSE;
-
-    TRACE("SUCCESS Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1unwrapDispatch");
-    return JNI_TRUE;
-}
-
-/*
- * Class:     com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method:    _unwrapJSObject
- * Signature: (I[I)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1unwrapJSObject
-  (JNIEnv* env, jclass, jint nsISupportsPtr, jintArray rval)
-{
-    TRACE("ENTER Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1unwrapJSObject");
-
-    // MAJOR HACK: check the vtable ptr against a known "good" as a very crude RTTI
-    long *vt1 = NS_REINTERPRET_CAST(long*,NS_STATIC_CAST(nsISupports*, &nsJSObjectLocker::sJSObjectLocker));
-    long *vt2 = NS_REINTERPRET_CAST(long*,nsISupportsPtr);
-    if (*vt1 != *vt2)
-        return JNI_FALSE;
-
-    // probably safe
-    nsJSObjectLocker* jsObjectLocker = NS_STATIC_CAST(nsJSObjectLocker*, NS_REINTERPRET_CAST(nsISupports*,nsISupportsPtr));
-    jsval r = jsObjectLocker->mVal;
-    if (!JSVAL_IS_OBJECT(r))
-        return JNI_FALSE;
-
-    env->SetIntArrayRegion(rval, 0, 1, (jint*)&r);
-    if (env->ExceptionCheck())
-        return JNI_FALSE;
-
-    TRACE("SUCCESS Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1unwrapJSObject");
-    return JNI_TRUE;
-}
-
-/*
- * Class:     com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method:    _wrapDispatch
- * Signature: (ILcom/google/gwt/dev/shell/moz/LowLevelMoz/DispatchObject;[I)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1wrapDispatch
-  (JNIEnv* env, jclass, jint scriptObjInt, jobject dispObj, jintArray rval)
-{
-    TRACE("ENTER Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1wrapDispatch");
-
-    nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
-    nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
-    if (!scriptContext)
-       return JNI_FALSE;
-
-    JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
-    JSObject* scriptWindow = (JSObject*)scriptObject->GetGlobalJSObject();
-
-    JSObject* newObj = JS_NewObject(cx, &gwt_nativewrapper_class, 0, scriptWindow);
-    if (!newObj)
-        return JNI_FALSE;
-
-    jobject dispObjRef = env->NewGlobalRef(dispObj);
-    if (!dispObjRef || env->ExceptionCheck())
-        return JNI_FALSE;
-
-    if (!JS_SetPrivate(cx, newObj, dispObjRef))
-    {
-        env->DeleteGlobalRef(dispObjRef);
-        return JNI_FALSE;
-    }
-
-    // forcibly setup a "toString" method to override the default
-    jclass dispClass = env->GetObjectClass(dispObj);
-    if (env->ExceptionCheck())
-        return JS_FALSE;
-
-    jmethodID getFieldMeth = env->GetMethodID(dispClass, "getField", "(Ljava/lang/String;)I");
-    if (!getFieldMeth || env->ExceptionCheck())
-        return JS_FALSE;
-
-    jstring ident = env->NewStringUTF("@java.lang.Object::toString()");
-    if (!ident || env->ExceptionCheck())
-        return JS_FALSE;
-
-    jsval toStringFunc = env->CallIntMethod(dispObj, getFieldMeth, ident);
-    if (env->ExceptionCheck())
-        return JS_FALSE;
-
-    if (!JS_DefineProperty(cx, newObj, "toString", toStringFunc, JS_PropertyStub, JS_PropertyStub, JSPROP_READONLY | JSPROP_PERMANENT))
-        return JNI_FALSE;
-
-    env->SetIntArrayRegion(rval, 0, 1, (jint*)&newObj);
-    if (env->ExceptionCheck())
-        return JNI_FALSE;
-
-    TRACE("SUCCESS Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1wrapDispatch");
-    return JNI_TRUE;
-}
-
-/*
- * Class:     com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method:    _wrapFunction
- * Signature: (ILjava/lang/String;Lcom/google/gwt/dev/shell/moz/LowLevelMoz/DispatchMethod;[I)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1wrapFunction
-  (JNIEnv* env, jclass, jint scriptObjInt, jstring name, jobject dispMeth, jintArray rval)
-{
-    TRACE("ENTER Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1wrapFunction");
-
-    nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
-    nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
-    if (!scriptContext)
-        return JNI_FALSE;
-    JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
-    JSObject* scriptWindow = (JSObject*)scriptObject->GetGlobalJSObject();
-
-    JStringWrap nameStr(env, name);
-    if (!nameStr.str())
-        return JNI_FALSE;
-
-    JSFunction* function = JS_NewFunction(cx, gwt_invoke, 0, JSFUN_LAMBDA, 0, nameStr.str());
-    if (!function)
-        return JNI_FALSE;
-
-    JSObject* funObj = JS_GetFunctionObject(function);
-    if (!funObj)
-        return JNI_FALSE;
-
-    // Create a wrapper object to hold and clean up dispMeth
-    JSObject* cleanupObj = JS_NewObject(cx, &gwt_functionwrapper_class, 0, scriptWindow);
-    if (!cleanupObj)
-        return JNI_FALSE;
-
-    jobject dispMethRef = env->NewGlobalRef(dispMeth);
-    if (!dispMethRef || env->ExceptionCheck())
-        return JNI_FALSE;
-
-    // Store our global ref in the wrapper object
-    if (!JS_SetPrivate(cx, cleanupObj, dispMethRef))
-    {
-        env->DeleteGlobalRef(dispMethRef);
-        return JNI_FALSE;
-    }
-
-    // Store the wrapper object in funObj's reserved slot
-    if(!JS_SetReservedSlot(cx, funObj, 0, OBJECT_TO_JSVAL(cleanupObj)))
-        return JS_FALSE;
-
-    env->SetIntArrayRegion(rval, 0, 1, (jint*)&funObj);
-    if (env->ExceptionCheck())
-        return JNI_FALSE;
-
-    TRACE("SUCCESS Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1wrapFunction");
-    return JNI_TRUE;
-}
-
-/*
- * Class:     com_google_gwt_dev_shell_moz_LowLevelMoz
- * Method:    _wrapJSObject
- * Signature: (II[I)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1wrapJSObject
-  (JNIEnv* env, jclass, jint scriptObjInt, jint jsobjval, jintArray rval)
-{
-    TRACE("ENTER Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1wrapJSObject");
-
-    if (!JSVAL_IS_OBJECT(jsobjval))
-        return JNI_FALSE;
-
-    nsIScriptGlobalObject* scriptObject = NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
-    nsCOMPtr<nsIScriptContext> scriptContext(scriptObject->GetContext());
-    if (!scriptContext)
-        return JNI_FALSE;
-    JSContext* cx = (JSContext*)scriptContext->GetNativeContext();
-
-    nsISupports* objLocker = new nsJSObjectLocker(cx, jsobjval);
-    if (!objLocker)
-        return JNI_FALSE;
-
-    jint r = (jint)objLocker;
-    env->SetIntArrayRegion(rval, 0, 1, &r);
-    if (env->ExceptionCheck())
-        return JNI_FALSE;
-
-    objLocker->AddRef();
-    TRACE("SUCCESS Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1wrapJSObject");
-    return JNI_TRUE;
-}
-
-} // extern "C"
diff --git a/jni/linux/NativeWrapper.cpp b/jni/linux/NativeWrapper.cpp
new file mode 100644
index 0000000..8d74787
--- /dev/null
+++ b/jni/linux/NativeWrapper.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/*
+ * Defines the JavaScript classes gwt_nativewrapper_class and
+ * gwt_functionwrapper_class, which interface via JNI to Java objects.
+ */
+
+#include <jni.h>
+#include "JsRootedValue.h"
+#include "gwt-jni.h"
+#include "Tracer.h"
+
+extern "C" {
+  
+static JSBool JS_DLL_CALLBACK gwt_nativewrapper_getProperty(JSContext *cx,
+    JSObject *obj, jsval id, jsval *vp)
+{
+  Tracer tracer("gwt_nativewrapper_getProperty");
+
+  if (*vp != JSVAL_VOID)
+    return JS_TRUE;
+  jclass dispClass;
+  jobject dispObj;
+  jstring ident;
+  if (!getJavaPropertyStats(cx, obj, id, dispClass, dispObj, ident)) {
+    tracer.setFail("getJavaPropertyStats failed");
+    return JS_FALSE;
+  }
+  JsRootedValue* js_rooted_value = GetFieldAsRootedValue(cx, dispClass,
+      dispObj, ident);
+  if (!js_rooted_value) {
+    tracer.setFail("can't get field");
+    return JS_FALSE;
+  }
+  *vp = js_rooted_value->getValue();
+  return JS_TRUE;
+}
+
+static void JS_DLL_CALLBACK gwt_nativewrapper_finalize(JSContext *cx,
+    JSObject *obj)
+{
+  jobject dispObj = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
+  if (dispObj)
+    savedJNIEnv->DeleteGlobalRef(dispObj);
+}
+
+static JSBool JS_DLL_CALLBACK gwt_nativewrapper_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
+{
+  Tracer tracer("gwt_nativewrapper_setProperty");
+
+  jclass dispClass;
+  jobject dispObj;
+  jstring ident;
+  if (!getJavaPropertyStats(cx,obj,id,dispClass,dispObj,ident)) {
+    tracer.setFail("getJavaPropertyStats failed");
+    return JS_FALSE;
+  }
+  JsRootedValue* js_rooted_value = new JsRootedValue(cx, *vp); 
+  if (!SetFieldFromRootedValue(cx, dispClass, dispObj, ident, js_rooted_value)) {
+    tracer.setFail("can't set field");
+    return JS_FALSE;
+  }
+  return JS_TRUE;
+}
+
+
+JSClass gwt_nativewrapper_class = {
+  "gwt_nativewrapper_class", JSCLASS_HAS_PRIVATE,
+  JS_PropertyStub, JS_PropertyStub, gwt_nativewrapper_getProperty,
+  gwt_nativewrapper_setProperty, JS_EnumerateStub, JS_ResolveStub,
+  JS_ConvertStub, gwt_nativewrapper_finalize,
+  JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+JSClass gwt_functionwrapper_class = {
+  "gwt_functionwrapper_class", JSCLASS_HAS_PRIVATE,
+  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
+  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, gwt_nativewrapper_finalize,
+  JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+} // extern "C"
diff --git a/jni/linux/Tracer.cpp b/jni/linux/Tracer.cpp
new file mode 100644
index 0000000..706dc97
--- /dev/null
+++ b/jni/linux/Tracer.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+#include "Tracer.h"
+
+#ifdef ENABLE_TRACING
+
+// initialize static fields
+FILE* Tracer::outfp = 0;
+JNIEnv* Tracer::jniEnv = 0;
+jclass Tracer::traceClass;
+jmethodID Tracer::traceMethod;
+int Tracer::indentation = 0;
+Tracer::LogLevel Tracer::logLevel = Tracer::LEVEL_WARNING;
+
+/*
+ * Sets a JNI environment and Java class to pass trace messages to.
+ * 
+ * env - JNI environment to use for trace calls
+ * clazz - Java class, which must provide static void trace(String)
+ */
+bool Tracer::setJava(JNIEnv* env, jclass clazz) {
+  jniEnv = env;
+  if (!env) {
+    return true;
+  }
+  traceClass = static_cast<jclass>(env->NewGlobalRef(clazz));
+  if (!traceClass || env->ExceptionCheck()) {
+    return false;
+  }
+  traceMethod = env->GetStaticMethodID(traceClass, "trace",
+    "(Ljava/lang/String;)V");
+  if (!traceMethod || env->ExceptionCheck()) {
+    return false;
+  }
+
+  jstring msg = jniEnv->NewStringUTF("== Java trace started ==");
+  jniEnv->CallStaticVoidMethod(traceClass, traceMethod, msg);
+  return true;
+}
+
+/*
+ * Throw a HostedModeException and log a failure message.
+ * 
+ * Creates a new HostedModeException with the failure message,
+ * and also logs the failure message 
+ * 
+ * env - JNI environment to throw the exception in
+ * msg - failure message 
+ */
+void Tracer::throwHostedModeException(JNIEnv* env, const char* msg) {
+  setFail(msg);
+  jclass exceptionClass
+      = env->FindClass("com/google/gwt/dev/shell/HostedModeException");
+  env->ThrowNew(exceptionClass, fail_msg_);
+}
+
+#endif // ENABLE_TRACING
diff --git a/jni/linux/Tracer.h b/jni/linux/Tracer.h
new file mode 100644
index 0000000..85148b3
--- /dev/null
+++ b/jni/linux/Tracer.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+#ifndef JNI_LINUX_TRACER_H_
+#define JNI_LINUX_TRACER_H_
+
+#include <cstdio>
+#include <cstdarg>
+#include <cstring>
+#include <jni.h>
+
+// comment this out to remove almost all runtime overhead (with usual compiler
+// support) from tracing.
+#define ENABLE_TRACING
+
+/*
+ * Utility class for tracing.  This class is intended to be used as follows:
+ * 
+ * {
+ *   Tracer tracer("method name");
+ *   ... do work
+ *   if (fail) {
+ *      tracer.setFail("failure explanation");
+ *      return;
+ *   }
+ *   if (fail2) {
+ *      tracer.throwHostedModeException("failure explanation");
+ *      return;
+ *   }
+ *   return;
+ * }
+ * 
+ * The class automatically logs an enter message when it is created, as well
+ * as leave/fail messages when it is destroyed.  Logging is performed to a
+ * file or to a Java static member function on a class (or both) -- these
+ * are configured by using static member functions setFile() and setJava().
+ * 
+ * This class knows about the Java class
+ *   com.google.gwt.dev.shell.HostedModeException
+ * and throws a new instance of that exception if requested.
+ */
+class Tracer {
+public:
+  enum LogLevel {
+    LEVEL_ERROR = 0,
+    LEVEL_WARNING,
+    LEVEL_NOTICE,
+    LEVEL_INFO,
+    LEVEL_DEBUG,
+    LEVEL_DEBUG_V1,
+    LEVEL_DEBUG_V2,
+  };
+protected:
+#ifdef ENABLE_TRACING
+  // static variables that specify where logging is performed.  This are
+  // set by calling setFile() and setJava().
+  static FILE*     outfp;
+  static JNIEnv*   jniEnv;
+  static jclass    traceClass;
+  static jmethodID traceMethod;
+  static int       indentation;
+  static LogLevel  logLevel;
+
+  // method is set when the instance is created.
+  const char*		method_;
+  // fail_msg is set to indicate a failure has occurred.
+  const char*		fail_msg_;
+  // level of this trace object
+  LogLevel  log_level_;
+#endif
+  
+public:
+  /*
+   * Set the logging level.
+   */
+  static void setLevel(LogLevel level) {
+#ifdef ENABLE_TRACING
+    logLevel = level;
+#endif
+  }
+  
+protected:
+  /*
+   * Log a message (with supplied prefix) to the configured file.
+   * Only called if a file was specified and successfully opened for writing.
+   */
+  static void logFile(const char* msg) {
+#ifdef ENABLE_TRACING
+    for (int i = 0; i < indentation; ++i) {
+      putc(' ', outfp);
+    }
+    fputs(msg, outfp);
+    putc('\n', outfp);
+    fflush(outfp);
+#else
+    (void)msg; // avoid unused warning
+#endif
+  }
+
+  /*
+   * Log a message (with supplied prefix) to the configured Java class.
+   * Only called if a file was specified and successfully accessed.
+   * 
+   * Call static void trace(String msg) on the configured class. 
+   */
+  static void logJava(const char* msg) {
+#ifdef ENABLE_TRACING
+    // TODO(jat): fixed buffer size
+    char buf[512];
+    for (int i = 0; (i < indentation) && (i < int(sizeof(buf))); ++i) {
+      buf[i] = ' ';
+    }
+    strncpy(buf + indentation, msg, sizeof(buf) - indentation);
+    buf[sizeof(buf) - 1] = 0; // ensure null termination
+    jstring str = jniEnv->NewStringUTF(buf);
+    jniEnv->CallStaticVoidMethod(traceClass, traceMethod, str);
+#else
+    (void)msg; // avoid unused warning
+#endif
+  }
+
+  /*
+   * Log a message to a file and/or class with the default logging level.
+   * 
+   * If the preprocessor symbol DISABLE_TRACING has been defined, this is
+   * completely removed from the code path.
+   */
+  void logPrefix(const char* prefix) {
+#ifdef ENABLE_TRACING
+    logPrefix(prefix, log_level_);
+#else
+    (void)prefix; // avoid unused warning
+#endif
+  }
+  
+  /*
+   * Log a message to a file and/or class.
+   * 
+   * If the preprocessor symbol DISABLE_TRACING has been defined, this is
+   * completely removed from the code path.
+   */
+  void logPrefix(const char* prefix, LogLevel level) {
+#ifdef ENABLE_TRACING
+    if (level>logLevel) return;
+    log("%-5.5s %s%s%s", prefix, method_, fail_msg_ ? ": " : "",
+        fail_msg_ ? fail_msg_ : "");
+#endif
+  }
+    
+public:
+  /*
+   * Create an instance with the specified method name and no failure
+   * message.  Log an ENTER message.
+   */
+  Tracer(const char* method, LogLevel log_level = LEVEL_ERROR)
+#ifdef ENABLE_TRACING
+      : method_(method), fail_msg_(0), log_level_(log_level) {
+    log("ENTER %s", method);
+    indentation++;
+#else
+  { (void)method; (void)log_level; // avoid unused warnings
+#endif
+  }
+
+  /*
+   * Create an instance with the specified method name and no failure
+   * message.  Log an ENTER message and the this pointer.
+   */
+  Tracer(const char* method, const void* objThis,
+      LogLevel log_level = LEVEL_ERROR)
+#ifdef ENABLE_TRACING
+      : method_(method), fail_msg_(0), log_level_(log_level) {
+    log("ENTER %s(this=%08x)", method, unsigned(objThis));
+    indentation++;
+#else
+  { (void)method; (void)objThis; (void)log_level; // avoid unused warnings
+#endif
+  }
+
+  /*
+   * Destroy the instance and log a fail or leave message.
+   */
+  ~Tracer() {
+#ifdef ENABLE_TRACING
+    --indentation;
+    if(fail_msg_) {
+      logPrefix("*FAIL", LEVEL_ERROR);
+    } else {
+      logPrefix("LEAVE");
+    }
+#endif
+  }
+  
+  /*
+   * Specify a filename to receive logging output.  Close any previously
+   * opened file.  If a null filename is passed, disable logging to a
+   * file.
+   * 
+   * filename - the file path to receive logging output.  This file is
+   *     truncated if it already exists.
+   * 
+   * Returns false on failure.
+   */
+  static bool setFile(const char* filename) {
+#ifdef ENABLE_TRACING
+    if (outfp) {
+      fclose(outfp);
+      outfp = 0;
+    }
+    if (!filename) {
+      return true;
+    }
+    outfp = fopen(filename, "w");
+    if (!outfp) {
+      return false;
+    }
+    fprintf(outfp, "== started logging ==\n");
+    fflush(outfp);
+#else
+    (void)filename; // avoid unused warning
+#endif
+    return true;
+  }
+  
+  /*
+   * Specify a Java class to receive logging output.  The supplied class
+   * must have a static void trace(String) member function which is called
+   * for output.  Logging to a Java class is disabled if the supplied JNI
+   * environment is null.
+   * 
+   * env - JNI environment
+   * clazz - the Java class to receive logging output
+   * 
+   * Returns false on failure.
+   */ 
+  static bool setJava(JNIEnv* env, jclass clazz)
+#ifdef ENABLE_TRACING  
+  ;
+#else
+  // inline a null body if we aren't debugging; avoid unused warnings
+  { (void)env; (void)clazz; return true; }
+#endif
+  
+  /*
+   * Set a failure message, overwriting any previously specified failure
+   * message.  Passing a null string will remove any previous failure
+   * notification.
+   */
+  void setFail(const char* fail_msg) {
+#ifdef ENABLE_TRACING
+    fail_msg_ = fail_msg;
+#else
+    (void)fail_msg; // avoid unused warning
+#endif
+  }
+  
+  /*
+   * Throw a Java HostedModeException as well as set a failure message to
+   * be logged.
+   * 
+   * env - JNI environment to throw exception into
+   * fail_msg - failure message 
+   */
+  void throwHostedModeException(JNIEnv* env, const char* fail_msg);
+  
+  /*
+   * Log an arbitrary message.
+   */
+  static void log(const char* format, ...) {
+#ifdef ENABLE_TRACING
+    va_list args;
+    va_start(args, format);
+    char msg[512]; // TODO(jat): fixed size buffer
+    vsnprintf(msg, sizeof(msg), format, args);
+    msg[sizeof(msg) - 1] = 0; // ensure null termination
+    if(outfp) logFile(msg);
+    if(jniEnv) logJava(msg);
+    va_end(args);
+#else
+    (void)format; // avoid unused warning
+#endif
+  }
+};
+
+#endif /* JNI_LINUX_TRACER_H_ */
diff --git a/jni/linux/gwt-jni.h b/jni/linux/gwt-jni.h
new file mode 100644
index 0000000..e0a627a
--- /dev/null
+++ b/jni/linux/gwt-jni.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+#ifndef JNI_LINUX_GWT_JNI_H_
+#define JNI_LINUX_GWT_JNI_H_
+
+#include <jni.h>
+#include "JsRootedValue.h"
+#include "JStringWrap.h"
+
+extern JNIEnv* savedJNIEnv;
+extern jclass lowLevelMozClass;
+
+// JavaScript class objects
+extern JSClass gwt_nativewrapper_class;
+extern JSClass gwt_functionwrapper_class;
+
+extern jobject NewJsValueMoz(JSContext* context);
+extern jobject NewJsValueMoz(JsRootedValue* js_rooted_value);
+extern JsRootedValue* GetJsRootedValue(jobject jsvalue);
+extern JsRootedValue* GetFieldAsRootedValue(JSContext* context, jclass clazz,
+    jobject obj, jstring field_name);
+extern bool SetFieldFromRootedValue(JSContext* context, jclass clazz,
+    jobject obj, jstring field_name, JsRootedValue* js_rooted_value);
+extern JSBool getJavaPropertyStats(JSContext *cx, JSObject *obj, jsval id,
+    jclass& dispClass, jobject& dispObj, jstring& jident);
+extern JSBool invokeJavaMethod(JSContext *cx, JSObject *obj, uintN argc,
+    jsval *argv, jsval *rval);
+    
+#endif /*JNI_LINUX_GWT_JNI_H_*/
diff --git a/jni/linux/mozilla-headers.h b/jni/linux/mozilla-headers.h
new file mode 100644
index 0000000..b115794
--- /dev/null
+++ b/jni/linux/mozilla-headers.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+// include all of the necessary Mozilla headers.
+#include "mozilla-config.h"
+#include "nsIServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsICategoryManager.h"
+#include "nsIScriptNameSpaceManager.h"
+#include "nsIScriptObjectOwner.h"
+#include "nsIScriptGlobalObject.h"
+#include "nsIScriptContext.h"
+#include "nsIDOMWindow.h"
+#include "nsIXPConnect.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
diff --git a/jni/linux/prebuilt/libgwt-ll.so b/jni/linux/prebuilt/libgwt-ll.so
index ee03c5e..596f2f1 100755
--- a/jni/linux/prebuilt/libgwt-ll.so
+++ b/jni/linux/prebuilt/libgwt-ll.so
Binary files differ
diff --git a/jni/mac/Makefile b/jni/mac/Makefile
index 1f59692..2787428 100644
--- a/jni/mac/Makefile
+++ b/jni/mac/Makefile
@@ -75,6 +75,8 @@
 ##
 all: $(GWT_LL_LIB) $(GWT_WEBKIT_LIB) $(SWT_LIBS)
 
+staging: all
+	cp libgwt-*.jnilib ../../build/staging/gwt-mac-0.0.0/
 ##
 # Copy WebKit binary frameworks locally.
 ##
diff --git a/jni/mac/gwt-webkit.cpp b/jni/mac/gwt-webkit.cpp
index ce5a797..86b61d4 100644
--- a/jni/mac/gwt-webkit.cpp
+++ b/jni/mac/gwt-webkit.cpp
@@ -28,6 +28,38 @@
 
 using namespace KJS;
 
+static void PrintJSValue(JSValue* val, char* prefix="") {
+  static const char* typeStrings[]={
+    "unspecified",
+    "number",
+    "boolean",
+    "undefined",
+    "null",
+    "string",
+    "object",
+    "getter/setter",
+  };
+  JSType type = val->type();
+  const char* typeString=typeStrings[type];
+  char buf[256];
+	char* p = buf;
+	p += snprintf(p, sizeof(buf)-(p-buf), "%s%s: ", prefix, typeString);
+	if (val->isNumber()) {
+		p += snprintf(p, sizeof(buf)-(p-buf), "%lf", val->getNumber());
+	} else if(val->isString()) {
+		CString str(val->getString().UTF8String());
+		p += snprintf(p, sizeof(buf)-(p-buf), "%.*s", str.size(), str.c_str());
+	} else if(val->isObject()) {
+		const JSObject* obj = val->getObject();
+		const ClassInfo* cinfo = obj->classInfo();
+		const char* cname = cinfo ? cinfo->className : "js object";
+		p += snprintf(p, sizeof(buf)-(p-buf), "%s @ %08x", cname, unsigned(obj));
+	} else if(val->isBoolean()) {
+		p += snprintf(p, sizeof(buf)-(p-buf), "%s", val->getBoolean() ? "true" : "false");
+	}
+	TRACE(buf);
+}
+
 /*
  * Class:     com_google_gwt_dev_shell_mac_LowLevelSaf
  * Method:    isNull
@@ -35,13 +67,13 @@
  */
 JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_isNull
   (JNIEnv *env, jclass, jint jsval) {
-    TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__isNull");
+    TRACE("ENTER LowLevelSaf__isNull");
 
   JSValue* val = (JSValue*)jsval;
   if (!val)
     return JNI_FALSE;
 
-    TRACE("SUCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__isNull");
+    TRACE("SUCCESS LowLevelSaf__isNull");
   return val->isNull();
 }
 
@@ -50,14 +82,15 @@
  * Method:    isUndefined
  * Signature: (I)Z
  */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_isUndefined
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_mac_LowLevelSaf_isUndefined
   (JNIEnv *env, jclass, jint jsval) {
-    TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__isUndefined");
+    TRACE("ENTER LowLevelSaf__isUndefined");
   JSValue* val = (JSValue*)jsval;
   if (!val)
     return JNI_FALSE;
 
-    TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__isUndefined");
+    TRACE("SUCCESS LowLevelSaf__isUndefined");
   return val->isUndefined();
 }
 
@@ -66,13 +99,47 @@
  * Method:    jsNull
  * Signature: ()I
  */
-JNIEXPORT jint JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf_jsNull
+extern "C" JNIEXPORT jint JNICALL
+Java_com_google_gwt_dev_shell_mac_LowLevelSaf_jsNull
   (JNIEnv *, jclass) {
   return (jint)jsNull();
 }
 
 /*
  * Class:     com_google_gwt_dev_shell_mac_LowLevelSaf
+ * Method:    getTypeString
+ * Signature: (I)Ljava/lang/String;
+ */
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_google_gwt_dev_shell_mac_LowLevelSaf_getTypeString
+   (JNIEnv *env, jclass, jint jsval) {
+  static const char* typeStrings[]={
+    "unspecified",
+    "number",
+    "boolean",
+    "undefined",
+    "null",
+    "string",
+    "object",
+    "getter/setter",
+  };
+  JSValue* val = (JSValue*)jsval;
+  if (!val)
+    return 0;
+  JSType type = val->type();
+  const char* typeString=typeStrings[type];
+  if (type == ObjectType) {
+	  if (val->isObject(&DispWrapper::info)) {
+ 	  	typeString = "Java object";
+ 	  } else {
+ 	  	typeString = "JS object";
+	  }
+  }
+  return env->NewStringUTF(typeString);
+}
+
+/*
+ * Class:     com_google_gwt_dev_shell_mac_LowLevelSaf
  * Method:    jsUndefined
  * Signature: ()I
  */
@@ -88,7 +155,7 @@
  */
 JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1coerceToBoolean
   (JNIEnv * env, jclass, jint execState, jint jsval, jbooleanArray rval) {
-    TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1coerceToBoolean");
+    TRACE("ENTER LowLevelSaf__1coerceToBoolean");
 
   if (!execState || !jsval)
     return JNI_FALSE;
@@ -98,7 +165,7 @@
   if (env->ExceptionCheck())
       return JNI_FALSE;
 
-    TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1coerceToBoolean");
+    TRACE("SUCCESS LowLevelSaf__1coerceToBoolean");
   return JNI_TRUE;
 }
 
@@ -109,7 +176,7 @@
  */
 JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1coerceToDouble
   (JNIEnv *env, jclass, jint execState, jint jsval, jdoubleArray rval) {
-    TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1coerceToDouble");
+    TRACE("ENTER LowLevelSaf__1coerceToDouble");
 
   if (!execState || !jsval)
     return JNI_FALSE;
@@ -119,7 +186,7 @@
   if (env->ExceptionCheck())
       return JNI_FALSE;
 
-    TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1coerceToDouble");
+    TRACE("SUCCESS LowLevelSaf__1coerceToDouble");
   return JNI_TRUE;
 }
 
@@ -130,7 +197,7 @@
  */
 JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1coerceToString
   (JNIEnv *env, jclass, jint execState, jint jsval, jobjectArray rval) {
-    TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1coerceToString");
+    TRACE("ENTER LowLevelSaf__1coerceToString");
 
   JSValue *val = (JSValue*)jsval;
   if (!execState || !val)
@@ -152,7 +219,7 @@
   if (env->ExceptionCheck())
     return JNI_FALSE;
 
-  TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1coerceToString");
+  TRACE("SUCCESS LowLevelSaf__1coerceToString");
   return JNI_TRUE;
 }
 
@@ -163,7 +230,10 @@
  */
 JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1convertBoolean
   (JNIEnv *env, jclass, jboolean jval, jintArray rval) {
-    TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1convertBoolean");
+    TRACE("ENTER LowLevelSaf__1convertBoolean");
+  char buf[256];
+  snprintf(buf, sizeof(buf), " val=%s", jval ? "true" : "false");
+  TRACE(buf);
 
   JSValue *jsval = (jval == JNI_FALSE) ? jsBoolean(false) : jsBoolean(true);
   if (!jsval)
@@ -173,7 +243,7 @@
   if (env->ExceptionCheck())
     return JNI_FALSE;
   
-    TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1convertBoolean");
+    TRACE("SUCCESS LowLevelSaf__1convertBoolean");
   return JNI_TRUE;
 }
 
@@ -184,7 +254,10 @@
  */
 JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1convertDouble
   (JNIEnv *env, jclass, jdouble jval, jintArray rval) {
-    TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1convertDouble");
+    TRACE("ENTER LowLevelSaf__1convertDouble");
+  char buf[256];
+  snprintf(buf, sizeof(buf), " val=%lf", jval);
+  TRACE(buf);
 
   JSValue *jsval = jsNumber(jval);
   if (!jsval)
@@ -194,7 +267,7 @@
   if (env->ExceptionCheck())
     return JNI_FALSE;
 
-    TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1convertDouble");
+    TRACE("SUCCESS LowLevelSaf__1convertDouble");
   return JNI_TRUE;
 }
 
@@ -205,11 +278,14 @@
  */
 JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1convertString
   (JNIEnv *env, jclass, jstring jval, jintArray rval) {
-    TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1convertString");
+    TRACE("ENTER LowLevelSaf__1convertString");
 
   JStringWrap jstr(env, jval);
   if (!jstr.jstr())
     return JNI_FALSE;
+  char buf[256];
+  snprintf(buf, sizeof(buf), " val=%s", jstr.str());
+  TRACE(buf);
   
   JSValue *jsval = jsString(UString((const UChar*)jstr.jstr(), jstr.length()));
   /*
@@ -222,11 +298,14 @@
   if (!jsval)
     return JNI_FALSE;
 
+  snprintf(buf, sizeof(buf), " return={%08x} ", unsigned(jsval));
+  PrintJSValue(jsval, buf);
+  
   env->SetIntArrayRegion(rval,0,1,(const jint*)&jsval);
   if (env->ExceptionCheck())
     return JNI_FALSE;
 
-  TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1convertString");
+  TRACE("SUCCESS LowLevelSaf__1convertString");
   return JNI_TRUE;
 }
 
@@ -237,20 +316,23 @@
  */
 JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1executeScript
   (JNIEnv* env, jclass, jint execState, jstring code) {
-    TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1executeScript"); 
+    TRACE("ENTER LowLevelSaf__1executeScript"); 
   if (!execState || !code)
     return JNI_FALSE;
 
   JStringWrap jcode(env, code);
   if (!jcode.jstr())
     return JNI_FALSE;
-
+  char buf[1024];
+  snprintf(buf, sizeof(buf), " code=%s", jcode.str());
+  TRACE(buf);
+    
   Interpreter* interp = ((ExecState*)execState)->dynamicInterpreter();
   if (!interp)
     return JNI_FALSE;
 
   interp->evaluate(UString(), 0, (const UChar*)jcode.jstr(), jcode.length());
-    TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1executeScript");
+    TRACE("SUCCESS LowLevelSaf__1executeScript");
   return JNI_TRUE;
 }
 
@@ -261,7 +343,7 @@
  */
 JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1executeScriptWithInfo
   (JNIEnv* env, jclass, jint execState, jstring code, jstring file, jint line) {
-    TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1executeScriptWithInfo");
+    TRACE("ENTER LowLevelSaf__1executeScriptWithInfo");
   if (!execState || !code || !file)
     return JNI_FALSE;
 
@@ -272,13 +354,17 @@
   JStringWrap jfile(env, file);
   if (!jcode.jstr())
     return JNI_FALSE;
-
+  
+  char buf[1024];
+  snprintf(buf, sizeof(buf), " code=%s, file=%s, line=%d", jcode.str(), jfile.str(), line);
+  TRACE(buf);
+  
   Interpreter* interp = ((ExecState*)execState)->dynamicInterpreter();
   if (!interp)
     return JNI_FALSE;
 
   interp->evaluate(UString((const UChar*)jfile.jstr(), jfile.length()), line, (const UChar*)jcode.jstr(), jcode.length());
-    TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1executeScriptWithInfo");
+    TRACE("SUCCESS LowLevelSaf__1executeScriptWithInfo");
   return JNI_TRUE;
 }
 
@@ -309,7 +395,7 @@
  */
 JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1getGlobalExecState
   (JNIEnv *env, jclass, jint scriptObject, jintArray rval) {
-    TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1getGlobalExecState");
+    TRACE("ENTER LowLevelSaf__1getGlobalExecState");
 
   if (!scriptObject || !((JSValue*)scriptObject)->isObject())
     return JNI_FALSE;
@@ -322,7 +408,7 @@
     env->SetIntArrayRegion(rval, 0, 1, (jint*)&execState);
     if (env->ExceptionCheck())
         return JNI_FALSE;
-    TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1getGlobalExecState");
+    TRACE("SUCCESS LowLevelSaf__1getGlobalExecState");
   return JNI_TRUE;
 
 }
@@ -352,7 +438,7 @@
   }
 
 #ifdef FILETRACE
-    gout = fopen("gwt-ll.log", "w");
+    gout = fopen("/tmp/gwt-ll.log", "w");
     filetrace("LOG STARTED");
 #endif // FILETRACE
 
@@ -372,18 +458,22 @@
  */
 JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1invoke
   (JNIEnv* env, jclass, jint jsexecState, jint jsScriptObject, jstring method, jint jsthis, jint argc, jintArray argv, jintArray rval) {
-    TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1invoke");
+    TRACE("ENTER LowLevelSaf__1invoke");
 
   if (!jsexecState || !jsScriptObject || !method || !rval)
     return JNI_FALSE;
-  
+  JStringWrap jmethod(env, method);
+  char buf[256];
+  snprintf(buf, sizeof(buf), "sciptObject=%08x, method=%s, argc=%d",
+      jsScriptObject, jmethod.str(), argc);
+  TRACE(buf);
+  PrintJSValue((JSValue*)jsthis, " jsthis=");
   ExecState* execState = (ExecState*)jsexecState;
 
   JSObject* scriptObj = (JSObject*)jsScriptObject;
   if (!scriptObj->isObject())
     return JNI_FALSE;
 
-  JStringWrap jmethod(env, method);
   if (!jmethod.jstr())
     return JNI_FALSE;
 
@@ -408,7 +498,9 @@
     env->GetIntArrayRegion(argv, i, 1, &argi);
     if (env->ExceptionCheck())
       return JNI_FALSE;
-
+    snprintf(buf, sizeof(buf), " arg[%d]={%08x} ", i, argi);
+    TRACE(buf);
+    PrintJSValue((JSValue*)argi, buf);
     if (argi) {
       args.append((JSValue*)argi);
     } else {
@@ -417,16 +509,68 @@
   }
 
   JSValue* result = func->call(execState, thisObj, args);
-    env->SetIntArrayRegion(rval, 0, 1, (jint*)&result);
-    if (env->ExceptionCheck())
-        return JNI_FALSE;
+  gcProtectNullTolerant(result);
+  PrintJSValue(result, " return=");
+  env->SetIntArrayRegion(rval, 0, 1, (jint*)&result);
+  if (env->ExceptionCheck())
+    return JNI_FALSE;
 
-    TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1invoke");
+  TRACE("SUCCESS LowLevelSaf__1invoke");
   return JNI_TRUE;
 }
 
 /*
  * Class:     com_google_gwt_dev_shell_mac_LowLevelSaf
+ * Method:    isBoolean
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_mac_LowLevelSaf_isBoolean
+  (JNIEnv *, jclass, jint jsval) {
+  if (!jsval)
+    return JNI_FALSE;
+    return ((JSValue*)jsval)->isBoolean() ? JNI_TRUE : JNI_FALSE;
+}
+
+/*
+ * Class:     com_google_gwt_dev_shell_mac_LowLevelSaf
+ * Method:    isNumber
+ * Signature: (I)Z
+ */
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_mac_LowLevelSaf_isNumber
+  (JNIEnv *, jclass, jint jsval) {
+  if (!jsval)
+    return JNI_FALSE;
+  return ((JSValue*)jsval)->isNumber() ? JNI_TRUE : JNI_FALSE;
+}
+
+/*
+ * Class:     com_google_gwt_dev_shell_mac_LowLevelSaf
+ * Method:    _isString
+ * Signature: (I)Z
+ * 
+ * Must return true for JavaScript String objects as well as string primitives.
+ */
+JNIEXPORT jboolean JNICALL
+Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1isString
+  (JNIEnv *, jclass, jint jsval) {
+  if (!jsval)
+    return JNI_FALSE;
+  JSValue* jsValue = reinterpret_cast<JSValue*>(jsval);
+  if(jsValue->isString()) return JNI_TRUE;
+	if(jsValue->isObject()) {
+		const JSObject* obj = jsValue->getObject();
+		const ClassInfo* cinfo = obj->classInfo();
+		if (cinfo && !strcmp(cinfo->className, "String")) {
+			return JNI_TRUE;
+		}
+	}
+	return JNI_FALSE;
+}
+
+/*
+ * Class:     com_google_gwt_dev_shell_mac_LowLevelSaf
  * Method:    _isObject
  * Signature: (I)Z
  */
@@ -439,24 +583,12 @@
 
 /*
  * Class:     com_google_gwt_dev_shell_mac_LowLevelSaf
- * Method:    _isString
- * Signature: (I)Z
- */
-JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1isString
-  (JNIEnv *, jclass, jint jsval) {
-  if (!jsval)
-    return JNI_FALSE;
-  return ((JSValue*)jsval)->isString() ? JNI_TRUE : JNI_FALSE;
-}
-
-/*
- * Class:     com_google_gwt_dev_shell_mac_LowLevelSaf
  * Method:    _isWrappedDispatch
  * Signature: (I[Z)Z
  */
 JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1isWrappedDispatch
   (JNIEnv* env, jclass, jint jsval, jbooleanArray rval) {
-    TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1isWrappedDispatch");
+    TRACE("ENTER LowLevelSaf__1isWrappedDispatch");
   if (!jsval)
     return JNI_FALSE;
 
@@ -467,7 +599,7 @@
     if (env->ExceptionCheck())
         return JNI_FALSE;
 
-    TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1isWrappedDispatch");
+    TRACE("SUCCESS LowLevelSaf__1isWrappedDispatch");
     return JNI_TRUE;
 }
 
@@ -498,13 +630,13 @@
  */
 JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1raiseJavaScriptException
   (JNIEnv *env, jclass, jint execState, jint jsval) {
-    TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1raiseJavaScriptException");
+    TRACE("ENTER LowLevelSaf__1raiseJavaScriptException");
 
   if (!execState || !jsval)
     return JNI_FALSE;
 
   ((ExecState*)execState)->setException((JSValue*)jsval);
-    TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1raiseJavaScriptException");
+    TRACE("SUCCESS LowLevelSaf__1raiseJavaScriptException");
   return JNI_TRUE;
 }
 
@@ -515,7 +647,7 @@
  */
 JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1unwrapDispatch
   (JNIEnv* env, jclass, jint jsval, jobjectArray rval) {
-    TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1unwrapDispatch");
+    TRACE("ENTER LowLevelSaf__1unwrapDispatch");
   if (!jsval)
     return JNI_FALSE;
 
@@ -528,7 +660,7 @@
     if (env->ExceptionCheck())
         return JNI_FALSE;
 
-    TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1unwrapDispatch");
+    TRACE("SUCCESS LowLevelSaf__1unwrapDispatch");
     return JNI_TRUE;
 }
 
@@ -539,7 +671,7 @@
  */
 JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1wrapDispatch
   (JNIEnv* env, jclass, jobject dispObj, jintArray rval) {
-    TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1wrapDispatch");
+    TRACE("ENTER LowLevelSaf__1wrapDispatch");
 
     jobject dispObjRef = env->NewGlobalRef(dispObj);
     if (!dispObjRef || env->ExceptionCheck())
@@ -553,11 +685,15 @@
    * out in favor of better memory mgmt scheme.
    */
   gcProtectNullTolerant(wrapper);
+  char buf[256];
+  snprintf(buf, sizeof(buf), " return={%08x} ", unsigned(wrapper));
+  PrintJSValue((JSValue*)wrapper, buf);
+
     env->SetIntArrayRegion(rval, 0, 1, (jint*)&wrapper);
     if (env->ExceptionCheck())
         return JNI_FALSE;
 
-    TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1wrapDispatch");
+    TRACE("SUCCESS LowLevelSaf__1wrapDispatch");
     return JNI_TRUE;
 }
 
@@ -568,7 +704,7 @@
  */
 JNIEXPORT jboolean JNICALL Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1wrapFunction
   (JNIEnv* env, jclass, jstring name, jobject dispMeth, jintArray rval) {
-    TRACE("ENTER Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1wrapFunction");
+    TRACE("ENTER LowLevelSaf__1wrapFunction");
 
     jobject dispMethRef = env->NewGlobalRef(dispMeth);
     if (!dispMethRef || env->ExceptionCheck())
@@ -586,11 +722,14 @@
    * out in favor of better memory mgmt scheme.
    */
   gcProtectNullTolerant(wrapper);
+  char buf[256];
+  snprintf(buf, sizeof(buf), " return={%08x} ", unsigned(wrapper));
+  PrintJSValue((JSValue*)wrapper, buf);
     env->SetIntArrayRegion(rval, 0, 1, (jint*)&wrapper);
     if (env->ExceptionCheck())
         return JNI_FALSE;
 
-    TRACE("SUCCESS Java_com_google_gwt_dev_shell_mac_LowLevelSaf__1wrapFunction");
+    TRACE("SUCCESS LowLevelSaf__1wrapFunction");
     return JNI_TRUE;
 }
 
diff --git a/jni/mac/gwt-webkit.h b/jni/mac/gwt-webkit.h
index 3ba982b..1cf9cb8 100644
--- a/jni/mac/gwt-webkit.h
+++ b/jni/mac/gwt-webkit.h
@@ -30,6 +30,7 @@
 
 //#define FILETRACE
 //#define JAVATRACE
+
 #if defined(FILETRACE) && defined(JAVATRACE)
 #define TRACE(s) filetrace(s),javatrace(s)
 #elif defined(FILETRACE)
diff --git a/jni/mac/prebuilt/libgwt-webkit.jnilib b/jni/mac/prebuilt/libgwt-webkit.jnilib
index 0fdd5b4..328bcb3 100755
--- a/jni/mac/prebuilt/libgwt-webkit.jnilib
+++ b/jni/mac/prebuilt/libgwt-webkit.jnilib
Binary files differ
diff --git a/user/src/com/google/gwt/core/client/JavaScriptObject.java b/user/src/com/google/gwt/core/client/JavaScriptObject.java
index f832daf..abbb960 100644
--- a/user/src/com/google/gwt/core/client/JavaScriptObject.java
+++ b/user/src/com/google/gwt/core/client/JavaScriptObject.java
@@ -41,31 +41,30 @@
   }-*/;
 
   /**
-   * the underlying JavaScript object.
+   * The underlying JavaScript object. This is used internally and should never
+   * be accessed by client code.
    */
-  protected final int opaque;
+  protected Object hostedModeReference;
 
   /**
-   * Creates a new <code>JavaScriptObject</code>. This constructor is used
-   * internally and should never be called by a user.
-   * 
-   * @param opaque the underlying JavaScript object
+   * Not directly instantiable.  Subclasses should also define a protected
+   * no-arg constructor to prevent client code from directly instantiating
+   * the class.
    */
-  protected JavaScriptObject(int opaque) {
-    this.opaque = opaque;
-  }
+  protected JavaScriptObject() {
+  };
 
   public boolean equals(Object other) {
     if (!(other instanceof JavaScriptObject)) {
       return false;
     }
     return equalsImpl(this, (JavaScriptObject) other);
-  };
+  }
 
   public int hashCode() {
     return Impl.getHashCode(this);
   }
-
+  
   public String toString() {
     /*
      * Hosted mode will marshal an explicit argument from a JavaScriptObject
diff --git a/user/src/com/google/gwt/user/client/Element.java b/user/src/com/google/gwt/user/client/Element.java
index 4b45bed..02cd3f0 100644
--- a/user/src/com/google/gwt/user/client/Element.java
+++ b/user/src/com/google/gwt/user/client/Element.java
@@ -29,13 +29,11 @@
 public final class Element extends JavaScriptObject {
 
   /**
-   * Creates a new <code>Element</code>. This constructor is used internally
-   * and should never be called by a user.
-   * 
-   * @param opaque the underlying DOM element
+   * Not directly instantiable.  Subclasses should also define a protected
+   * no-arg constructor to prevent client code from directly instantiating
+   * the class.
    */
-  Element(int opaque) {
-    super(opaque);
+  protected Element() {
   }
 
   /*
diff --git a/user/src/com/google/gwt/user/client/Event.java b/user/src/com/google/gwt/user/client/Event.java
index 8e1e641..fb36430 100644
--- a/user/src/com/google/gwt/user/client/Event.java
+++ b/user/src/com/google/gwt/user/client/Event.java
@@ -146,13 +146,11 @@
     | ONMOUSEOVER | ONMOUSEOUT;
 
   /**
-   * Creates a new <code>Element</code>. This constructor is used internally
-   * and should never be called by a user.
-   * 
-   * @param opaque the underlying DOM element
+   * Not directly instantiable.  Subclasses should also define a protected
+   * no-arg constructor to prevent client code from directly instantiating
+   * the class.
    */
-  Event(int opaque) {
-    super(opaque);
+  protected Event() {
   }
 
   /*
diff --git a/user/test/com/google/gwt/dev/jjs/test/HostedTest.java b/user/test/com/google/gwt/dev/jjs/test/HostedTest.java
index e36d6dd..a13c636 100644
--- a/user/test/com/google/gwt/dev/jjs/test/HostedTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/HostedTest.java
@@ -1,4 +1,18 @@
-// Copyright 2006 Google Inc. All Rights Reserved.
+/*
+ * Copyright 2007 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
 /**********************
  *                    *
  *   DO NOT FORMAT    *
@@ -7,6 +21,7 @@
 package com.google.gwt.dev.jjs.test;
 
 import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.GWT;
 import com.google.gwt.junit.client.GWTTestCase;
 
 public class HostedTest extends GWTTestCase {
@@ -15,6 +30,60 @@
     return s + "foo";
   }
 
+  private native static JavaScriptObject getBoxedBooleanAsObject(boolean v) /*-{
+    return new Boolean(v);
+  }-*/;
+  
+  private native static boolean getBoxedBooleanAsBool(boolean v) /*-{
+    return new Boolean(v);
+  }-*/;
+  
+  private native static JavaScriptObject getBoxedNumberAsObject(double v) /*-{
+    return new Number(v);
+  }-*/;
+  
+  private native static double getBoxedNumberAsDouble(double v) /*-{
+    return new Number(v);
+  }-*/;
+  
+  private native static JavaScriptObject getBoxedStringAsObject(String v) /*-{
+    return new String(v);
+  }-*/;
+  
+  private native static String getBoxedStringAsString(String v) /*-{
+    return new String(v);
+  }-*/;
+  
+  private native static double getDouble(double v) /*-{
+    return -v;
+  }-*/;
+  
+  private native static int getInt(int v) /*-{
+    return -v;
+  }-*/;
+
+  // this should cause an exception
+  private static native Object getIntAsObject() /*-{
+    return 5;
+  }-*/;
+  
+  private native static String getJSOAsString(JavaScriptObject jso) /*-{
+    return "" + jso;
+  }-*/;
+  
+  private native static Object getObject(Object o) /*-{
+    return o;
+  }-*/;
+
+  private native static String getString(String s) /*-{
+    return s + "me";
+  }-*/;
+
+  // ok to return JS string from an Object method
+  private static native Object getStringAsObject() /*-{
+    return "test";
+  }-*/;
+  
   private static native JavaScriptObject getsFooFunc() /*-{
     return @com.google.gwt.dev.jjs.test.HostedTest::sFoo(Ljava/lang/String;);
   }-*/;
@@ -51,6 +120,18 @@
     return "com.google.gwt.dev.jjs.CompilerSuite";
   }
 
+  public void testBasic() {
+    int iv = getInt(14);
+    assertEquals(iv, -14);
+    double dv = getDouble(31.5);
+    assertEquals(dv, -31.5, 0.0);
+    String sv = getString("test");
+    assertEquals(sv, "testme");
+    Object oin = String.class;
+    Object oout = getObject(oin);
+    assertEquals(oin, oout);
+  }
+  
   public void testByteMarshalling() {
     byte b = 100;
     assertEquals(100, byteAsInt(b));
@@ -142,7 +223,7 @@
    * Tests that we can use a source level name for a nested type instead of the
    * binary name.
    */
-  private static class A {
+  protected static class A {
     public static class B {
       int b = 1;
       public native int getUsingSourceRef() /*-{
@@ -165,6 +246,43 @@
     assertEquals(1, b.getUsingBinaryRef());
   }
 
+  /*
+   * Test that returning strings from methods declared as returning Object
+   * works, and that returning a primitive does not.
+   */
+  public void testObjectReturns() {
+    String str = (String)getStringAsObject();
+    assertEquals(str, "test");
+    try {
+      getIntAsObject();
+      // should have thrown an exception in hosted mode,
+      // so fail unless we are in web mode
+      assertTrue(GWT.isScript());
+    } catch(IllegalArgumentException e) {
+      // expected exception
+    }
+  }
+
+  /*
+   * Test that returning JavaScript boxed primitives works as expected.
+   * Currently only String is automatically unboxed, so Boolean and Number
+   * are currently disabled.
+   */
+  public void testAutoBoxing() {
+    JavaScriptObject bvo = getBoxedBooleanAsObject(true);
+    assertEquals(getJSOAsString(bvo), "true");
+    // boolean bv = getBoxedBooleanAsBool(true);
+    // assertEquals(bv, true);
+    JavaScriptObject nvo = getBoxedNumberAsObject(42);
+    assertEquals(getJSOAsString(nvo), "42");
+    // double nv = getBoxedNumberAsDouble(42);
+    // assertEquals(nv, 42, 0);
+    JavaScriptObject svo = getBoxedStringAsObject("test");
+    assertEquals(getJSOAsString(svo), "test");
+    String sv = getBoxedStringAsString("test");
+    assertEquals(sv, "test");
+  }
+
   /**
    * Tests that using the JavaScript toString method results in a call to 
    * the java.lang.Object::toString() method.
diff --git a/user/test/com/google/gwt/dev/jjs/test/MiscellaneousTest.java b/user/test/com/google/gwt/dev/jjs/test/MiscellaneousTest.java
index dd9defe..b86f760 100644
--- a/user/test/com/google/gwt/dev/jjs/test/MiscellaneousTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/MiscellaneousTest.java
@@ -33,10 +33,6 @@
   }
 
   private static class Foo extends JavaScriptObject {
-    protected Foo(int opaque) {
-      super(opaque);
-    }
-
     public String toString() {
       return "Foo";
     }