Fix for issue #1062; allows checked exceptions declared a JSNI method signature to actually be thrown in hosted mode as the correct type.

Review by: jat

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1054 8db76d5a-ed1c-0410-87a9-c151d255dfc7
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 76c2ba4..272974e 100644
--- a/dev/core/src/com/google/gwt/dev/shell/JavaScriptHost.java
+++ b/dev/core/src/com/google/gwt/dev/shell/JavaScriptHost.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
@@ -45,7 +45,7 @@
    * Invoke a native JavaScript function that returns a boolean value.
    */
   public static boolean invokeNativeBoolean(String name, Object jthis,
-      Class[] types, Object[] args) {
+      Class[] types, Object[] args) throws Throwable {
     return sHost.invokeNativeBoolean(name, jthis, types, args);
   }
 
@@ -53,7 +53,7 @@
    * Invoke a native JavaScript function that returns a byte value.
    */
   public static byte invokeNativeByte(String name, Object jthis, Class[] types,
-      Object[] args) {
+      Object[] args) throws Throwable {
     return sHost.invokeNativeByte(name, jthis, types, args);
   }
 
@@ -61,7 +61,7 @@
    * Invoke a native JavaScript function that returns a character value.
    */
   public static char invokeNativeChar(String name, Object jthis, Class[] types,
-      Object[] args) {
+      Object[] args) throws Throwable {
     return sHost.invokeNativeChar(name, jthis, types, args);
   }
 
@@ -69,7 +69,7 @@
    * Invoke a native JavaScript function that returns a double value.
    */
   public static double invokeNativeDouble(String name, Object jthis,
-      Class[] types, Object[] args) {
+      Class[] types, Object[] args) throws Throwable {
     return sHost.invokeNativeDouble(name, jthis, types, args);
   }
 
@@ -77,7 +77,7 @@
    * Invoke a native JavaScript function that returns a float value.
    */
   public static float invokeNativeFloat(String name, Object jthis,
-      Class[] types, Object[] args) {
+      Class[] types, Object[] args) throws Throwable {
     return sHost.invokeNativeFloat(name, jthis, types, args);
   }
 
@@ -85,7 +85,7 @@
    * Invoke a native JavaScript function that returns a handle value.
    */
   public static Object invokeNativeHandle(String name, Object jthis,
-      Class returnType, Class[] types, Object[] args) {
+      Class returnType, Class[] types, Object[] args) throws Throwable {
     return sHost.invokeNativeHandle(name, jthis, returnType, types, args);
   }
 
@@ -93,7 +93,7 @@
    * Invoke a native JavaScript function that returns an integer value.
    */
   public static int invokeNativeInt(String name, Object jthis, Class[] types,
-      Object[] args) {
+      Object[] args) throws Throwable {
     return sHost.invokeNativeInt(name, jthis, types, args);
   }
 
@@ -101,7 +101,7 @@
    * Invoke a native JavaScript function that returns a long value.
    */
   public static long invokeNativeLong(String name, Object jthis, Class[] types,
-      Object[] args) {
+      Object[] args) throws Throwable {
     return sHost.invokeNativeLong(name, jthis, types, args);
   }
 
@@ -109,7 +109,7 @@
    * Invoke a native JavaScript function that returns an object value.
    */
   public static Object invokeNativeObject(String name, Object jthis,
-      Class[] types, Object[] args) {
+      Class[] types, Object[] args) throws Throwable {
     return sHost.invokeNativeObject(name, jthis, types, args);
   }
 
@@ -117,7 +117,7 @@
    * Invoke a native JavaScript function that returns a short value.
    */
   public static short invokeNativeShort(String name, Object jthis,
-      Class[] types, Object[] args) {
+      Class[] types, Object[] args) throws Throwable {
     return sHost.invokeNativeShort(name, jthis, types, args);
   }
 
@@ -125,7 +125,7 @@
    * Invoke a native JavaScript function that returns a string value.
    */
   public static String invokeNativeString(String name, Object jthis,
-      Class[] types, Object[] args) {
+      Class[] types, Object[] args) throws Throwable {
     return sHost.invokeNativeString(name, jthis, types, args);
   }
 
