Fixes issue #1281; linux hosted mode was crashing sometimes during 
JS_SetReservedSlot(). There might have also been an unrelated issue with 
storing stale JSContext*'s inside of JsRootedValues.

- Keeps a static stack of JSContext*'s instead of storing them; this is 
safe to do because the JS engine is always higher on the call stack than 
any client-side Java code (including gwtOnLoad).
- Protects newborns that could sometimes be insta-gc'd within 
JsValueMoz._setWrappedJavaObject() and _setWrappedFunction().
- Reorganized some functions into more appropriate cpp files.

Patch by: jat, scottb
Review by: scottb, jat :)



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1268 8db76d5a-ed1c-0410-87a9-c151d255dfc7
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
index 81e9735..01f80d8 100644
--- a/dev/linux/src/com/google/gwt/dev/shell/moz/JsValueMoz.java
+++ b/dev/linux/src/com/google/gwt/dev/shell/moz/JsValueMoz.java
@@ -129,7 +129,7 @@
     }
 
     public void doCleanup() {
-      JsValueMoz.destroyJsRootedValue(jsRootedValue);
+      _destroyJsRootedValue(jsRootedValue);
     }
   }
 
@@ -141,23 +141,14 @@
   /**
    * Flag to enable debug checks on underlying JsRootedValues.
    */
-  private static final DebugLogging debugInfo = debugFlag ? new DebugLogging() : null;
+  private static final DebugLogging debugInfo = debugFlag ? new DebugLogging()
+      : null;
 
   /**
    * This must match the value from jsapi.h.
    */
   private static final int JSVAL_VOID = 0x80000001;
 
-  /**
-   * 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);
-  }
-
   // CHECKSTYLE_NAMING_OFF -- native methods start with '_'
   protected static native boolean _getBoolean(int jsRootedValue);
 
@@ -212,32 +203,22 @@
 
   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;
-  }
+  private static native int _createJsRootedValue(int jsval);
 
   /**
    * Destroy a JsRootedValue.
    * 
    * @param jsRootedValue pointer to underlying JsRootedValue as an integer.
    */
-  private static void destroyJsRootedValue(int jsRootedValue) {
-    _destroyJsRootedValue(jsRootedValue);
-  }
+  private static native void _destroyJsRootedValue(int jsRootedValue);
+
+  // CHECKSTYLE_NAMING_ON
 
   /**
    * Convert an address to a hex string.
@@ -255,6 +236,16 @@
   private int jsRootedValue;
 
   /**
+   * Create a JsValueMoz object representing the undefined value.
+   */
+  public JsValueMoz() {
+    this.jsRootedValue = _createJsRootedValue(JSVAL_VOID);
+    if (debugFlag) {
+      debugInfo.createInstance(jsRootedValue);
+    }
+  }
+
+  /**
    * Create a JsValueMoz object wrapping a JsRootedValue object given the
    * pointer to it as an integer.
    * 
@@ -279,21 +270,6 @@
     }
   }
 
-  /**
-   * 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);
-    if (debugFlag) {
-      debugInfo.createInstance(jsRootedValue);
-    }
-  }
-
   /*
    * (non-Javadoc)
    * 
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 f4875a0..b95a9ff 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
@@ -35,9 +35,9 @@
      * @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
+     *          value
      */
-    void invoke(int jscontext, int jsthis, int[] jsargs, int returnValue);
+    void invoke(int jsthis, int[] jsargs, int returnValue);
   }
 
   /**
@@ -119,8 +119,8 @@
    * 
    * @throws RuntimeException if the invoke fails
    */