@@ -133,7 +133,7 @@
    * Invoke a native JavaScript function that returns no value.
    */
   public static void invokeNativeVoid(String name, Object jthis, Class[] types,
-      Object[] args) {
+      Object[] args) throws Throwable {
     sHost.invokeNativeVoid(name, jthis, types, args);
   }
 
@@ -153,7 +153,7 @@
       return sHost.rebindAndCreate(className);
     } catch (Throwable e) {
       String msg = "Deferred binding failed for '" + className
-        + "' (did you forget to inherit a required module?)";
+          + "' (did you forget to inherit a required module?)";
       throw new RuntimeException(msg, e);
     }
   }
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 1774936..a9c43e7 100644
--- a/dev/core/src/com/google/gwt/dev/shell/JsniInjector.java
+++ b/dev/core/src/com/google/gwt/dev/shell/JsniInjector.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
@@ -233,6 +233,8 @@
         false, false);
 
     sb.append(methodDecl + " {");
+    // wrap the call in a try-catch block
+    sb.append("try {");
 
     // Write the Java call to the property invoke method, adding
     // downcasts where necessary.
@@ -303,7 +305,21 @@
     // parameters.
     //
     sb.append(Jsni.buildArgList(method));
-    sb.append(");}");
+    sb.append(");");
+
+    // Catch exceptions; rethrow if the exception is RTE or declared.
+    sb.append("} catch (java.lang.Throwable __gwt_exception) {");
+    sb.append("if (__gwt_exception instanceof java.lang.RuntimeException) throw (java.lang.RuntimeException) __gwt_exception;");
+    JType[] throwTypes = method.getThrows();
+    for (int i = 0; i < throwTypes.length; ++i) {
+      String typeName = throwTypes[i].getQualifiedSourceName();
+      sb.append("if (__gwt_exception instanceof " + typeName + ") throw (" + typeName
+          + ") __gwt_exception;");
+    }
+    sb.append("throw new java.lang.RuntimeException(\"Undeclared checked exception thrown out of JavaScript; web mode behavior may differ.\", __gwt_exception);");
+    sb.append("}");
+
+    sb.append("}");
 
     // Add extra lines at the end to match JSNI body.
     //
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 bd991a1..c987314 100644
--- a/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java
+++ b/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.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
@@ -30,25 +30,25 @@
  */
 public abstract class ModuleSpace implements ShellJavaScriptHost {
 
-  protected static ThreadLocal sCaughtJavaExceptionObject = new ThreadLocal();
+  private static ThreadLocal sCaughtJavaExceptionObject = new ThreadLocal();
 
-  protected static ThreadLocal sLastThrownJavaException = new ThreadLocal();
+  private static ThreadLocal sLastThrownJavaException = new ThreadLocal();
 
-  protected static ThreadLocal sThrownJavaExceptionObject = new ThreadLocal();
+  private static ThreadLocal sThrownJavaExceptionObject = new ThreadLocal();
 
   /**
    * Logger is thread local.
    */
   private static ThreadLocal threadLocalLogger = new ThreadLocal();
 
-  public static void setThrownJavaException(RuntimeException re) {
-    RuntimeException was = (RuntimeException) sLastThrownJavaException.get();
-    if (was != re) {
+  public static void setThrownJavaException(Throwable t) {
+    Throwable was = (Throwable) sLastThrownJavaException.get();
+    if (was != t) {
       // avoid logging the same exception twice
-      getLogger().log(TreeLogger.WARN, "Exception thrown into JavaScript", re);
-      sLastThrownJavaException.set(re);
+      getLogger().log(TreeLogger.WARN, "Exception thrown into JavaScript", t);
+      sLastThrownJavaException.set(t);
     }
-    sThrownJavaExceptionObject.set(re);
+    sThrownJavaExceptionObject.set(t);
   }
 
   protected static RuntimeException createJavaScriptException(ClassLoader cl,
@@ -145,6 +145,22 @@
     host.getClassLoader().clear();
   }
 
+  public void exceptionCaught(int number, String name, String message) {
+    Throwable thrown = (Throwable) sThrownJavaExceptionObject.get();
+
+    if (thrown != null) {
+      // See if the caught exception was thrown by us
+      if (isExceptionSame(thrown, number, name, message)) {
+        sCaughtJavaExceptionObject.set(thrown);
+        sThrownJavaExceptionObject.set(null);
+        return;
+      }
+    }
+
+    sCaughtJavaExceptionObject.set(createJavaScriptException(
+        getIsolatedClassLoader(), name, message));
+  }
+  
   /**
    * Get the unique key for this module.
    * 
@@ -164,7 +180,7 @@
   }
 
   public boolean invokeNativeBoolean(String name, Object jthis, Class[] types,
-      Object[] args) {
+      Object[] args) throws Throwable {
     JsValue result = invokeNative(name, jthis, types, args);
     Boolean value = (Boolean) JsValueGlue.get(result, Boolean.class,
         "invokeNativeBoolean(" + name + ")");
@@ -172,7 +188,7 @@
   }
 
   public byte invokeNativeByte(String name, Object jthis, Class[] types,
-      Object[] args) {
+      Object[] args) throws Throwable {
     JsValue result = invokeNative(name, jthis, types, args);
     Byte value = (Byte) JsValueGlue.get(result, Byte.class, "invokeNativeByte("
         + name + ")");
@@ -180,7 +196,7 @@
   }
 
   public char invokeNativeChar(String name, Object jthis, Class[] types,
-      Object[] args) {
+      Object[] args) throws Throwable {
     JsValue result = invokeNative(name, jthis, types, args);
     Character value = (Character) JsValueGlue.get(result, Character.class,
         "invokeNativeCharacter(" + name + ")");
@@ -188,7 +204,7 @@
   }
 
   public double invokeNativeDouble(String name, Object jthis, Class[] types,
-      Object[] args) {
+      Object[] args) throws Throwable {
     JsValue result = invokeNative(name, jthis, types, args);
     Double value = (Double) JsValueGlue.get(result, Double.class,
         "invokeNativeDouble(" + name + ")");
@@ -196,7 +212,7 @@
   }
 
   public float invokeNativeFloat(String name, Object jthis, Class[] types,
-      Object[] args) {
+      Object[] args) throws Throwable {
     JsValue result = invokeNative(name, jthis, types, args);
     Float value = (Float) JsValueGlue.get(result, Float.class,
         "invokeNativeFloat(" + name + ")");
@@ -204,7 +220,7 @@
   }
 
   public Object invokeNativeHandle(String name, Object jthis, Class returnType,
-      Class[] types, Object[] args) {
+      Class[] types, Object[] args) throws Throwable {
 
     JsValue result = invokeNative(name, jthis, types, args);
     return JsValueGlue.get(result, returnType, "invokeNativeHandle(" + name
@@ -212,7 +228,7 @@
   }
 
   public int invokeNativeInt(String name, Object jthis, Class[] types,
-      Object[] args) {
+      Object[] args) throws Throwable {
     JsValue result = invokeNative(name, jthis, types, args);
     Integer value = (Integer) JsValueGlue.get(result, Integer.class,
         "invokeNativeInteger(" + name + ")");
@@ -220,7 +236,7 @@
   }
 
   public long invokeNativeLong(String name, Object jthis, Class[] types,
-      Object[] args) {
+      Object[] args) throws Throwable {
     JsValue result = invokeNative(name, jthis, types, args);
     Long value = (Long) JsValueGlue.get(result, Long.class, "invokeNativeLong("
         + name + ")");
@@ -228,14 +244,14 @@
   }
 
   public Object invokeNativeObject(String name, Object jthis, Class[] types,
-      Object[] args) {
+      Object[] args) throws Throwable {
     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) {
+      Object[] args) throws Throwable {
     JsValue result = invokeNative(name, jthis, types, args);
     Short value = (Short) JsValueGlue.get(result, Short.class,
         "invokeNativeShort(" + name + ")");
@@ -243,14 +259,14 @@
   }
 
   public String invokeNativeString(String name, Object jthis, Class[] types,
-      Object[] args) {
+      Object[] args) throws Throwable {
     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) {
+      Object[] args) throws Throwable {
     JsValue result = invokeNative(name, jthis, types, args);
     if (!result.isUndefined()) {
       getLogger().log(
@@ -287,7 +303,16 @@
 
     // Make sure we can resolve JSNI references to static Java names.
     //
-    initializeStaticDispatcher();
+    try {
+      Object staticDispatch = getStaticDispatcher();
+      createNative("initializeStaticDispatcher", 0, "__defineStatic",
+          new String[] {"__arg0"}, "window.__static = __arg0;");
+      invokeNativeVoid("__defineStatic", null, new Class[] {Object.class},
+          new Object[] {staticDispatch});
+    } catch (Throwable e) {
+      logger.log(TreeLogger.ERROR, "Unable to initialize static dispatcher", e);
+      throw new UnableToCompleteException();
+    }
 
     // Actually run user code.
     //
@@ -416,7 +441,7 @@
    * @return the return value as a Variant.
    */
   protected abstract JsValue doInvoke(String name, Object jthis, Class[] types,
-      Object[] args);
+      Object[] args) throws Throwable;
 
   protected CompilingClassLoader getIsolatedClassLoader() {
     return host.getClassLoader();
@@ -425,7 +450,7 @@
   /**
    * Injects the magic needed to resolve JSNI references from module-space.
    */
-  protected abstract void initializeStaticDispatcher();
+  protected abstract Object getStaticDispatcher();
 
   /**
    * Invokes a native JavaScript function.
@@ -437,14 +462,31 @@
    * @return the return value as a Variant.
    */
   protected final JsValue invokeNative(String name, Object jthis,
-      Class[] types, Object[] args) {
+      Class[] types, Object[] args) throws Throwable {
     // Whenever a native method is invoked, release any enqueued cleanup objects
     JsValue.mainThreadCleanup();
-    return doInvoke(name, jthis, types, args);
+    JsValue result = doInvoke(name, jthis, types, args);
+    // Is an exception active?
+    Throwable thrown = (Throwable) sCaughtJavaExceptionObject.get();
+    if (thrown == null) {
+      return result;
+    }
+    sCaughtJavaExceptionObject.set(null);
+
+    /*
+     * The stack trace on the stored exception will not be very useful due to
+     * how it was created. Using fillInStackTrace() resets the stack trace to
+     * this moment in time, which is usually far more useful.
+     */
+    thrown.fillInStackTrace();
+    throw thrown;
   }
 
-  protected boolean isExceptionActive() {
-    return sCaughtJavaExceptionObject.get() != null;
+  protected boolean isExceptionSame(Throwable original, int number,
+      String name, String message) {
+    // For most platforms, the null exception means we threw it.
+    // IE overrides this.
+    return (name == null && message == null);
   }
 
   protected String rebind(String sourceName) throws UnableToCompleteException {
@@ -463,12 +505,6 @@
     }
   }
 
-  protected RuntimeException takeJavaException() {
-    RuntimeException re = (RuntimeException) sCaughtJavaExceptionObject.get();
-    sCaughtJavaExceptionObject.set(null);
-    return re;
-  }
-
   /**
    * Clear the module's JavaScriptHost 'host' field.
    */
diff --git a/dev/core/src/com/google/gwt/dev/shell/ModuleSpacePropertyOracle.java b/dev/core/src/com/google/gwt/dev/shell/ModuleSpacePropertyOracle.java
index 459b5f7..ce466d2 100644
--- a/dev/core/src/com/google/gwt/dev/shell/ModuleSpacePropertyOracle.java
+++ b/dev/core/src/com/google/gwt/dev/shell/ModuleSpacePropertyOracle.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
@@ -94,7 +94,7 @@
         //
         value = space.invokeNativeString("__gwt_getProperty", null,
             new Class[] {String.class}, new Object[] {prop.getName()});
-      } catch (RuntimeException e) {
+      } catch (Throwable e) {
         // Treat as an unknown value.
         //
         String msg = "Error while executing the JavaScript provider for property '"
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 858a1dc..3f049a6 100644
--- a/dev/core/src/com/google/gwt/dev/shell/ShellJavaScriptHost.java
+++ b/dev/core/src/com/google/gwt/dev/shell/ShellJavaScriptHost.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
@@ -48,73 +48,73 @@
    * Invoke a native JavaScript function that returns a boolean value.
    */
   abstract boolean invokeNativeBoolean(String name, Object jthis,
-      Class[] types, Object[] args);
+      Class[] types, Object[] args) throws Throwable;
 
   /**
    * Invoke a native JavaScript function that returns a byte value.
    */
   abstract byte invokeNativeByte(String name, Object jthis, Class[] types,
-      Object[] args);
+      Object[] args) throws Throwable;
 
   /**
    * Invoke a native JavaScript function that returns a character value.
    */
   abstract char invokeNativeChar(String name, Object jthis, Class[] types,
-      Object[] args);
+      Object[] args) throws Throwable;
 
   /**
    * Invoke a native JavaScript function that returns a double value.
    */
   abstract double invokeNativeDouble(String name, Object jthis, Class[] types,
-      Object[] args);
+      Object[] args) throws Throwable;
 
   /**
    * Invoke a native JavaScript function that returns a float value.
    */
   abstract float invokeNativeFloat(String name, Object jthis, Class[] types,
-      Object[] args);
+      Object[] args) throws Throwable;
 
   /**
    * Invoke a native JavaScript function that returns a handle value.
    */
   abstract Object invokeNativeHandle(String name, Object jthis,
-      Class returnType, Class[] types, Object[] args);
+      Class returnType, Class[] types, Object[] args) throws Throwable;
 
   /**
    * Invoke a native JavaScript function that returns an integer value.
    */
   abstract int invokeNativeInt(String name, Object jthis, Class[] types,
-      Object[] args);
+      Object[] args) throws Throwable;
 
   /**
    * Invoke a native JavaScript function that returns a long value.
    */
   abstract long invokeNativeLong(String name, Object jthis, Class[] types,
-      Object[] args);
+      Object[] args) throws Throwable;
 
   /**
    * Invoke a native JavaScript function that returns an object value.
    */
   abstract Object invokeNativeObject(String name, Object jthis, Class[] types,
-      Object[] args);
+      Object[] args) throws Throwable;
 
   /**
    * Invoke a native JavaScript function that returns a short value.
    */
   abstract short invokeNativeShort(String name, Object jthis, Class[] types,
-      Object[] args);
+      Object[] args) throws Throwable;
 
   /**
    * Invoke a native JavaScript function that returns a string value.
    */
   abstract String invokeNativeString(String name, Object jthis, Class[] types,
-      Object[] args);
+      Object[] args) throws Throwable;
 
   /**
    * Invoke a native JavaScript function that returns no value.
    */
   abstract void invokeNativeVoid(String name, Object jthis, Class[] types,
-      Object[] args);
+      Object[] args) throws Throwable;
 
   /**
    * Logs to the dev shell logger.
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 9ac0296..2297ee4 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
@@ -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
@@ -88,16 +88,9 @@
       // If we get here, it means an exception is being thrown from
       // Java back into JavaScript
       Throwable t = e.getTargetException();
-      RuntimeException re;
-      if (t instanceof RuntimeException) {
-        re = (RuntimeException) t;
-      } else {
-        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);
+      ModuleSpaceMoz.setThrownJavaException(t);
       LowLevelMoz.raiseJavaScriptException(jscontext);
     } catch (IllegalArgumentException e) {
       // TODO(jat): log to treelogger instead?  If so, how do I get to it?
@@ -111,4 +104,4 @@
       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 e326fb5..2b1fa7d 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
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Google Inc.
+ * Copyright 2007 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -20,7 +20,6 @@
 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;
 
 /**
  * An implementation of {@link com.google.gwt.dev.shell.ModuleSpace} for
@@ -28,8 +27,6 @@
  */
 public class ModuleSpaceMoz extends ModuleSpace {
 
-  private DispatchObject staticDispatch;
-
   private final int window;
 
   /**
@@ -45,8 +42,11 @@
     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)
+  /*
+   * (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) {
@@ -57,7 +57,9 @@
     LowLevelMoz.executeScriptWithInfo(window, newScript, file, line);
   }
 
-  /* (non-Javadoc)
+  /*
+   * (non-Javadoc)
+   * 
    * @see com.google.gwt.dev.shell.ModuleSpace#dispose()
    */
   public void dispose() {
@@ -65,25 +67,6 @@
     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();
-
-    // See if the caught exception is null (thus thrown by us)
-    if (thrown != null) {
-      if (name == null && message == null) {
-        sCaughtJavaExceptionObject.set(thrown);
-        sThrownJavaExceptionObject.set(null);
-        return;
-      }
-    }
-
-    sCaughtJavaExceptionObject.set(createJavaScriptException(
-        getIsolatedClassLoader(), name, message));
-  }
-
   /**
    * Invokes a native JavaScript function.
    * 
@@ -109,31 +92,12 @@
       jsArgsInt[i] = argv[i].getJsRootedValue();
     }
     JsValueMoz returnVal = JsValueMoz.createUndefinedValue(window);
-    LowLevelMoz.invoke(window, name, jsthis.getJsRootedValue(),
-        jsArgsInt, returnVal.getJsRootedValue());
-    
-    if (!isExceptionActive()) {
-      return returnVal;
-    }
-
-    /*
-     * The stack trace on the stored exception will not be very useful due to
-     * how it was created. Using fillInStackTrace() resets the stack trace to
-     * this moment in time, which is usually far more useful.
-     */
-    RuntimeException thrown = takeJavaException();
-    thrown.fillInStackTrace();
-    throw thrown;
+    LowLevelMoz.invoke(window, name, jsthis.getJsRootedValue(), jsArgsInt,
+        returnVal.getJsRootedValue());
+    return returnVal;
   }
 
-  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});
+  protected Object getStaticDispatcher() {
+    return new GeckoDispatchAdapter(getIsolatedClassLoader());
   }
 }
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 38106dd..87cf270 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
@@ -84,14 +84,7 @@
         // If we get here, it means an exception is being thrown from
         // Java back into JavaScript
         Throwable t = e.getTargetException();
-        RuntimeException re;
-        if (t instanceof RuntimeException) {
-          re = (RuntimeException) t;
-        } else {
-          re = new RuntimeException("Checked exception thrown into JavaScript"
-              + " (Web Mode behavior may differ)", t);
-        }
-        ModuleSpaceSaf.setThrownJavaException(re);
+        ModuleSpaceSaf.setThrownJavaException(t);
         LowLevelSaf.raiseJavaScriptException(execState, LowLevelSaf.jsNull());
         return LowLevelSaf.jsUndefined();
       }
@@ -99,4 +92,4 @@
       LowLevelSaf.popExecState(execState);
     }
   }
-}
\ No newline at end of file
+}
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 02371e6..70b4533 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
@@ -26,14 +26,12 @@
  */
 public class ModuleSpaceSaf extends ModuleSpace {
 
-  private DispatchObject staticDispatch;
-
   private final int window;
 
   /**
    * Constructs a browser interface for use with a global window object.
    * 
-   * @param moduleName name of the module 
+   * @param moduleName name of the module
    * @param key unique key for this instance of the module
    */
   public ModuleSpaceSaf(ModuleSpaceHost host, int scriptGlobalObject,
@@ -61,22 +59,6 @@
     super.dispose();
   }
 
-  public void exceptionCaught(int number, String name, String message) {
-    RuntimeException thrown = (RuntimeException) sThrownJavaExceptionObject.get();
-
-    // See if the caught exception is null (thus thrown by us)
-    if (thrown != null) {
-      if (name == null && message == null) {
-        sCaughtJavaExceptionObject.set(thrown);
-        sThrownJavaExceptionObject.set(null);
-        return;
-      }
-    }
-
-    sCaughtJavaExceptionObject.set(createJavaScriptException(
-        getIsolatedClassLoader(), name, message));
-  }
-
   /**
    * Invokes a native JavaScript function.
    * 
@@ -99,36 +81,18 @@
     }
 
     int result = LowLevelSaf.invoke(curExecState, window, name, jsthis, argv);
-    if (!isExceptionActive()) {
-      return new JsValueSaf(result);
-    }
+    return new JsValueSaf(result);
+}
 
-    /*
-     * The stack trace on the stored exception will not be very useful due to
-     * how it was created. Using fillInStackTrace() resets the stack trace to
-     * this moment in time, which is usually far more useful.
-     */
-    RuntimeException thrown = takeJavaException();
-    thrown.fillInStackTrace();
-    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 Object getStaticDispatcher() {
+    return new WebKitDispatchAdapter(getIsolatedClassLoader());
   }
 
   protected int wrapObjectAsJSObject(Object o) {
     if (o == null) {
       return LowLevelSaf.jsNull();
     }
-    
+
     DispatchObject dispObj;
     if (o instanceof DispatchObject) {
       dispObj = (DispatchObject) o;
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 38c8220..b66e86e 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
@@ -305,15 +305,8 @@
       // Java back into JavaScript
 
       Throwable t = e.getTargetException();
-      RuntimeException re;
-      if (t instanceof RuntimeException) {
-        re = (RuntimeException) t;
-      } else {
-        re = new RuntimeException("Checked exception thrown into JavaScript"
-            + " (web mode behavior may differ)", t);
-      }
-      ex = new HResultException(re);
-      ModuleSpace.setThrownJavaException(re);
+      ex = new HResultException(t);
+      ModuleSpace.setThrownJavaException(t);
     } catch (Exception e) {
       // Log to the console for detailed examination.
       //
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
index 404fb2a..f287bf5 100644
--- a/dev/windows/src/com/google/gwt/dev/shell/ie/JsValueIE6.java
+++ b/dev/windows/src/com/google/gwt/dev/shell/ie/JsValueIE6.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
@@ -404,12 +404,17 @@
    * @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) {
+  public void setWrappedJavaObject(CompilingClassLoader cl, Object val) {
+    IDispatchImpl dispObj;
+    if (val == null) {
       setNull();
       return;
+    } else if (val instanceof IDispatchImpl) {
+      dispObj = (IDispatchImpl)val;
+    } else {
+      dispObj = new IDispatchProxy(cl, val);
     }
-    IDispatch disp = new IDispatch(new IDispatchProxy(cl, obj).getAddress());
+    IDispatch disp = new IDispatch(dispObj.getAddress());
     disp.AddRef();
     setVariant(new Variant(disp));
   }
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 84cc999..9b47ce4 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
@@ -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
@@ -121,4 +121,4 @@
     }
     throw new HResultException(COM.E_NOTSUPPORTED);
   }
-}
\ No newline at end of file
+}
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 b24cf05..681d4f3 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
@@ -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
@@ -23,7 +23,6 @@
 import org.eclipse.swt.internal.ole.win32.IDispatch;
 import org.eclipse.swt.ole.win32.OleAutomation;
 import org.eclipse.swt.ole.win32.Variant;