-  public static void invoke(int scriptObject, String methodName,
-      int jsthis, int[] jsargs, int retval) {
+  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.");
@@ -130,11 +130,9 @@
   /**
    * Call this to raise an exception in JavaScript before returning control.
    * Currently, the JavaScript exception throw is always null.
-   * 
-   * @param jscontext A JSContext pointer as a Java int
    */
-  public static void raiseJavaScriptException(int jscontext) {
-    if (!_raiseJavaScriptException(jscontext)) {
+  public static void raiseJavaScriptException() {
+    if (!_raiseJavaScriptException()) {
       throw new RuntimeException(
           "Failed to raise Java Exception into JavaScript.");
     }
@@ -192,7 +190,6 @@
   }
 
   // CHECKSTYLE_NAMING_OFF: Non JSNI native code may have leading '_'s.
-  private static native boolean _executeScript(int scriptObject, String code);
 
   private static native boolean _executeScriptWithInfo(int scriptObject,
       String newScript, String file, int line);
@@ -203,7 +200,7 @@
    * @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
+   *          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
@@ -211,7 +208,7 @@
   private static native boolean _invoke(int scriptObject, String methodName,
       int jsThisInt, int[] jsArgsInt, int jsRetValInt);
 
-  private static native boolean _raiseJavaScriptException(int jscontext);
+  private static native boolean _raiseJavaScriptException();
 
   private static native boolean _registerExternalFactoryHandler();
 
@@ -219,6 +216,7 @@
 
   /**
    * Print debug information for a JS method invocation.
+   * 
    * TODO(jat): remove this method
    * 
    * @param methodName the name of the JS method being invoked
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 2297ee4..ea7301f 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
@@ -40,20 +40,18 @@
   }
 
   /**
-   * Invoke a Java method from JavaScript.
-   * This is called solely from native code.
+   * 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)
+   * exceptions still need to be made platform-independent)
    */
-  public void invoke(int jscontext, int jsthisInt, int[] jsargsInt,
-      int returnValueInt) {
+  public void invoke(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) {
@@ -89,11 +87,11 @@
       // Java back into JavaScript
       Throwable t = e.getTargetException();
       // TODO(jat): if this was originally JavaScript exception, re-throw the
-      // original exception rather than just a null. 
+      // original exception rather than just a null.
       ModuleSpaceMoz.setThrownJavaException(t);
-      LowLevelMoz.raiseJavaScriptException(jscontext);
+      LowLevelMoz.raiseJavaScriptException();
     } catch (IllegalArgumentException e) {
-      // TODO(jat): log to treelogger instead?  If so, how do I get to it?
+      // 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) {
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 2b1fa7d..e9430e4 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
@@ -79,7 +79,7 @@
   protected JsValue doInvoke(String name, Object jthis, Class[] types,
       Object[] args) {
 
-    JsValueMoz jsthis = JsValueMoz.createUndefinedValue(window);
+    JsValueMoz jsthis = new JsValueMoz();
     CompilingClassLoader isolatedClassLoader = getIsolatedClassLoader();
     jsthis.setWrappedJavaObject(isolatedClassLoader, jthis);
 
@@ -87,11 +87,11 @@
     JsValueMoz argv[] = new JsValueMoz[argc];
     int[] jsArgsInt = new int[argc];
     for (int i = 0; i < argc; ++i) {
-      argv[i] = JsValueMoz.createUndefinedValue(window);
+      argv[i] = new JsValueMoz();
       JsValueGlue.set(argv[i], isolatedClassLoader, types[i], args[i]);
       jsArgsInt[i] = argv[i].getJsRootedValue();
     }
-    JsValueMoz returnVal = JsValueMoz.createUndefinedValue(window);
+    JsValueMoz returnVal = new JsValueMoz();
     LowLevelMoz.invoke(window, name, jsthis.getJsRootedValue(), jsArgsInt,
         returnVal.getJsRootedValue());
     return returnVal;
diff --git a/jni/linux/ExternalWrapper.cpp b/jni/linux/ExternalWrapper.cpp
index eff2802..d13d8b1 100644
--- a/jni/linux/ExternalWrapper.cpp
+++ b/jni/linux/ExternalWrapper.cpp
@@ -41,6 +41,7 @@
 {
   Tracer tracer("gwtOnLoad");
   tracer.log("context=%08x", unsigned(cx));
+  JsRootedValue::ContextManager context(cx);
   JsRootedValue::ensureRuntime(cx);
   if (argc < 2) {
     tracer.setFail("less than 2 args");
@@ -120,6 +121,7 @@
     JSObject *obj, jsval id, jsval *vp)
 {
   Tracer tracer("gwt_external_getProperty");
+  JsRootedValue::ContextManager context(cx);
   if (*vp != JSVAL_VOID)
     return JS_TRUE;
 
@@ -166,10 +168,13 @@
 
 static void JS_DLL_CALLBACK gwt_external_finalize(JSContext *cx, JSObject *obj)
 {
+  // We don't need to push a context if all we do is DeleteGlobalRef
+  Tracer tracer("gwt_external_finalize", obj);
   jobject externalObject = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
-  if (externalObject)
+  if (externalObject) {
     savedJNIEnv->DeleteGlobalRef(externalObject);
-  JS_FinalizeStub(cx,obj);
+  }
+  JS_FinalizeStub(cx, obj);
 }
 
 static JSBool JS_DLL_CALLBACK gwt_external_setProperty(JSContext *cx,
@@ -201,7 +206,14 @@
     tracer.setFail("null script object pointer");
     return NS_ERROR_INVALID_POINTER;
   }
-  if (!mScriptObject) {
+  if (!jsWindowExternalObject) {
+    JSContext* cx = NS_REINTERPRET_CAST(JSContext*,
+        aContext->GetNativeContext());
+    if (!cx) {
+      tracer.setFail("can't get JSContext");
+      return NS_ERROR_UNEXPECTED;
+    }
+    JsRootedValue::ContextManager context(cx);
     *aScriptObject = 0;
 
     nsIScriptGlobalObject* globalObject = aContext->GetGlobalObject();
@@ -239,12 +251,6 @@
       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) {
@@ -262,16 +268,16 @@
       tracer.setFail("can't define gwtOnLoad function on JavaScript object");
       return NS_ERROR_UNEXPECTED;
     }
-    mScriptObject = newObj;
+    jsWindowExternalObject = newObj;
   }
 
-  *aScriptObject = mScriptObject;
+  *aScriptObject = jsWindowExternalObject;
   return NS_OK;
 }
 
 NS_IMETHODIMP ExternalWrapper::SetScriptObject(void* aScriptObject)
 {
-    mScriptObject = aScriptObject;
+    jsWindowExternalObject = aScriptObject;
     return NS_OK;
 }
 
diff --git a/jni/linux/ExternalWrapper.h b/jni/linux/ExternalWrapper.h
index 7cabc23..0168083 100644
--- a/jni/linux/ExternalWrapper.h
+++ b/jni/linux/ExternalWrapper.h
@@ -22,10 +22,10 @@
   NS_DECL_ISUPPORTS
   NS_IMETHOD GetScriptObject(nsIScriptContext *aContext, void** aScriptObject);
   NS_IMETHOD SetScriptObject(void* aScriptObject);
-  ExternalWrapper(): mScriptObject(0) { }
+  ExternalWrapper(): jsWindowExternalObject(0) { }
 private:
   ~ExternalWrapper() { }
-  void *mScriptObject;
+  void *jsWindowExternalObject;
 };
 
 class nsRpExternalFactory : public nsIFactory {
diff --git a/jni/linux/JsRootedValue.cpp b/jni/linux/JsRootedValue.cpp
index 90b890b..29b95ca 100644
--- a/jni/linux/JsRootedValue.cpp
+++ b/jni/linux/JsRootedValue.cpp
@@ -16,25 +16,29 @@
 
 #include "JsRootedValue.h"
 
-// intialize static value used to hold the JavaScript String class.
+// Initialize static value used to hold the JavaScript String class.
 JSClass* JsRootedValue::stringClass = 0;
 
-// intialize static reference to the sole JSRuntime value in Gecko
+// Initialize static reference to the sole JSRuntime value in Gecko.
 JSRuntime* JsRootedValue::runtime = 0;
 
+// Static stack of JavaScript execution contexts.
+std::stack<JSContext*> JsRootedValue::contextStack;
+
 /*
  * Actually get the stringClass pointer from JavaScript.
  */
 void JsRootedValue::fetchStringClass() const {
   Tracer tracer("JsRootedValue::fetchStringClass");
-  jsval val = JS_GetEmptyStringValue(context_);
+  JSContext* cx = currentContext();
+  jsval val = JS_GetEmptyStringValue(cx);
   JSObject* obj;
   // on error, leave stringClass null
-  if (!JS_ValueToObject(context_, val, &obj)) return;
+  if (!JS_ValueToObject(cx, val, &obj)) return;
   if (!obj) {
     tracer.log("ensureStringClass: null object");
     return;
   }
-  stringClass = JS_GET_CLASS(context_, obj);
+  stringClass = JS_GET_CLASS(cx, obj);
   tracer.log("stringClass=%08x", unsigned(stringClass));
 }
diff --git a/jni/linux/JsRootedValue.h b/jni/linux/JsRootedValue.h
index 881ac12..65c25af 100644
--- a/jni/linux/JsRootedValue.h
+++ b/jni/linux/JsRootedValue.h
@@ -20,6 +20,7 @@
 #include "mozilla-headers.h"
 
 #include "Tracer.h"
+#include <stack>
 
 extern "C" JSClass gwt_nativewrapper_class;
 
@@ -42,13 +43,14 @@
 {
 private:
   // the JavaScript String class
-  static JSClass*              stringClass;
+  static JSClass* stringClass;
   
   // Javascript runtime
-  static JSRuntime*            runtime;
+  static JSRuntime* runtime;
 
-  // Javascript context
-  JSContext*    context_;
+  // stack of Javascript contexts
+  static std::stack<JSContext*> contextStack;
+
   // underlying Javascript value
   jsval         value_;
 
@@ -78,13 +80,64 @@
     }
   }
 
+  /*
+   * Push a new JavaScript execution context.
+   */
+  static void pushContext(JSContext* context) {
+    Tracer tracer("JsRootedValue::pushContext");
+    tracer.log("pushed context=%08x", unsigned(context));
+    contextStack.push(context);
+  }
+  
+  /*
+   * Pop a JavaScript execution context from the context stack.
+   */
+  static void popContext() {
+    Tracer tracer("JsRootedValue::popContext");
+    JSContext* context = currentContext();
+    contextStack.pop();
+    tracer.log("popped context=%08x", unsigned(context));
+  }
+
 public:
   /*
+   * This is a helper class used to push/pop JSContext values automatically,
+   * using RAII.  Simply create a ContextManager object on the stack and it
+   * will push the context at creation time and pop it at destruction time,
+   * so you don't have to worry about covering all the exit paths from a
+   * function.
+   */
+  class ContextManager {
+  public:
+    explicit ContextManager(JSContext* context) {
+      JsRootedValue::pushContext(context);
+    }
+    ~ContextManager() {
+      JsRootedValue::popContext();
+    }
+  };
+
+  /*
+   * This is a helper class to manage short-lived roots on the stack, using
+   * RAII to free the roots when no longer needed.
+   */
+  class Temp {
+  private:
+    void* ptr_;
+  public:
+    explicit Temp(void* ptr) : ptr_(ptr) {
+      JS_AddNamedRootRT(runtime, ptr_, "temporary root");
+    }
+    ~Temp() {
+      JS_RemoveRootRT(runtime, ptr_);
+    }
+  };
+  
+  /*
    * Copy constructor - make another rooted value that refers to the same
    * JavaScript object (or has the same value if a primitive)
    */
-  JsRootedValue(const JsRootedValue& rooted_value)
-      : context_(rooted_value.context_), value_(rooted_value.value_)
+  JsRootedValue(const JsRootedValue& rooted_value) : value_(rooted_value.value_)
   {
     constructorHelper("JsRootedValue copy ctor");
   }
@@ -92,8 +145,7 @@
   /*
    * Create a value with a given jsval value
    */
-  JsRootedValue(JSContext* context, jsval value)
-      : context_(context), value_(value)
+  JsRootedValue(jsval value) : value_(value)
   {
     constructorHelper("JsRootedValue jsval ctor");
   }
@@ -101,7 +153,7 @@
   /*
    * Create a void value
    */
-  JsRootedValue(JSContext* context) : context_(context), value_(JSVAL_VOID) {
+  JsRootedValue() : value_(JSVAL_VOID) {
     constructorHelper("JsRootedValue void ctor");
   }
   
@@ -110,7 +162,6 @@
    */
   ~JsRootedValue() {
     Tracer tracer("~JsRootedValue", this);
-    tracer.log("context=%08x", unsigned(context_));
     // ignore error since currently it is not possible to fail
     JS_RemoveRootRT(runtime, &value_);
   }
@@ -123,32 +174,26 @@
   }
   
   /*
-   * Return the JSContext* pointer.
+   * Return the current JavaScript execution context.
    */
-  JSContext* getContext() const { return context_; }
-  
-  /*
-   * Return the global object for this value's context.
-   */
-  JSObject* getGlobalObject() const { return JS_GetGlobalObject(getContext()); }
-  
+  static JSContext* currentContext() {
+    Tracer tracer("JsRootedValue::currentContext");
+    if (contextStack.empty()) {
+      // TODO(jat): better error handling?
+      fprintf(stderr, "JsRootedValue::currentContext - context stack empty\n");
+      ::abort();
+    }
+    JSContext* context = contextStack.top();
+    tracer.log("context=%08x", unsigned(context));
+    return context;
+  }
+
   /* 
    * Return the underlying JS object
    */
   jsval getValue() const { return value_; }
   
   /*
-   * Sets the value of the underlying JS object 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.
@@ -172,7 +217,7 @@
   double getDouble() const {
     jsdouble return_value=0.0;
     // ignore return value -- if it fails, value will remain 0.0
-    JS_ValueToNumber(getContext(), value_, &return_value); 
+    JS_ValueToNumber(currentContext(), value_, &return_value); 
     return double(return_value);
   }
   /*
@@ -182,7 +227,7 @@
    */
   bool setDouble(double val) {
     jsval js_double;
-    if(!JS_NewDoubleValue(getContext(), jsdouble(val), &js_double)) {
+    if(!JS_NewDoubleValue(currentContext(), jsdouble(val), &js_double)) {
       return false;
     }
     return setValue(js_double);
@@ -261,7 +306,7 @@
    * Return this value as a string, converting as necessary.
    */
   JSString* asString() const {
-    return JS_ValueToString(getContext(), value_);
+    return JS_ValueToString(currentContext(), value_);
   }
   
   /* Returns the string as a JSString pointer.
@@ -299,7 +344,7 @@
    * Returns false on failure.
    */
   bool setString(const wchar_t* utf16) {
-    JSString* str = JS_NewUCStringCopyZ(getContext(),
+    JSString* str = JS_NewUCStringCopyZ(currentContext(),
         reinterpret_cast<const jschar*>(utf16));
     return setValue(STRING_TO_JSVAL(str));
   }
@@ -310,7 +355,7 @@
    * Returns false on failure.
    */
   bool setString(const wchar_t* utf16, size_t len) {
-    JSString* str = JS_NewUCStringCopyN(getContext(),
+    JSString* str = JS_NewUCStringCopyN(currentContext(),
         reinterpret_cast<const jschar*>(utf16), len);
     return setValue(STRING_TO_JSVAL(str));
   }
@@ -336,7 +381,7 @@
    * Result is undefined if it is not actually an object.
    */
   const JSClass* getObjectClass() const {
-    return isObject() ? JS_GET_CLASS(getContext(), getObject()) : 0;
+    return isObject() ? JS_GET_CLASS(currentContext(), getObject()) : 0;
   }
   
   /*
diff --git a/jni/linux/JsValueMoz.cpp b/jni/linux/JsValueMoz.cpp
index 5398b76..be30ea4 100644
--- a/jni/linux/JsValueMoz.cpp
+++ b/jni/linux/JsValueMoz.cpp
@@ -34,15 +34,18 @@
  * Returns null on failure.  Caller is responsible for deleting
  * returned JsRootedValue when done with it.
  */
-JsRootedValue* GetFieldAsRootedValue(JSContext* context, jclass clazz,
+static JsRootedValue* GetFieldAsRootedValue(JSContext* cx, jclass clazz,
     jobject obj, jstring fieldName)
 {
+  Tracer tracer("GetFieldAsRootedValue");
+  JsRootedValue::ContextManager context(cx);
   jmethodID getFieldMeth = savedJNIEnv->GetMethodID(clazz, "getField",
       "(Ljava/lang/String;I)V");
-  if (!getFieldMeth || savedJNIEnv->ExceptionCheck())
+  if (!getFieldMeth || savedJNIEnv->ExceptionCheck()) {
     return 0;
+  }
 
-  JsRootedValue* jsRootedValue = new JsRootedValue(context);
+  JsRootedValue* jsRootedValue = new JsRootedValue();
   savedJNIEnv->CallVoidMethod(obj, getFieldMeth, fieldName,
   	 reinterpret_cast<jint>(jsRootedValue));
   if (savedJNIEnv->ExceptionCheck()) {
@@ -63,18 +66,22 @@
  * 
  * returns true on success, false on failure
  */
-bool SetFieldFromRootedValue(JSContext* context, jclass clazz,
+static bool SetFieldFromRootedValue(JSContext* cx, jclass clazz,
     jobject obj, jstring fieldName, JsRootedValue* jsRootedValue)
 {
+  Tracer tracer("SetFieldAsRootedValue");
+  JsRootedValue::ContextManager context(cx);
   jmethodID getFieldMeth = savedJNIEnv->GetMethodID(clazz, "setField",
       "(Ljava/lang/String;I)V");
-  if (!getFieldMeth || savedJNIEnv->ExceptionCheck())
+  if (!getFieldMeth || savedJNIEnv->ExceptionCheck()) {
     return false;
+  }
 
   savedJNIEnv->CallVoidMethod(obj, getFieldMeth, fieldName,
   	 reinterpret_cast<jint>(jsRootedValue));
-  if (savedJNIEnv->ExceptionCheck())
+  if (savedJNIEnv->ExceptionCheck()) {
     return false;
+  }
 
   return true;
 }
@@ -88,7 +95,9 @@
   jniEnv->ThrowNew(exceptionClass, msg);
 }
 
-
+/*
+ * Types of jsvals.
+ */
 enum JsValueType {
   JSVAL_TYPE_VOID=0,
   JSVAL_TYPE_NULL,
@@ -99,6 +108,9 @@
   JSVAL_TYPE_UNKNOWN,
 };
 
+/*
+ * Names of jsval types -- must match the order of the enum above.
+ */
 static const char* JsValueTypeStrings[]={
   "undefined",
   "null",
@@ -109,6 +121,9 @@
   "unknown",
 };
 
+/*
+ * Return the type of a jsval.
+ */
 static JsValueType GetValueType(jsval val) {
   if(JSVAL_IS_VOID(val)) {
     return JSVAL_TYPE_VOID;
@@ -128,24 +143,156 @@
 }
 
 /*
+ * Called from JavaScript to call a Java method that has previously been
+ * wrapped.  See _setWrappedFunction for use.
+ */ 
+static JSBool invokeJavaMethod(JSContext *cx, JSObject *obj, uintN argc,
+    jsval *argv, jsval *rval)
+{
+  Tracer tracer("invokeJavaMethod");
+  JsRootedValue::ContextManager context(cx);
+
+  // I kid you not; this is how XPConnect gets their function object so they can
+  // multiplex dispatch the call from a common site.  See XPCDispObject.cpp(466)
+  //
+  // I now have a secondary confirmation that this trick is legit.
+  // brandon@mozilla.org writes:
+  //
+  // argv[-2] is part of the JS API, unabstracted.  Just as argv[0] is the
+  // first argument (if argc != 0), argv[-1] is the |this| parameter (equal
+  // to OBJECT_TO_JSVAL(obj) in a native method with the standard |obj|
+  // second formal parameter name), and argv[-2] is the callee object, tagged
+  // as a jsval.
+  if (JS_TypeOfValue(cx, argv[-2]) != JSTYPE_FUNCTION) {
+    tracer.setFail("not a function type");
+    return JS_FALSE;
+  }
+  JSObject* funObj = JSVAL_TO_OBJECT(argv[-2]);
+
+  // Pull the wrapper object out of the funObj's reserved slot
+  jsval jsCleanupObj;
+  if (!JS_GetReservedSlot(cx, funObj, 0, &jsCleanupObj)) {
+    tracer.setFail("JS_GetReservedSlot failed");
+    return JS_FALSE;
+  }
+  JSObject* cleanupObj = JSVAL_TO_OBJECT(jsCleanupObj);
+  if (!cleanupObj) {
+    tracer.setFail("cleanupObj is null");
+    return JS_FALSE;
+  }
+
+  // Get DispatchMethod instance out of the wrapper object
+  jobject dispMeth =
+      NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, cleanupObj));
+  if (!dispMeth) {
+    tracer.setFail("dispMeth is null");
+    return JS_FALSE;
+  }
+  jclass dispClass = savedJNIEnv->GetObjectClass(dispMeth);
+  if (!dispClass || savedJNIEnv->ExceptionCheck()) {
+    tracer.setFail("GetObjectClass returns null");
+    return JS_FALSE;
+  }
+
+  // lookup the invoke method on the dispatch object
+  jmethodID invokeID =
+      savedJNIEnv->GetMethodID(dispClass, "invoke", "(I[II)V");
+  if (!invokeID || savedJNIEnv->ExceptionCheck()) {
+    tracer.setFail("GetMethodID failed");
+    return JS_FALSE;
+  }
+
+  // create an array of integers to hold the JsRootedValue pointers passed
+  // to the invoke method
+  jintArray args = savedJNIEnv->NewIntArray(argc);
+  if (!args || savedJNIEnv->ExceptionCheck()) {
+    tracer.setFail("NewIntArray failed");
+    return JS_FALSE;
+  }
+
+  // these arguments are already rooted by the JS interpreter, but we
+  // can't easily take advantage of that without complicating the JsRootedValue
+  // interface.
+  
+  // argv[-1] is OBJECT_TO_JSVAL(this)
+  JsRootedValue* jsThis = new JsRootedValue(argv[-1]);
+  tracer.log("jsthis=%08x, RV=%08x", unsigned(argv[-1]), unsigned(jsThis));
+
+  // create JsRootedValues for arguments  
+  JsRootedValue *jsArgs[argc]; 
+  for (uintN i = 0; i < argc; ++i) {
+    jsArgs[i] = new JsRootedValue(argv[i]);
+  }
+  savedJNIEnv->SetIntArrayRegion(args, 0, argc,
+      reinterpret_cast<jint*>(jsArgs));
+  if (savedJNIEnv->ExceptionCheck()) {
+    tracer.setFail("SetIntArrayRegion failed");
+    return JS_FALSE;
+  }
+  
+  // slot for return value
+  JsRootedValue* jsReturnVal = new JsRootedValue();
+
+  // TODO(jat): small window here where invocation may fail before Java
+  // takes ownership of the JsRootedValue objects.  One solution would be
+  // to reference-count them between Java and C++ (so the reference count
+  // would always be 0, 1, or 2).  Also setField has a similar problem.
+  // I plan to fix this when switching away from Java holding pointers to
+  // C++ objects as part of the fix for 64-bit support (which we could
+  // accomplish inefficiently by changing int to long everywhere, but there
+  // are other 64-bit issues to resolve and we need to reduce the number of
+  // roots the JS interpreter has to search.
+  
+  // call Java method
+  savedJNIEnv->CallVoidMethod(dispMeth, invokeID, reinterpret_cast<int>(jsThis),
+      args, reinterpret_cast<int>(jsReturnVal));
+  
+  JSBool returnValue = JS_TRUE;
+  
+  if (savedJNIEnv->ExceptionCheck()) {
+    tracer.log("dispMeth=%08x", unsigned(dispMeth));
+    tracer.setFail("java exception is active:");
+    jobject exception = savedJNIEnv->ExceptionOccurred();
+    if (exception) {
+      fprintf(stderr, "Exception occurred in MethodDispatch.invoke:\n");
+      savedJNIEnv->ExceptionDescribe();
+      savedJNIEnv->DeleteLocalRef(exception);
+    }
+    returnValue = JS_FALSE;
+  } else if (JS_IsExceptionPending(cx)) {
+    tracer.setFail("js exception is active");
+    returnValue = JS_FALSE;
+  }
+
+  // extract return value
+  *rval = jsReturnVal->getValue();
+
+#if 0
+  // NOTE: C++ objects are not cleaned up here because Java now owns them.
+  // TODO(jat): if reference-counted, they *do* need to be Released here.
+  
+  // free JsRootedValues
+  for (uintN i = 0; i < argc; ++i) {
+    delete jsArgs[i];
+  }
+  delete jsThis;
+  delete jsReturnVal;
+#endif
+
+  return returnValue;
+}
+
+/*
  * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
  * Method:    _createJsRootedValue()
- * Signature: (II)I
+ * Signature: (I)I
  */
 extern "C" JNIEXPORT jint JNICALL
 Java_com_google_gwt_dev_shell_moz_JsValueMoz__1createJsRootedValue
-  (JNIEnv* jniEnv, jclass, jint scriptObjInt, jint jsval)
+  (JNIEnv* jniEnv, jclass, 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);
+  JsRootedValue* jsRootedValue = new JsRootedValue(jsval);
   return NS_REINTERPRET_CAST(jint, jsRootedValue);
 }
 
@@ -203,8 +350,6 @@
  * 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
@@ -274,14 +419,14 @@
       (jsRootedValueInt);
   Tracer tracer("JsValueMoz._getTypeString", jsRootedValue);
   jsval val = jsRootedValue->getValue();
-  JSContext* context = jsRootedValue->getContext();
+  JSContext* cx = JsRootedValue::currentContext();
   JsValueType valueType = GetValueType(val);
   const char* typeString = 0;
   char buf[256];
   if(valueType == JSVAL_TYPE_OBJECT) {
     JSObject* jsObject = JSVAL_TO_OBJECT(val);
-    JSClass* objClass = JS_GET_CLASS(context, jsObject);
-    if (JS_InstanceOf(context, jsObject,
+    JSClass* objClass = JS_GET_CLASS(cx, jsObject);
+    if (JS_InstanceOf(cx, jsObject,
         &gwt_nativewrapper_class, 0)) {
       typeString = "Java object";
     } else {
@@ -315,14 +460,14 @@
     return 0;
   }
   JSObject* jsObject = JSVAL_TO_OBJECT(val);
-  JSContext* context = jsRootedValue->getContext();
-  if(!JS_InstanceOf(context, jsObject, &gwt_nativewrapper_class, 0)) {
+  JSContext* cx = JsRootedValue::currentContext();
+  if(!JS_InstanceOf(cx, jsObject, &gwt_nativewrapper_class, 0)) {
     tracer.throwHostedModeException(jniEnv,
       "Javascript object not a Java object");
     return 0;
   } 
   jobject javaObject
-      = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(context, jsObject));
+      = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, jsObject));
   return javaObject;
 } 
 
@@ -374,7 +519,7 @@
   bool returnValue = false;
   if(JSVAL_IS_OBJECT(val)) {
     JSObject* jsObject = JSVAL_TO_OBJECT(val);
-    returnValue = !JS_InstanceOf(jsRootedValue->getContext(), jsObject,
+    returnValue = !JS_InstanceOf(JsRootedValue::currentContext(), jsObject,
         &gwt_nativewrapper_class, 0);
     tracer.log("jsobject=%08x, isJSObject=%s", unsigned(jsObject),
         returnValue ? "true" : "false");
@@ -485,7 +630,7 @@
   bool returnValue = false;
   if(JSVAL_IS_OBJECT(val)) {
     JSObject* jsObject = JSVAL_TO_OBJECT(val);
-    returnValue = JS_InstanceOf(jsRootedValue->getContext(), jsObject,
+    returnValue = JS_InstanceOf(JsRootedValue::currentContext(), jsObject,
         &gwt_nativewrapper_class, 0);
     tracer.log("jsobject=%08x, wrappedJava=%s", unsigned(jsObject),
         returnValue ? "true" : "false");
@@ -567,8 +712,7 @@
   JsRootedValue* jsOtherRootedValue = reinterpret_cast<JsRootedValue*>
       (jsOtherRootedValueInt);
   Tracer tracer("JsValueMoz._setJsRootedValue", jsRootedValue);
-  jsRootedValue->setContextValue(jsOtherRootedValue->getContext(),
-      jsOtherRootedValue->getValue());
+  jsRootedValue->setValue(jsOtherRootedValue->getValue());
 }
 
 /*
@@ -647,15 +791,17 @@
   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,
+  JSContext* cx = JsRootedValue::currentContext();
+  JSObject* scriptWindow = JS_GetGlobalObject(cx);
+  JSObject* newObj = JS_NewObject(cx, &gwt_nativewrapper_class, 0,
       scriptWindow);
   if (!newObj) {
     tracer.throwHostedModeException(jniEnv,
         "Unable to allocate JS object to wrap Java object");
     return;
   }
+  // Save in output value so it won't get GCed.
+  jsRootedValue->setObject(newObj); 
   tracer.log("jsobject=%08x", unsigned(newObj));
   
   // TODO(jat): how does this globalref get freed?
@@ -665,7 +811,7 @@
         "Unable to allocate global reference for JS wrapper");
     return;
   } 
-  if (!JS_SetPrivate(context, newObj, dispObjRef)) {
+  if (!JS_SetPrivate(cx, newObj, dispObjRef)) {
     jniEnv->DeleteGlobalRef(dispObjRef);
     tracer.throwHostedModeException(jniEnv,
         "Unable to allocate global reference for JS wrapper");
@@ -674,39 +820,34 @@
   // 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");
+    tracer.throwHostedModeException(jniEnv, "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");
+    tracer.throwHostedModeException(jniEnv, "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");
+    tracer.throwHostedModeException(jniEnv,
+        "Can't create Java string for toString method name");
     return;
   }
   // allocate a new root to hold the result of the getField call
-  JsRootedValue* toStringFunc = new JsRootedValue(context, JSVAL_VOID); 
+  JsRootedValue* toStringFunc = new JsRootedValue(); 
   jniEnv->CallVoidMethod(obj, getFieldMeth, ident,
       NS_REINTERPRET_CAST(jint, toStringFunc));
   if (toStringFunc->isUndefined() || jniEnv->ExceptionCheck()) {
-    jniEnv->DeleteGlobalRef(dispObjRef);
-    tracer.setFail("getField(toString) failed");
+    tracer.throwHostedModeException(jniEnv, "getField(toString) failed");
     return;
   } 
-  if (!JS_DefineProperty(context, newObj, "toString", toStringFunc->getValue(),
+  if (!JS_DefineProperty(cx, newObj, "toString", toStringFunc->getValue(),
       JS_PropertyStub, JS_PropertyStub, JSPROP_READONLY | JSPROP_PERMANENT)) {
-    jniEnv->DeleteGlobalRef(dispObjRef);
-    tracer.setFail("can't define JS toString method");
+    tracer.throwHostedModeException(jniEnv, "Can't define JS toString method");
     return;
   }
-  jsRootedValue->setObject(newObj); 
 } 
 
 /*
@@ -724,8 +865,8 @@
   JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
       (jsRootedValueInt);
   Tracer tracer("JsValueMoz._setWrappedFunction", jsRootedValue);
-  JSContext* context = jsRootedValue->getContext();
-  JSObject* scriptWindow = JS_GetGlobalObject(context);
+  JSContext* cx = JsRootedValue::currentContext();
+  JSObject* scriptWindow = JS_GetGlobalObject(cx);
   JStringWrap nameStr(jniEnv, methodName);
   if (!nameStr.str()) {
     tracer.throwHostedModeException(jniEnv,
@@ -734,8 +875,8 @@
   }
   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());
+  JSFunction* function = JS_NewFunction(cx, invokeJavaMethod, 0,
+      JSFUN_LAMBDA, 0, nameStr.str());
   if (!function) {
     tracer.throwHostedModeException(jniEnv, "JS_NewFunction failed");
     return;
@@ -745,31 +886,35 @@
     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,
+  // Save in output value so it won't get GCed.
+  jsRootedValue->setObject(funObj); 
+
+  // Create a cleanup object to hold and clean up dispMeth
+  JSObject* cleanupObj = JS_NewObject(cx, &gwt_functionwrapper_class, 0,
       scriptWindow);
   if (!cleanupObj) {
     tracer.throwHostedModeException(jniEnv, "JS_NewObject failed");
     return;
   }
-  jobject dispMethRef = jniEnv->NewGlobalRef(dispatchMethod);
-  if (!dispMethRef || jniEnv->ExceptionCheck()) {
+  tracer.log("funObj=%08x, cleanupObj=%08x", unsigned(funObj),
+      unsigned(cleanupObj));
+  // Store the cleanup object in funObj's reserved slot; now GC protected.
+  if(!JS_SetReservedSlot(cx, funObj, 0, OBJECT_TO_JSVAL(cleanupObj))) {
+    tracer.throwHostedModeException(jniEnv, "JS_SetReservedSlot failed");
     return;
   }
-
+  jobject dispMethRef = jniEnv->NewGlobalRef(dispatchMethod);
+  if (!dispMethRef || jniEnv->ExceptionCheck()) {
+    tracer.throwHostedModeException(jniEnv,
+        "NewGlobalRef(dispatchMethod) failed");
+    return;
+  }
   // Store our global ref in the wrapper object
-  if (!JS_SetPrivate(context, cleanupObj, dispMethRef)) {
+  if (!JS_SetPrivate(cx, 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);
 }      
 
 /*
@@ -787,7 +932,7 @@
       (jsRootedValueInt);
   Tracer tracer("JsValueMoz._toString", jsRootedValue);
   jsval val = jsRootedValue->getValue();
-  JSContext* cx = jsRootedValue->getContext();
+  JSContext* cx = JsRootedValue::currentContext();
 
   // if it is a JavaScript object that has a toString member function
   // call that, otherwise call JS_ValueToString
diff --git a/jni/linux/LowLevelMoz.cpp b/jni/linux/LowLevelMoz.cpp
index 1711b20..b0ee1ed 100644
--- a/jni/linux/LowLevelMoz.cpp
+++ b/jni/linux/LowLevelMoz.cpp
@@ -48,9 +48,6 @@
 // include javah-generated header to make sure we match
 #include "LowLevelMoz.h"
 
-// TODO(jat) should be in a header
-extern nsCID kGwtExternalCID;
-
 JNIEnv* savedJNIEnv = 0;
 jclass lowLevelMozClass;
 
@@ -202,199 +199,13 @@
 }
 
 /*
- * 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);
-}
-
-/* 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)
-{
-  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) {
-    tracer.setFail("not a function type");
-    return JS_FALSE;
-  }
-  JSObject* funObj = JSVAL_TO_OBJECT(argv[-2]);
-
-  // Pull the wrapper object out of the funObj's reserved slot
-  jsval jsCleanupObj;
-  if (!JS_GetReservedSlot(cx, funObj, 0, &jsCleanupObj)) {
-    tracer.setFail("JS_GetReservedSlot failed");
-    return JS_FALSE;
-  }
-  JSObject* cleanupObj = JSVAL_TO_OBJECT(jsCleanupObj);
-  if (!cleanupObj) {
-    tracer.setFail("cleanupObj is null");
-    return JS_FALSE;
-  }
-
-  // Get DispatchMethod instance out of the wrapper object
-  jobject dispMeth =
-      NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, cleanupObj));
-  if (!dispMeth) {
-    tracer.setFail("dispMeth is null");
-    return JS_FALSE;
-  }
-  jclass dispClass = savedJNIEnv->GetObjectClass(dispMeth);
-  if (!dispClass || savedJNIEnv->ExceptionCheck()) {
-      tracer.setFail("GetObjectClass returns null");
-      return JS_FALSE;
-  }
-
-  // lookup the invoke method on the dispatch object
-  jmethodID invokeID =
-      savedJNIEnv->GetMethodID(dispClass, "invoke", "(II[II)V");
-  if (!invokeID || savedJNIEnv->ExceptionCheck()) {
-    tracer.setFail("GetMethodID failed");
-    return JS_FALSE;
-  }
-
-  // create an array of integers to hold the JsRootedValue pointers passed
-  // to the invoke method
-  jintArray args = savedJNIEnv->NewIntArray(argc);
-  if (!args || savedJNIEnv->ExceptionCheck()) {
-    tracer.setFail("NewIntArray failed");
-    return JS_FALSE;
-  }
-
-  // these arguments are already rooted by the JS interpreter, but we
-  // can't easily take advantage of that without complicating the JsRootedValue
-  // interface.
-  
-  // argv[-1] is OBJECT_TO_JSVAL(this)
-  JsRootedValue* jsThis = new JsRootedValue(cx, argv[-1]);
-  tracer.log("jsthis=%08x, RV=%08x", unsigned(argv[-1]), unsigned(jsThis));
-
-  // create JsRootedValues for arguments  
-  JsRootedValue *jsArgs[argc]; 
-  for (uintN i = 0; i < argc; ++i) {
-    jsArgs[i] = new JsRootedValue(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);
-
-  // 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;
-  }
-
-  // extract return value
-  *rval = jsReturnVal->getValue();
-
-#if 0
-  // NOTE: C++ objects are not cleaned up here because Java now owns them.
-  // TODO(jat): if reference-counted, they *do* need to be Released here.
-  
-  // free JsRootedValues
-  for (uintN i = 0; i < argc; ++i) {
-    delete jsArgs[i];
-  }
-  delete jsThis;
-  delete jsReturnVal;
-#endif
-
-  return returnValue;
-}
-
-/*
- * 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
- */
-JSBool getJavaPropertyStats(JSContext *cx, JSObject *obj, jsval id,
-    jclass& dispClass, jobject& dispObj, jstring& jident)
-{
-  Tracer tracer("getJavaPropertyStats");
-  if (!JSVAL_IS_STRING(id)) {
-    tracer.setFail("id is not a string");
-    return JS_FALSE;
-  }
-
-  jident = savedJNIEnv->NewString(JS_GetStringChars(JSVAL_TO_STRING(id)),
-      JS_GetStringLength(JSVAL_TO_STRING(id)));
-  if (!jident || savedJNIEnv->ExceptionCheck()) {
-    tracer.setFail("unable to create Java string");
-    return JS_FALSE;
-  }
-
-  dispObj = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
-  if (!dispObj) {
-    tracer.setFail("can't get dispatch object");
-    return JS_FALSE;
-  }
-
-  dispClass = savedJNIEnv->GetObjectClass(dispObj);
-  if (savedJNIEnv->ExceptionCheck()) {
-    tracer.setFail("can't get class of dispatch object");
-    return JS_FALSE;
-  }
-
-  return JS_TRUE;
-}
-
-/*
  * Class:     com_google_gwt_dev_shell_moz_LowLevelMoz
  * Method:    _executeScriptWithInfo
  * Signature: (ILjava/lang/String;Ljava/lang/String;I)Z
  */
 extern "C" JNIEXPORT jboolean JNICALL
 Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1executeScriptWithInfo
-    (JNIEnv* env, jclass llClass, jint scriptObject, jstring code,
+    (JNIEnv* env, jclass llClass, jint scriptObjectInt, jstring code,
      jstring file, jint line)
 {
   Tracer tracer("LowLevelMoz._executeScriptWithInfo");
@@ -409,18 +220,20 @@
     return JNI_FALSE;
   }
   tracer.log("code=%s, file=%s, line=%d", jcode.str(), jfile.str(), line);
+  JSContext* cx = JsRootedValue::currentContext();
+  nsCOMPtr<nsIScriptContext> scriptContext(GetScriptContextFromJSContext(cx));
 
-  nsIScriptGlobalObject* globalObject =
-      NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObject);
-  nsCOMPtr<nsIScriptContext> scriptContext(globalObject->GetContext());
+  nsIScriptGlobalObject* scriptObject =
+      NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjectInt);
+  JSObject* scriptWindow =
+      reinterpret_cast<JSObject*>(scriptObject->GetGlobalJSObject());
   nsXPIDLString scriptString;
   scriptString = jcode.jstr();
 
   nsXPIDLString aRetValue;
   PRBool aIsUndefined;
-  if (NS_FAILED(scriptContext->EvaluateString(scriptString,
-      globalObject->GetGlobalJSObject(), 0, jfile.str(), line, 0,
-      aRetValue, &aIsUndefined))) {
+  if (NS_FAILED(scriptContext->EvaluateString(scriptString, scriptWindow, 0,
+      jfile.str(), line, 0, aRetValue, &aIsUndefined))) {
     tracer.setFail("EvaluateString failed");
     return JNI_FALSE;
   }
@@ -448,16 +261,9 @@
   jint jsArgc = env->GetArrayLength(jsArgsInt);
   tracer.log("method=%s, jsthis=%08x, #args=%d", methodStr.str(), jsThisInt,
      jsArgc);
-
+  JSContext* cx = JsRootedValue::currentContext();
   nsIScriptGlobalObject* scriptObject =
       NS_REINTERPRET_CAST(nsIScriptGlobalObject*, scriptObjInt);
-  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());
 
@@ -519,15 +325,14 @@
 /*
  * Class:     com_google_gwt_dev_shell_moz_LowLevelMoz
  * Method:    _raiseJavaScriptException
- * Signature: (I)Z
+ * Signature: ()Z
  */
 extern "C" JNIEXPORT jboolean JNICALL
 Java_com_google_gwt_dev_shell_moz_LowLevelMoz__1raiseJavaScriptException
-    (JNIEnv* env, jclass, jint jscontext)
+    (JNIEnv* env, jclass)
 {
   Tracer tracer("LowLevelMoz._raiseJavaScriptException");
-  JSContext* cx = reinterpret_cast<JSContext*>(jscontext);
-  JS_SetPendingException(cx, JSVAL_NULL);
+  JS_SetPendingException(JsRootedValue::currentContext(), JSVAL_NULL);
   return JNI_TRUE;
 }
 
diff --git a/jni/linux/NativeWrapper.cpp b/jni/linux/NativeWrapper.cpp
index 26014e2..be3963e 100644
--- a/jni/linux/NativeWrapper.cpp
+++ b/jni/linux/NativeWrapper.cpp
@@ -24,16 +24,120 @@
 #include "gwt-jni.h"
 #include "Tracer.h"
 
-extern "C" {
+/*
+ * Helper function to get reference Java attributes from Javascript.
+ * 
+ * cx - JSContext pointer
+ * obj - JavaScript object which is a wrapped Java object
+ * id - property name, as a jsval string
+ * dispClass - output parameter of DispatchMethod subclass
+ * dispObj - output parameter of Java object
+ * jident - output parameter of property name as a Java string
+ */
+static JSBool getJavaPropertyStats(JSContext *cx, JSObject *obj, jsval id,
+    jclass& dispClass, jobject& dispObj, jstring& jident)
+{
+  Tracer tracer("getJavaPropertyStats");
+  if (!JSVAL_IS_STRING(id)) {
+    tracer.setFail("id is not a string");
+    return JS_FALSE;
+  }
+
+  jident = savedJNIEnv->NewString(JS_GetStringChars(JSVAL_TO_STRING(id)),
+      JS_GetStringLength(JSVAL_TO_STRING(id)));
+  if (!jident || savedJNIEnv->ExceptionCheck()) {
+    tracer.setFail("unable to create Java string");
+    return JS_FALSE;
+  }
+
+  dispObj = NS_REINTERPRET_CAST(jobject, JS_GetPrivate(cx, obj));
+  if (!dispObj) {
+    tracer.setFail("can't get dispatch object");
+    return JS_FALSE;
+  }
+
+  dispClass = savedJNIEnv->GetObjectClass(dispObj);
+  if (savedJNIEnv->ExceptionCheck()) {
+    tracer.setFail("can't get class of dispatch object");
+    return JS_FALSE;
+  }
+
+  return JS_TRUE;
+}
   
+/*
+ * Returns the value of a field on a Java object.
+ * 
+ * context - JavaScript context
+ * clazz - class of obj
+ * obj - Java object to retreive field from
+ * fieldName - name of field on Java object to retrieve
+ * 
+ * Returns null on failure.  Caller is responsible for deleting
+ * returned JsRootedValue when done with it.
+ */
+static JsRootedValue* GetFieldAsRootedValue(JSContext* cx, jclass clazz,
+    jobject obj, jstring fieldName)
+{
+  Tracer tracer("GetFieldAsRootedValue");
+  JsRootedValue::ContextManager context(cx);
+  jmethodID getFieldMeth = savedJNIEnv->GetMethodID(clazz, "getField",
+      "(Ljava/lang/String;I)V");
+  if (!getFieldMeth || savedJNIEnv->ExceptionCheck()) {
+    return 0;
+  }
+
+  JsRootedValue* jsRootedValue = new JsRootedValue();
+  savedJNIEnv->CallVoidMethod(obj, getFieldMeth, fieldName,
+    reinterpret_cast<jint>(jsRootedValue));
+  if (savedJNIEnv->ExceptionCheck()) {
+    delete jsRootedValue;
+    return 0;
+  }
+  return jsRootedValue;
+}
+  
+/*
+ * Sets the value of a field on a Java object.
+ * 
+ * context - JavaScript context
+ * clazz - class of obj
+ * obj - Java object to store into field
+ * fieldName - name of field on Java object to store into
+ * jsRootedValue - the value to store in the field
+ * 
+ * returns true on success, false on failure
+ */
+static bool SetFieldFromRootedValue(JSContext* cx, jclass clazz,
+    jobject obj, jstring fieldName, JsRootedValue* jsRootedValue)
+{
+  Tracer tracer("SetFieldAsRootedValue");
+  JsRootedValue::ContextManager context(cx);
+  jmethodID getFieldMeth = savedJNIEnv->GetMethodID(clazz, "setField",
+      "(Ljava/lang/String;I)V");
+  if (!getFieldMeth || savedJNIEnv->ExceptionCheck()) {
+    return false;
+  }
+
+  savedJNIEnv->CallVoidMethod(obj, getFieldMeth, fieldName,
+    reinterpret_cast<jint>(jsRootedValue));
+  if (savedJNIEnv->ExceptionCheck()) {
+    return false;
+  }
+
+  return true;
+}
+
 static JSBool JS_DLL_CALLBACK gwt_nativewrapper_getProperty(JSContext *cx,
     JSObject *obj, jsval id, jsval *vp)
 {
   Tracer tracer("gwt_nativewrapper_getProperty");
   tracer.log("context=%08x", unsigned(cx));
-
-  if (*vp != JSVAL_VOID)
+  if (*vp != JSVAL_VOID) {
     return JS_TRUE;
+  }
+  JsRootedValue::ContextManager context(cx);
+
   jclass dispClass;
   jobject dispObj;
   jstring ident;
@@ -63,22 +167,23 @@
 {
   Tracer tracer("gwt_nativewrapper_setProperty");
   tracer.log("context=%08x", unsigned(cx));
+  JsRootedValue::ContextManager context(cx);
   jclass dispClass;
   jobject dispObj;
   jstring ident;
-  if (!getJavaPropertyStats(cx,obj,id,dispClass,dispObj,ident)) {
+  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)) {
+  JsRootedValue* js_rooted_value = new JsRootedValue(*vp); 
+  if (!SetFieldFromRootedValue(cx, dispClass, dispObj, ident,
+      js_rooted_value)) {
     tracer.setFail("can't set field");
     return JS_FALSE;
   }
   return JS_TRUE;
 }
 
-
 JSClass gwt_nativewrapper_class = {
   "gwt_nativewrapper_class", JSCLASS_HAS_PRIVATE,
   JS_PropertyStub, JS_PropertyStub, gwt_nativewrapper_getProperty,
@@ -93,5 +198,3 @@
   JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, gwt_nativewrapper_finalize,
   JSCLASS_NO_OPTIONAL_MEMBERS
 };
-
-} // extern "C"
diff --git a/jni/linux/gwt-jni.h b/jni/linux/gwt-jni.h
index 1b19019..c477f1c 100644
--- a/jni/linux/gwt-jni.h
+++ b/jni/linux/gwt-jni.h
@@ -23,20 +23,10 @@
 extern JNIEnv* savedJNIEnv;
 extern jclass lowLevelMozClass;
 
-// JavaScript class objects
-extern "C" JSClass gwt_nativewrapper_class;
-extern "C" JSClass gwt_functionwrapper_class;
+extern nsCID kGwtExternalCID;
 
-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);
-    
+// JavaScript class objects
+extern JSClass gwt_nativewrapper_class;
+extern JSClass gwt_functionwrapper_class;
+
 #endif /*JNI_LINUX_GWT_JNI_H_*/
diff --git a/jni/linux/prebuilt/libgwt-ll.so b/jni/linux/prebuilt/libgwt-ll.so
index 40622ed..48351ee 100755
--- a/jni/linux/prebuilt/libgwt-ll.so
+++ b/jni/linux/prebuilt/libgwt-ll.so
Binary files differ