-import org.eclipse.swt.widgets.Display;
 
 /**
  * An implementation of {@link com.google.gwt.dev.shell.ModuleSpace} for
@@ -31,14 +30,14 @@
  */
 public class ModuleSpaceIE6 extends ModuleSpace {
   /**
-   * Invoke a JavaScript function.  The static function exists to allow
+   * Invoke a JavaScript function. The static function exists to allow
    * platform-dependent code to make JavaScript calls without having a
    * ModuleSpaceIE6 (and all that entails) if it is not required.
    * 
    * @param window the window containing the function
    * @param name the name of the function
-   * @param vArgs the array of arguments.  vArgs[0] is the this parameter
-   *     supplied to the function, which must be null if it is static.
+   * @param vArgs the array of arguments. vArgs[0] is the this parameter
+   *          supplied to the function, which must be null if it is static.
    * @return the return value of the JavaScript function
    */
   protected static Variant doInvokeOnWindow(OleAutomation window, String name,
@@ -78,19 +77,15 @@
   private static int CODE(int hresult) {
     return hresult & 0xFFFF;
   }
-
   // CHECKSTYLE_ON
 
-  private Variant staticDispatch;
-
-  private IDispatchProxy staticDispatchProxy;
-
   private final OleAutomation window;
 
   /**
    * Constructs a browser interface for use with an IE6 'window' automation
    * object.
-   * @param moduleName 
+   * 
+   * @param moduleName
    */
   public ModuleSpaceIE6(ModuleSpaceHost host, IDispatch scriptFrameWindow,
       String moduleName, Object key) {
@@ -119,30 +114,6 @@
   }
 
   public void dispose() {
-    /*
-     * Dispose the static dispatcher. This should be simple and straightforward,
-     * but isn't, because IE (especially 7) appears to over-Release() the static
-     * dispatcher on unload (less often when shutting down, but occasionally
-     * then as well). Because this occurs *after* the window unload event, we
-     * intentionally use Display.asyncExec() to defer it until after the browser
-     * is done cleaning up. We then dispose() the static dispatcher only if it
-     * has not already been disposed().
-     */
-    if (staticDispatch != null) {
-      final Variant staticDispatchToDispose = staticDispatch;
-      staticDispatch = null;
-
-      Display.getCurrent().asyncExec(new Runnable() {
-        public void run() {
-          // If the proxy has already been disposed, don't try to do so again,
-          // as this will attempt to call through a null vtable.
-          if (!staticDispatchProxy.isDisposed()) {
-            staticDispatchToDispose.dispose();
-          }
-        }
-      });
-    }
-
     // Dispose everything else.
     if (window != null) {
       window.dispose();
@@ -150,24 +121,6 @@
     super.dispose();
   }
 
-  public void exceptionCaught(int number, String name, String message) {
-    RuntimeException thrown = (RuntimeException) sThrownJavaExceptionObject.get();
-
-    // See if the caught exception matches the thrown exception
-    if (thrown != null) {
-      HResultException hre = new HResultException(thrown);
-      if (CODE(hre.getHResult()) == CODE(number)
-          && hre.getMessage().equals(message)) {
-        sCaughtJavaExceptionObject.set(thrown);
-        sThrownJavaExceptionObject.set(null);
-        return;
-      }
-    }
-
-    sCaughtJavaExceptionObject.set(createJavaScriptException(
-        getIsolatedClassLoader(), name, message));
-  }
-
   /**
    * Invokes a native javascript function.
    * 
@@ -178,7 +131,7 @@
    * @return the return value as a Variant.
    */
   protected JsValue doInvoke(String name, Object jthis, Class[] types,
-      Object[] args) {
+      Object[] args) throws Throwable {
     Variant[] vArgs = null;
     try {
       // Build the argument list, including 'jthis'.
@@ -196,23 +149,12 @@
 
       Variant result = doInvokeOnWindow(window, name, vArgs);
       try {
-        if (!isExceptionActive()) {
-          return new JsValueIE6(result);
-        }
+        return new JsValueIE6(result);
       } finally {
         if (result != null) {
           result.dispose();
         }
       }
-
-      /*
-       * The stack trace on the stored exception will not be very useful due to
-       * how it was created. Using fillInStackTrace() resets the stack trace to
-       * this moment in time, which is usually far more useful.
-       */
-      RuntimeException thrown = takeJavaException();
-      thrown.fillInStackTrace();
-      throw thrown;
     } finally {
       // We allocated variants for all arguments, so we must dispose them all.
       //
@@ -223,19 +165,14 @@
       }
     }
   }
+  
+  protected Object getStaticDispatcher() {
+    return new IDispatchProxy(getIsolatedClassLoader());
+  }
 
-  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});
+  protected boolean isExceptionSame(Throwable original, int number, String name, String message) {
+    HResultException hre = new HResultException(original);
+    return CODE(hre.getHResult()) == CODE(number) && hre.getMessage().equals(message);
   }
 
   private Variant execute(String code) {