Merge:
  changes/scottb/jso -r1776:1933
Into:
  trunk@1933

Review by: spoon



git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1934 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/build.xml b/dev/core/build.xml
index ae6e997..d95c206 100755
--- a/dev/core/build.xml
+++ b/dev/core/build.xml
@@ -26,6 +26,8 @@
 			<zipfileset src="${gwt.tools.lib}/apache/tapestry-util-text-4.0.2.jar" />
 			<zipfileset src="${gwt.tools.lib}/apache/ant-1.6.5.jar" />
 			<zipfileset src="${gwt.tools.lib}/eclipse/jdt-3.3.1.jar" />
+			<zipfileset src="${gwt.tools.lib}/objectweb/asm-3.1.jar" />
+			<zipfileset src="${gwt.tools.lib}/objectweb/asm-commons-3.1.jar" />
 			<zipfileset src="${gwt.tools.lib}/tomcat/ant-launcher-1.6.5.jar" />
 			<zipfileset src="${gwt.tools.lib}/tomcat/catalina-1.0.jar" />
 			<zipfileset src="${gwt.tools.lib}/tomcat/catalina-optional-1.0.jar" />
diff --git a/dev/core/src/com/google/gwt/dev/jdt/AbstractCompiler.java b/dev/core/src/com/google/gwt/dev/jdt/AbstractCompiler.java
index ea8406e..ea85b1e 100644
--- a/dev/core/src/com/google/gwt/dev/jdt/AbstractCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jdt/AbstractCompiler.java
@@ -177,6 +177,8 @@
         resolvePossiblyNestedType(typeName);
       }
 
+      JSORestrictionsChecker.check(cud);
+
       // Optionally remember this cud.
       //
       if (cuds != null) {
diff --git a/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java b/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java
index 5c1fe57..03be599 100644
--- a/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jdt/FindDeferredBindingSitesVisitor.java
@@ -68,7 +68,7 @@
         messageSend.sourceStart());
     DefaultProblem problem = new DefaultProblem(compResult.fileName, message,
         IProblem.ExternalProblemNotFixable, null, ProblemSeverities.Error,
-        messageSend.sourceStart, messageSend.sourceEnd, startLine, startColumn);
+        messageSend.sourceStart(), messageSend.sourceEnd(), startLine, startColumn);
     compResult.record(problem, scope.referenceContext());
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jdt/JSORestrictionsChecker.java b/dev/core/src/com/google/gwt/dev/jdt/JSORestrictionsChecker.java
new file mode 100644
index 0000000..c560db8
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jdt/JSORestrictionsChecker.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2008 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.jdt;
+
+import com.google.gwt.dev.shell.JsValueGlue;
+
+import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.internal.compiler.ASTVisitor;
+import org.eclipse.jdt.internal.compiler.CompilationResult;
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
+import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
+import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
+import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
+import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
+import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.Scope;
+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.problem.DefaultProblem;
+import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
+import org.eclipse.jdt.internal.compiler.util.Util;
+
+/**
+ * Check a compilation unit for violations of
+ * {@link com.google.gwt.core.client.JavaScriptObject JavaScriptObject} (JSO)
+ * restrictions. The restrictions are:
+ * 
+ * <ul>
+ * <li> All instance methods on JSO classes must be explicitly marked final.
+ * <li> JSO classes cannot implement interfaces that define methods.
+ * <li> No instance methods on JSO classes may override another method. (This
+ * catches accidents where JSO itself did not finalize some method from its
+ * superclass.)
+ * <li> JSO classes cannot have instance fields.
+ * <li> "new" operations cannot be used with JSO classes.
+ * <li> Every JSO class must have precisely one constructor, and it must be
+ * protected, empty, and no-argument.
+ * <li> Nested JSO classes must be static.
+ * </ul>
+ * 
+ * Any violations found are attached as errors on the
+ * CompilationUnitDeclaration.
+ */
+class JSORestrictionsChecker {
+
+  private class JSORestrictionsVisitor extends ASTVisitor implements
+      ClassFileConstants {
+
+    @Override
+    public void endVisit(AllocationExpression exp, BlockScope scope) {
+      if (exp.type != null && isJSOSubclass(exp.type.resolveType(scope))) {
+        errorOn(exp, ERR_NEW_JSO);
+      }
+    }
+
+    @Override
+    public void endVisit(ConstructorDeclaration meth, ClassScope scope) {
+      if (isForJSOSubclass(scope)) {
+        if ((meth.arguments != null) && (meth.arguments.length > 0)) {
+          errorOn(meth, ERR_CONSTRUCTOR_WITH_PARAMETERS);
+        }
+        if ((meth.modifiers & AccProtected) == 0) {
+          errorOn(meth, ERR_NONPROTECTED_CONSTRUCTOR);
+        }
+        if (meth.statements != null && meth.statements.length > 0) {
+          errorOn(meth, ERR_NONEMPTY_CONSTRUCTOR);
+        }
+      }
+    }
+
+    @Override
+    public void endVisit(FieldDeclaration field, MethodScope scope) {
+      if (isForJSOSubclass(scope)) {
+        if (!field.isStatic()) {
+          errorOn(field, ERR_INSTANCE_FIELD);
+        }
+      }
+    }
+
+    @Override
+    public void endVisit(MethodDeclaration meth, ClassScope scope) {
+      if (isForJSOSubclass(scope)) {
+        if (!meth.isStatic() && ((meth.modifiers & AccFinal) == 0)) {
+          errorOn(meth, ERR_INSTANCE_METHOD_NONFINAL);
+        }
+
+        if (meth.binding != null && meth.binding.isOverriding()) {
+          errorOn(meth, ERR_OVERRIDDEN_METHOD);
+        }
+      }
+    }
+
+    @Override
+    public void endVisit(TypeDeclaration type, BlockScope scope) {
+      checkType(type);
+    }
+
+    @Override
+    public void endVisit(TypeDeclaration type, ClassScope scope) {
+      checkType(type);
+    }
+
+    @Override
+    public void endVisit(TypeDeclaration type, CompilationUnitScope scope) {
+      checkType(type);
+    }
+
+    private void checkType(TypeDeclaration type) {
+      if (isJSOSubclass(type)) {
+        if (type.enclosingType != null && !type.binding.isStatic()) {
+          errorOn(type, ERR_IS_NONSTATIC_NESTED);
+        }
+
+        ReferenceBinding[] interfaces = type.binding.superInterfaces();
+        if (interfaces != null) {
+          for (ReferenceBinding interf : interfaces) {
+            if (interf.methods() != null && interf.methods().length > 0) {
+              String intfName = String.copyValueOf(interf.shortReadableName());
+              errorOn(type, errInterfaceWithMethods(intfName));
+            }
+          }
+        }
+      }
+    }
+  }
+
+  protected static final char[][] JSO_CLASS_CHARS = CharOperation.splitOn('.',
+      JsValueGlue.JSO_CLASS.toCharArray());
+  static final String ERR_CONSTRUCTOR_WITH_PARAMETERS = "JavaScriptObject constructors should have no parameters";
+  static final String ERR_INSTANCE_FIELD = "JavaScriptObject classes cannot have fields";
+  static final String ERR_INSTANCE_METHOD_NONFINAL = "JavaScriptObject methods must be final";
+  static final String ERR_IS_NONSTATIC_NESTED = "Nested JavaScriptObject classes must be static";
+  static final String ERR_NEW_JSO = "JavaScriptObject classes cannot be instantiated with new";
+  static final String ERR_NONEMPTY_CONSTRUCTOR = "JavaScriptObject constructors must be empty";
+  static final String ERR_NONPROTECTED_CONSTRUCTOR = "JavaScriptObject constructors must be protected";
+
+  static final String ERR_OVERRIDDEN_METHOD = "JavaScriptObject classes cannot override methods";
+
+  /**
+   * Checks an entire
+   * {@link org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration}.
+   * 
+   */
+  public static void check(CompilationUnitDeclaration cud) {
+    TypeBinding jsoType = cud.scope.environment().getType(JSO_CLASS_CHARS);
+    if (jsoType == null) {
+      // JavaScriptObject not available; do nothing
+      return;
+    }
+
+    JSORestrictionsChecker checker = new JSORestrictionsChecker(cud, jsoType);
+    checker.check();
+  }
+
+  static String errInterfaceWithMethods(String intfName) {
+    return "JavaScriptObject classes cannot implement interfaces with methods ("
+        + intfName + ")";
+  }
+
+  private final CompilationUnitDeclaration cud;
+
+  /**
+   * The type of the GWT JavaScriptObject class. Cannot be null.
+   */
+  private final TypeBinding jsoType;
+
+  private JSORestrictionsChecker(CompilationUnitDeclaration cud,
+      TypeBinding jsoType) {
+    assert jsoType != null;
+    this.cud = cud;
+    this.jsoType = jsoType;
+  }
+
+  private void check() {
+    cud.traverse(new JSORestrictionsVisitor(), cud.scope);
+  }
+
+  private TypeBinding classType(Scope scope) {
+    return scope.classScope().referenceType().binding;
+  }
+
+  private void errorOn(ASTNode node, String error) {
+    CompilationResult compResult = cud.compilationResult();
+    int[] lineEnds = compResult.getLineSeparatorPositions();
+    int startLine = Util.getLineNumber(node.sourceStart(), lineEnds, 0,
+        lineEnds.length - 1);
+    int startColumn = Util.searchColumnNumber(lineEnds, startLine,
+        node.sourceStart());
+    DefaultProblem problem = new DefaultProblem(compResult.fileName, error,
+        IProblem.ExternalProblemNotFixable, null, ProblemSeverities.Error,
+        node.sourceStart(), node.sourceEnd(), startLine, startColumn);
+    compResult.record(problem, cud);
+  }
+
+  private boolean isForJSOSubclass(Scope scope) {
+    return isJSOSubclass(classType(scope));
+  }
+
+  private boolean isJSOSubclass(TypeBinding typeBinding) {
+    return typeBinding.isCompatibleWith(jsoType) && typeBinding != jsoType;
+  }
+
+  private boolean isJSOSubclass(TypeDeclaration typeDecl) {
+    return isJSOSubclass(typeDecl.binding);
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/jdt/StaticCompilationUnitProvider.java b/dev/core/src/com/google/gwt/dev/jdt/StaticCompilationUnitProvider.java
index 75e4ef3..1b314a8 100644
--- a/dev/core/src/com/google/gwt/dev/jdt/StaticCompilationUnitProvider.java
+++ b/dev/core/src/com/google/gwt/dev/jdt/StaticCompilationUnitProvider.java
@@ -50,7 +50,8 @@
    * Creates a stable name for this compilation unit.
    */
   public final String getLocation() {
-    return "transient source for " + packageName + "." + simpleTypeName;
+    String prefix = (packageName.length() > 0) ? packageName + "." : "";
+    return "transient source for " + prefix + simpleTypeName;
   }
 
   public String getMainTypeName() {
diff --git a/dev/core/src/com/google/gwt/dev/jdt/TypeRefVisitor.java b/dev/core/src/com/google/gwt/dev/jdt/TypeRefVisitor.java
index afbc73e..6ae0829 100644
--- a/dev/core/src/com/google/gwt/dev/jdt/TypeRefVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jdt/TypeRefVisitor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -19,6 +19,7 @@
 import org.eclipse.jdt.internal.compiler.ast.ArrayQualifiedTypeReference;
 import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference;
 import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.MessageSend;
 import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference;
 import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
 import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
@@ -37,58 +38,79 @@
  */
 public abstract class TypeRefVisitor extends ASTVisitor {
 
+  @Override
   public void endVisit(ArrayQualifiedTypeReference x, BlockScope scope) {
     maybeDispatch(scope, x.resolvedType);
   }
 
+  @Override
   public void endVisit(ArrayQualifiedTypeReference x, ClassScope scope) {
     maybeDispatch(scope, x.resolvedType);
   }
 
+  @Override
   public void endVisit(ArrayTypeReference x, BlockScope scope) {
     maybeDispatch(scope, x.resolvedType);
   }
 
+  @Override
   public void endVisit(ArrayTypeReference x, ClassScope scope) {
     maybeDispatch(scope, x.resolvedType);
   }
 
+  @Override
+  public void endVisit(MessageSend messageSend, BlockScope scope) {
+    if (messageSend.binding.isStatic()) {
+      maybeDispatch(scope, messageSend.actualReceiverType);
+    }
+  }
+
+  @Override
   public void endVisit(ParameterizedQualifiedTypeReference x, BlockScope scope) {
     maybeDispatch(scope, x.resolvedType);
   }
 
+  @Override
   public void endVisit(ParameterizedQualifiedTypeReference x, ClassScope scope) {
     maybeDispatch(scope, x.resolvedType);
   }
 
+  @Override
   public void endVisit(ParameterizedSingleTypeReference x, BlockScope scope) {
     maybeDispatch(scope, x.resolvedType);
   }
 
+  @Override
   public void endVisit(ParameterizedSingleTypeReference x, ClassScope scope) {
     maybeDispatch(scope, x.resolvedType);
   }
 
+  @Override
   public void endVisit(QualifiedTypeReference x, BlockScope scope) {
     maybeDispatch(scope, x.resolvedType);
   }
 
+  @Override
   public void endVisit(QualifiedTypeReference x, ClassScope scope) {
     maybeDispatch(scope, x.resolvedType);
   }
 
+  @Override
   public void endVisit(SingleTypeReference x, BlockScope scope) {
     maybeDispatch(scope, x.resolvedType);
   }
 
+  @Override
   public void endVisit(SingleTypeReference x, ClassScope scope) {
     maybeDispatch(scope, x.resolvedType);
   }
 
+  @Override
   public void endVisit(Wildcard x, BlockScope scope) {
     maybeDispatch(scope, x.resolvedType);
   }
 
+  @Override
   public void endVisit(Wildcard x, ClassScope scope) {
     maybeDispatch(scope, x.resolvedType);
   }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
index 1def354..9008025 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -41,7 +41,8 @@
 import com.google.gwt.dev.jjs.impl.DeadCodeElimination;
 import com.google.gwt.dev.jjs.impl.GenerateJavaAST;
 import com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST;
-import com.google.gwt.dev.jjs.impl.JavaScriptObjectCaster;
+import com.google.gwt.dev.jjs.impl.JavaScriptObjectNormalizer;
+import com.google.gwt.dev.jjs.impl.JsoDevirtualizer;
 import com.google.gwt.dev.jjs.impl.MakeCallsStatic;
 import com.google.gwt.dev.jjs.impl.MethodAndClassFinalizer;
 import com.google.gwt.dev.jjs.impl.MethodCallTightener;
@@ -293,7 +294,6 @@
           goldenCuds, jsProgram);
 
       // BuildTypeMap can uncover syntactic JSNI errors; report & abort
-      // 
       checkForErrors(logger, true);
 
       // Compute all super type/sub type info
@@ -307,11 +307,10 @@
           options.isEnableAssertions());
 
       // GenerateJavaAST can uncover semantic JSNI errors; report & abort
-      // 
       checkForErrors(logger, true);
 
       /*
-       * TODO: if we defer this until later, we could maybe use the results of
+       * TODO: If we defer this until later, we could maybe use the results of
        * the assertions to enable more optimizations.
        */
       if (options.isEnableAssertions()) {
@@ -331,9 +330,11 @@
       }
 
       // Rebind each entry point.
-      //
       findEntryPoints(logger, rebindOracle, declEntryPoints, jprogram);
 
+      // Replace references to JSO subtypes with JSO itself.
+      JavaScriptObjectNormalizer.exec(jprogram);
+
       // (4) Optimize the normalized Java AST
       boolean didChange;
       do {
@@ -373,9 +374,9 @@
       // (5) "Normalize" the high-level Java tree into a lower-level tree more
       // suited for JavaScript code generation. Don't go reordering these
       // willy-nilly because there are some subtle interdependencies.
+      JsoDevirtualizer.exec(jprogram);
       CatchBlockNormalizer.exec(jprogram);
       CompoundAssignmentNormalizer.exec(jprogram);
-      JavaScriptObjectCaster.exec(jprogram);
       CastNormalizer.exec(jprogram);
       ArrayNormalizer.exec(jprogram);
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java
index 271ef66..133ace5 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java
@@ -32,14 +32,21 @@
     return fullName.substring(0, pos + 1);
   }
 
-  private static String getTypeName(JType type) {
+  private static String getTypeName(JProgram program, JType type) {
     String typeName;
     if (type instanceof JArrayType) {
       typeName = type.getJsniSignatureName().replace('/', '.');
+      // Mangle the class name to match hosted mode.
+      if (program.isJavaScriptObject(((JArrayType) type).getLeafType())) {
+        typeName = typeName.replace(";", "$;");
+      }
     } else {
       typeName = type.getName();
+      // Mangle the class name to match hosted mode.
+      if (program.isJavaScriptObject(type)) {
+        typeName += '$';
+      }
     }
-
     return typeName;
   }
 
@@ -53,7 +60,7 @@
     super(program);
     refType = type;
 
-    String typeName = getTypeName(type);
+    String typeName = getTypeName(program, type);
 
     JMethod method = program.getIndexedMethod(type.getClassLiteralFactoryMethod());
     assert method != null;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JNode.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JNode.java
index b8c9ba0..715014d 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JNode.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JNode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -39,6 +39,10 @@
     this.info = info;
   }
 
+  public JProgram getProgram() {
+    return program;
+  }
+
   public SourceInfo getSourceInfo() {
     return info;
   }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
index 6719dcc..c98ffb4 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -434,15 +434,30 @@
   }
 
   public JField getIndexedField(String string) {
-    return indexedFields.get(string);
+    JField field = indexedFields.get(string);
+    if (field == null) {
+      throw new InternalCompilerException("Unable to locate index field: "
+          + string);
+    }
+    return field;
   }
 
   public JMethod getIndexedMethod(String string) {
-    return indexedMethods.get(string);
+    JMethod method = indexedMethods.get(string);
+    if (method == null) {
+      throw new InternalCompilerException("Unable to locate index method: "
+          + string);
+    }
+    return method;
   }
 
   public JReferenceType getIndexedType(String string) {
-    return indexedTypes.get(string);
+    JReferenceType type = indexedTypes.get(string);
+    if (type == null) {
+      throw new InternalCompilerException("Unable to locate index type: "
+          + string);
+    }
+    return type;
   }
 
   public JClassType getJavaScriptObject() {
@@ -667,7 +682,7 @@
   }
 
   public boolean isJavaScriptObject(JType type) {
-    if (type instanceof JClassType) {
+    if (type instanceof JClassType && typeSpecialJavaScriptObject != null) {
       return typeOracle.canTriviallyCast((JClassType) type,
           typeSpecialJavaScriptObject);
     }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
index e6ceb60..b42d688 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -147,7 +147,16 @@
 
       JClassType cType = (JClassType) type;
       if (qType instanceof JClassType) {
-        return isSuperClass(cType, (JClassType) qType);
+        JClassType qcType = (JClassType) qType;
+        if (isSuperClass(cType, qcType)) {
+          return true;
+        }
+        // All JavaScriptObject types can be freely cast to each other.
+        JClassType jsoType = program.getJavaScriptObject();
+        if (jsoType != null) {
+          return isSameOrSuper(cType, jsoType)
+              && isSameOrSuper(qcType, jsoType);
+        }
       } else if (qType instanceof JInterfaceType) {
         return implementsInterface(cType, (JInterfaceType) qType);
       }
@@ -487,6 +496,10 @@
     return map2;
   }
 
+  private boolean isSameOrSuper(JClassType type, JClassType qType) {
+    return (type == qType || isSuperClass(type, qType));
+  }
+
   /**
    * Record the all of my super classes (and myself as a subclass of them).
    */
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java
index c781389..37d1be2 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -36,7 +36,6 @@
 import com.google.gwt.dev.jjs.ast.JType;
 import com.google.gwt.dev.jjs.ast.JTypeOracle;
 import com.google.gwt.dev.jjs.ast.JVisitor;
-import com.google.gwt.dev.jjs.ast.js.JClassSeed;
 import com.google.gwt.dev.jjs.ast.js.JsonObject;
 import com.google.gwt.dev.jjs.ast.js.JsonObject.JsonPropInit;
 
@@ -110,6 +109,12 @@
 
       // pass our info to JProgram
       program.initTypeInfo(classes, jsonObjects);
+
+      // JSO's maker queryId is -1 (used for array stores).
+      JClassType jsoType = program.getJavaScriptObject();
+      if (jsoType != null) {
+        queryIds.put(jsoType, -1);
+      }
       program.recordQueryIds(queryIds);
     }
 
@@ -194,7 +199,8 @@
        */
       computeSourceClass(type.extnds);
 
-      if (!program.typeOracle.isInstantiatedType(type)) {
+      if (!program.typeOracle.isInstantiatedType(type)
+          || program.isJavaScriptObject(type)) {
         return;
       }
 
@@ -221,13 +227,8 @@
         }
       }
 
-      /*
-       * Weird: JavaScriptObjects MUST have a typeId, the implementation of
-       * Cast.wrapJSO depends on it. Object must also have a typeId, to force
-       * String to have an id of 2.
-       */
-      if (yesSet == null && !program.isJavaScriptObject(type)
-          && (type != program.getTypeJavaLangObject())) {
+      // Object a typeId, to force String to have an id of 2.
+      if (yesSet == null && type != program.getTypeJavaLangObject()) {
         return; // won't satisfy anything
       }
 
@@ -235,8 +236,7 @@
       JReferenceType[] yesArray = new JReferenceType[nextQueryId];
       if (yesSet != null) {
         for (JReferenceType yesType : yesSet) {
-          Integer boxedInt = queryIds.get(yesType);
-          yesArray[boxedInt.intValue()] = yesType;
+          yesArray[queryIds.get(yesType)] = yesType;
         }
       }
 
@@ -268,6 +268,11 @@
           }
         }
 
+        // If the target type is a JavaScriptObject, don't record an id.
+        if (program.isJavaScriptObject(targetType)) {
+          return;
+        }
+
         recordCastInternal((JReferenceType) targetType, rhsType);
       }
     }
@@ -277,7 +282,7 @@
       JReferenceType toType = targetType;
       Set<JReferenceType> querySet = queriedTypes.get(toType);
       if (querySet == null) {
-        queryIds.put(toType, new Integer(nextQueryId++));
+        queryIds.put(toType, nextQueryId++);
         querySet = new HashSet<JReferenceType>();
         queriedTypes.put(toType, querySet);
       }
@@ -385,39 +390,22 @@
       } else if (toType instanceof JReferenceType) {
         JExpression curExpr = x.getExpr();
         JReferenceType refType = (JReferenceType) toType;
-        JType argType = x.getExpr().getType();
-        if (program.isJavaScriptObject(argType)) {
-          /*
-           * A JSO-derived class that is about to be cast must be "wrapped"
-           * first. Since a JSO was never constructed, it may not have an
-           * accessible prototype. Instead we copy fields from the seed
-           * function's prototype directly onto the target object as expandos.
-           * See com.google.gwt.lang.Cast.wrapJSO().
-           */
-          JMethod wrap = program.getIndexedMethod("Cast.wrapJSO");
-          // override the type of the called method with the JSO's type
-          JMethodCall call = new JMethodCall(program, x.getSourceInfo(), null,
-              wrap, argType);
-          JClassSeed seed = program.getLiteralClassSeed((JClassType) argType);
-          call.getArgs().add(curExpr);
-          call.getArgs().add(seed);
-          curExpr = call;
-        }
-        if (argType instanceof JClassType
-            && program.typeOracle.canTriviallyCast((JClassType) argType,
-                refType)) {
-          // TODO(???): why is this only for JClassType?
+        JReferenceType argType = (JReferenceType) x.getExpr().getType();
+        if (program.typeOracle.canTriviallyCast(argType, refType)) {
           // just remove the cast
           replaceExpr = curExpr;
         } else {
-          JMethod method = program.getIndexedMethod("Cast.dynamicCast");
+          boolean isJsoCast = program.isJavaScriptObject(toType);
+          JMethod method = program.getIndexedMethod(isJsoCast
+              ? "Cast.dynamicCastJso" : "Cast.dynamicCast");
           // override the type of the called method with the target cast type
           JMethodCall call = new JMethodCall(program, x.getSourceInfo(), null,
               method, toType);
-          Integer boxedInt = queryIds.get(refType);
-          JIntLiteral qId = program.getLiteralInt(boxedInt.intValue());
           call.getArgs().add(curExpr);
-          call.getArgs().add(qId);
+          if (!isJsoCast) {
+            JIntLiteral qId = program.getLiteralInt(queryIds.get(refType));
+            call.getArgs().add(qId);
+          }
           replaceExpr = call;
         }
       } else {
@@ -482,10 +470,9 @@
 
     @Override
     public void endVisit(JInstanceOf x, Context ctx) {
-      JType argType = x.getExpr().getType();
-      if (argType instanceof JClassType
-          && program.typeOracle.canTriviallyCast((JClassType) argType,
-              x.getTestType())) {
+      JReferenceType argType = (JReferenceType) x.getExpr().getType();
+      JReferenceType toType = x.getTestType();
+      if (program.typeOracle.canTriviallyCast(argType, toType)) {
         // trivially true if non-null; replace with a null test
         JNullLiteral nullLit = program.getLiteralNull();
         JBinaryOperation eq = new JBinaryOperation(program, x.getSourceInfo(),
@@ -493,13 +480,16 @@
             x.getExpr(), nullLit);
         ctx.replaceMe(eq);
       } else {
-        JMethod method = program.getIndexedMethod("Cast.instanceOf");
+        boolean isJsoCast = program.isJavaScriptObject(toType);
+        JMethod method = program.getIndexedMethod(isJsoCast
+            ? "Cast.instanceOfJso" : "Cast.instanceOf");
         JMethodCall call = new JMethodCall(program, x.getSourceInfo(), null,
             method);
-        Integer boxedInt = queryIds.get(x.getTestType());
-        JIntLiteral qId = program.getLiteralInt(boxedInt.intValue());
         call.getArgs().add(x.getExpr());
-        call.getArgs().add(qId);
+        if (!isJsoCast) {
+          JIntLiteral qId = program.getLiteralInt(queryIds.get(toType));
+          call.getArgs().add(qId);
+        }
         ctx.replaceMe(call);
       }
     }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
index 4880242..4687d70 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
@@ -237,20 +237,29 @@
         String[] parts = ident.substring(1).split("::");
         assert (parts.length == 2);
         String className = parts[0];
-        JReferenceType type = program.getFromTypeMap(className);
-        if (type == null) {
-          reportJsniError(info, methodDecl,
-              "Unresolvable native reference to type '" + className + "'");
-          return null;
+        JReferenceType type = null;
+        if (!className.equals("null")) {
+          type = program.getFromTypeMap(className);
+          if (type == null) {
+            reportJsniError(info, methodDecl,
+                "Unresolvable native reference to type '" + className + "'");
+            return null;
+          }
         }
         String rhs = parts[1];
         int parenPos = rhs.indexOf('(');
         if (parenPos < 0) {
           // look for a field
-          for (int i = 0; i < type.fields.size(); ++i) {
-            JField field = type.fields.get(i);
-            if (field.getName().equals(rhs)) {
-              return field;
+          if (type == null) {
+            if (rhs.equals("nullField")) {
+              return program.getNullField();
+            }
+          } else {
+            for (int i = 0; i < type.fields.size(); ++i) {
+              JField field = type.fields.get(i);
+              if (field.getName().equals(rhs)) {
+                return field;
+              }
             }
           }
 
@@ -259,18 +268,24 @@
                   + className + "'");
         } else {
           // look for a method
-          String methodName = rhs.substring(0, parenPos);
           String almostMatches = null;
-          for (int i = 0; i < type.methods.size(); ++i) {
-            JMethod method = type.methods.get(i);
-            if (method.getName().equals(methodName)) {
-              String jsniSig = getJsniSig(method);
-              if (jsniSig.equals(rhs)) {
-                return method;
-              } else if (almostMatches == null) {
-                almostMatches = "'" + jsniSig + "'";
-              } else {
-                almostMatches += ", '" + jsniSig + "'";
+          String methodName = rhs.substring(0, parenPos);
+          if (type == null) {
+            if (rhs.equals("nullMethod()")) {
+              return program.getNullMethod();
+            }
+          } else {
+            for (int i = 0; i < type.methods.size(); ++i) {
+              JMethod method = type.methods.get(i);
+              if (method.getName().equals(methodName)) {
+                String jsniSig = getJsniSig(method);
+                if (jsniSig.equals(rhs)) {
+                  return method;
+                } else if (almostMatches == null) {
+                  almostMatches = "'" + jsniSig + "'";
+                } else {
+                  almostMatches += ", '" + jsniSig + "'";
+                }
               }
             }
           }
@@ -291,14 +306,16 @@
 
       private void processField(JsNameRef nameRef, SourceInfo info,
           JField field, JsContext<JsExpression> ctx) {
-        if (field.isStatic() && nameRef.getQualifier() != null) {
-          reportJsniError(info, methodDecl,
-              "Cannot make a qualified reference to the static field "
-                  + field.getName());
-        } else if (!field.isStatic() && nameRef.getQualifier() == null) {
-          reportJsniError(info, methodDecl,
-              "Cannot make an unqualified reference to the instance field "
-                  + field.getName());
+        if (field.getEnclosingType() != null) {
+          if (field.isStatic() && nameRef.getQualifier() != null) {
+            reportJsniError(info, methodDecl,
+                "Cannot make a qualified reference to the static field "
+                    + field.getName());
+          } else if (!field.isStatic() && nameRef.getQualifier() == null) {
+            reportJsniError(info, methodDecl,
+                "Cannot make an unqualified reference to the instance field "
+                    + field.getName());
+          }
         }
 
         /*
@@ -328,14 +345,16 @@
 
       private void processMethod(JsNameRef nameRef, SourceInfo info,
           JMethod method) {
-        if (method.isStatic() && nameRef.getQualifier() != null) {
-          reportJsniError(info, methodDecl,
-              "Cannot make a qualified reference to the static method "
-                  + method.getName());
-        } else if (!method.isStatic() && nameRef.getQualifier() == null) {
-          reportJsniError(info, methodDecl,
-              "Cannot make an unqualified reference to the instance method "
-                  + method.getName());
+        if (method.getEnclosingType() != null) {
+          if (method.isStatic() && nameRef.getQualifier() != null) {
+            reportJsniError(info, methodDecl,
+                "Cannot make a qualified reference to the static method "
+                    + method.getName());
+          } else if (!method.isStatic() && nameRef.getQualifier() == null) {
+            reportJsniError(info, methodDecl,
+                "Cannot make an unqualified reference to the instance method "
+                    + method.getName());
+          }
         }
 
         JsniMethodRef methodRef = new JsniMethodRef(program, info,
@@ -531,12 +550,17 @@
           JMethod method = currentClass.methods.get(2);
           assert ("getClass".equals(method.getName()));
 
-          tryFindUpRefs(method);
-
-          JMethodBody body = (JMethodBody) method.getBody();
-          JClassLiteral classLit = program.getLiteralClass(currentClass);
-          body.getStatements().add(
-              new JReturnStatement(program, null, classLit));
+          if (program.isJavaScriptObject(currentClass)
+              && currentClass != program.getJavaScriptObject()) {
+            // Just use JavaScriptObject's implementation for all subclasses.
+            currentClass.methods.remove(2);
+          } else {
+            tryFindUpRefs(method);
+            JMethodBody body = (JMethodBody) method.getBody();
+            JClassLiteral classLit = program.getLiteralClass(currentClass);
+            body.getStatements().add(
+                new JReturnStatement(program, null, classLit));
+          }
         }
 
         // Implement Class.desiredAssertionStatus
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
index 38d9a42..e7baca2 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -507,7 +507,7 @@
         }
       }
 
-      if (typeOracle.isInstantiatedType(x)) {
+      if (typeOracle.isInstantiatedType(x) && !program.isJavaScriptObject(x)) {
         generateClassSetup(x, globalStmts);
       }
 
@@ -1211,6 +1211,8 @@
       if (x == program.getTypeJavaLangObject()) {
         // special: setup a "toString" alias for java.lang.Object.toString()
         generateToStringAlias(x, globalStmts);
+        // special: setup the identifying typeMarker field
+        generateTypeMarker(x, globalStmts);
       }
 
       generateTypeId(x, globalStmts);
@@ -1376,6 +1378,19 @@
       }
     }
 
+    private void generateTypeMarker(JClassType x, List<JsStatement> globalStmts) {
+      JField typeMarkerField = program.getIndexedField("Object.typeMarker");
+      JsName typeMarkerName = names.get(typeMarkerField);
+      if (typeMarkerName == null) {
+        // Was pruned; this compilation must have no JSO instanceof tests.
+        return;
+      }
+      JsNameRef fieldRef = typeMarkerName.makeRef();
+      fieldRef.setQualifier(globalTemp.makeRef());
+      JsExpression asg = createAssignment(fieldRef, nullMethodName.makeRef());
+      globalStmts.add(new JsExprStmt(asg));
+    }
+
     private JsExpression generateTypeTable() {
       JsArrayLiteral arrayLit = new JsArrayLiteral();
       for (int i = 0; i < program.getJsonTypeTable().size(); ++i) {
@@ -1664,6 +1679,7 @@
 
     // Object fields
     specialObfuscatedIdents.put("typeId", "tI");
+    specialObfuscatedIdents.put("typeMarker", "tM");
 
     // String polymorphic
     specialObfuscatedIdents.put("charAt", "cA");
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectCaster.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectCaster.java
deleted file mode 100644
index b4f0a7c..0000000
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectCaster.java
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Copyright 2007 Google Inc.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- * 
- * http://www.apache.org/licenses/LICENSE-2.0
- * 
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.jjs.impl;
-
-import com.google.gwt.dev.jjs.ast.Context;
-import com.google.gwt.dev.jjs.ast.JArrayRef;
-import com.google.gwt.dev.jjs.ast.JBinaryOperation;
-import com.google.gwt.dev.jjs.ast.JCastOperation;
-import com.google.gwt.dev.jjs.ast.JConditional;
-import com.google.gwt.dev.jjs.ast.JExpression;
-import com.google.gwt.dev.jjs.ast.JLocalDeclarationStatement;
-import com.google.gwt.dev.jjs.ast.JMethod;
-import com.google.gwt.dev.jjs.ast.JMethodCall;
-import com.google.gwt.dev.jjs.ast.JModVisitor;
-import com.google.gwt.dev.jjs.ast.JNewArray;
-import com.google.gwt.dev.jjs.ast.JParameter;
-import com.google.gwt.dev.jjs.ast.JProgram;
-import com.google.gwt.dev.jjs.ast.JReferenceType;
-import com.google.gwt.dev.jjs.ast.JReturnStatement;
-import com.google.gwt.dev.jjs.ast.JType;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Synthesize casts for JavaScriptObject types in cases where dynamic type
- * information may be necessary.
- */
-public class JavaScriptObjectCaster {
-
-  /**
-   * Synthesize casts from JavaScriptObjects to trigger wrapping.
-   */
-  private class AssignmentVisitor extends JModVisitor {
-
-    private JMethod currentMethod;
-
-    @Override
-    public void endVisit(JBinaryOperation x, Context ctx) {
-      if (x.isAssignment()) {
-        JType lhsType = x.getLhs().getType();
-        JExpression newRhs = checkAndReplaceJso(x.getRhs(), lhsType);
-        if (newRhs == x.getRhs()) {
-          // There's another case to check: if we have an array store that may
-          // trigger a type check, we need to wrap the rhs.
-          if (x.getLhs() instanceof JArrayRef) {
-            newRhs = checkAndReplaceJsoArrayStore(newRhs, lhsType);
-          }
-        }
-        if (newRhs != x.getRhs()) {
-          JBinaryOperation asg = new JBinaryOperation(program,
-              x.getSourceInfo(), lhsType, x.getOp(), x.getLhs(), newRhs);
-          ctx.replaceMe(asg);
-        }
-      }
-    }
-
-    @Override
-    public void endVisit(JConditional x, Context ctx) {
-      JExpression newThen = checkAndReplaceJso(x.getThenExpr(), x.getType());
-      JExpression newElse = checkAndReplaceJso(x.getElseExpr(), x.getType());
-      if (newThen != x.getThenExpr() || newElse != x.getElseExpr()) {
-        JConditional newCond = new JConditional(program, x.getSourceInfo(),
-            x.getType(), x.getIfTest(), newThen, newElse);
-        ctx.replaceMe(newCond);
-      }
-    }
-
-    @Override
-    public void endVisit(JLocalDeclarationStatement x, Context ctx) {
-      JExpression newInst = x.getInitializer();
-      if (newInst != null) {
-        newInst = checkAndReplaceJso(newInst, x.getLocalRef().getType());
-        if (newInst != x.getInitializer()) {
-          JLocalDeclarationStatement newStmt = new JLocalDeclarationStatement(
-              program, x.getSourceInfo(), x.getLocalRef(), newInst);
-          ctx.replaceMe(newStmt);
-        }
-      }
-    }
-
-    @Override
-    public void endVisit(JMethod x, Context ctx) {
-      currentMethod = null;
-    }
-
-    @Override
-    public void endVisit(JMethodCall x, Context ctx) {
-      // Check implicit assignments from argument and instance passing.
-
-      ArrayList<JExpression> args = x.getArgs();
-      JMethod target = x.getTarget();
-
-      for (int i = 0; i < target.params.size(); ++i) {
-        JParameter param = target.params.get(i);
-        JExpression arg = args.get(i);
-        JExpression newArg = checkAndReplaceJso(arg, param.getType(),
-            target.isNative());
-        this.didChange |= (newArg != arg);
-        args.set(i, newArg);
-      }
-
-      /*
-       * Virtual calls *require* wrapping to dispatch correctly. This should be
-       * a rare case since most call should get statically resolved already.
-       */
-      if (!target.isStatic()) {
-        JExpression newInst = checkAndReplaceJso(x.getInstance(),
-            program.getTypeJavaLangObject());
-        if (newInst != x.getInstance()) {
-          JMethodCall newCall = new JMethodCall(program, x.getSourceInfo(),
-              newInst, target, x.isStaticDispatchOnly());
-          newCall.getArgs().addAll(args);
-          ctx.replaceMe(newCall);
-        }
-      }
-    }
-
-    @Override
-    public void endVisit(JNewArray x, Context ctx) {
-      List<JExpression> initializers = x.initializers;
-      if (initializers != null) {
-        for (int i = 0; i < initializers.size(); ++i) {
-          JExpression intializer = initializers.get(i);
-          JExpression newInitializer = checkAndReplaceJsoArrayStore(intializer,
-              x.getArrayType().getLeafType());
-          if (intializer != newInitializer) {
-            initializers.set(i, newInitializer);
-            this.didChange = true;
-          }
-        }
-      }
-    }
-
-    @Override
-    public void endVisit(JReturnStatement x, Context ctx) {
-      if (x.getExpr() != null) {
-        JExpression newExpr = checkAndReplaceJso(x.getExpr(),
-            currentMethod.getType());
-        if (newExpr != x.getExpr()) {
-          JReturnStatement newStmt = new JReturnStatement(program,
-              x.getSourceInfo(), newExpr);
-          ctx.replaceMe(newStmt);
-        }
-      }
-    }
-
-    @Override
-    public boolean visit(JMethod x, Context ctx) {
-      currentMethod = x;
-      return true;
-    }
-
-    private JExpression checkAndReplaceJso(JExpression arg, JType targetType) {
-      return checkAndReplaceJso(arg, targetType, false);
-    }
-
-    /**
-     * Wraps a JSO-typed argument if the target type is a different type.
-     */
-    private JExpression checkAndReplaceJso(JExpression arg, JType targetType,
-        boolean nowrapJso) {
-      JType argType = arg.getType();
-      if (argType == targetType) {
-        return arg;
-      }
-      if (!(targetType instanceof JReferenceType)) {
-        return arg;
-      }
-      if (!program.isJavaScriptObject(argType)) {
-        return arg;
-      }
-      /*
-       * Special case: when calling a native method, only force a wrapping if
-       * the type is explicitly Object. As long as the target type is a JSO
-       * subclass, don't bother wrapping since we're losing type information
-       * anyway.
-       */
-      if (nowrapJso && program.isJavaScriptObject(targetType)) {
-        return arg;
-      }
-      // Synthesize a cast to the arg type to force a wrap
-      JCastOperation cast = new JCastOperation(program, arg.getSourceInfo(),
-          argType, arg);
-      return cast;
-    }
-
-    /**
-     * Wraps a JSO-typed argument.
-     * 
-     * TODO: We could eliminate casts cases where the array instance was never
-     * cast to a weaker type.
-     */
-    private JExpression checkAndReplaceJsoArrayStore(JExpression arg,
-        JType targetType) {
-      if (!(targetType instanceof JReferenceType)) {
-        return arg;
-      }
-      if (!program.isJavaScriptObject(arg.getType())) {
-        return arg;
-      }
-      // Synthesize a cast to the target type
-      JCastOperation cast = new JCastOperation(program, arg.getSourceInfo(),
-          targetType, arg);
-      return cast;
-    }
-  }
-
-  public static void exec(JProgram program) {
-    new JavaScriptObjectCaster(program).execImpl();
-  }
-
-  private final JProgram program;
-
-  private JavaScriptObjectCaster(JProgram program) {
-    this.program = program;
-  }
-
-  private void execImpl() {
-    AssignmentVisitor visitor = new AssignmentVisitor();
-    visitor.accept(program);
-  }
-
-}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectNormalizer.java
new file mode 100644
index 0000000..5fc544d
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectNormalizer.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2008 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.jjs.impl;
+
+import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.JArrayType;
+import com.google.gwt.dev.jjs.ast.JCastOperation;
+import com.google.gwt.dev.jjs.ast.JClassLiteral;
+import com.google.gwt.dev.jjs.ast.JField;
+import com.google.gwt.dev.jjs.ast.JInstanceOf;
+import com.google.gwt.dev.jjs.ast.JLocal;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
+import com.google.gwt.dev.jjs.ast.JNewArray;
+import com.google.gwt.dev.jjs.ast.JParameter;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JReferenceType;
+import com.google.gwt.dev.jjs.ast.JType;
+
+/**
+ * Replace references to JSO subtypes with JSO itself.
+ */
+public class JavaScriptObjectNormalizer {
+
+  /**
+   * Map types from JSO subtypes to JSO itself.
+   */
+  private class NormalizeVisitor extends JModVisitor {
+
+    @Override
+    public void endVisit(JCastOperation x, Context ctx) {
+      JType newType = translate(x.getCastType());
+      if (newType != x.getCastType()) {
+        ctx.replaceMe(new JCastOperation(program, x.getSourceInfo(), newType,
+            x.getExpr()));
+      }
+    }
+
+    @Override
+    public void endVisit(JClassLiteral x, Context ctx) {
+      JType newType = translate(x.getRefType());
+      if (newType != x.getRefType()) {
+        ctx.replaceMe(program.getLiteralClass(newType));
+      }
+    }
+
+    @Override
+    public void endVisit(JField x, Context ctx) {
+      x.setType(translate(x.getType()));
+    }
+
+    @Override
+    public void endVisit(JInstanceOf x, Context ctx) {
+      JReferenceType newType = (JReferenceType) translate(x.getTestType());
+      if (newType != x.getTestType()) {
+        ctx.replaceMe(new JInstanceOf(program, x.getSourceInfo(), newType,
+            x.getExpr()));
+      }
+    }
+
+    @Override
+    public void endVisit(JLocal x, Context ctx) {
+      x.setType(translate(x.getType()));
+    }
+
+    @Override
+    public void endVisit(JMethod x, Context ctx) {
+      x.setType(translate(x.getType()));
+    }
+
+    @Override
+    public void endVisit(JNewArray x, Context ctx) {
+      x.setType(translate(x.getType()));
+    }
+
+    @Override
+    public void endVisit(JParameter x, Context ctx) {
+      x.setType(translate(x.getType()));
+    }
+
+    private JType translate(JType type) {
+      if (program.isJavaScriptObject(type)) {
+        return program.getJavaScriptObject();
+      } else if (type instanceof JArrayType) {
+        JArrayType arrayType = (JArrayType) type;
+        if (program.isJavaScriptObject(arrayType.getLeafType())) {
+          return program.getTypeArray(program.getJavaScriptObject(),
+              arrayType.getDims());
+        }
+      }
+      return type;
+    }
+  }
+
+  public static void exec(JProgram program) {
+    new JavaScriptObjectNormalizer(program).execImpl();
+  }
+
+  private final JProgram program;
+
+  private JavaScriptObjectNormalizer(JProgram program) {
+    this.program = program;
+  }
+
+  private void execImpl() {
+    NormalizeVisitor visitor = new NormalizeVisitor();
+    visitor.accept(program);
+  }
+
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JsoDevirtualizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JsoDevirtualizer.java
new file mode 100644
index 0000000..ff4610e
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JsoDevirtualizer.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2008 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.jjs.impl;
+
+import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.JClassType;
+import com.google.gwt.dev.jjs.ast.JConditional;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JMethodBody;
+import com.google.gwt.dev.jjs.ast.JMethodCall;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
+import com.google.gwt.dev.jjs.ast.JParameter;
+import com.google.gwt.dev.jjs.ast.JParameterRef;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JReturnStatement;
+import com.google.gwt.dev.jjs.impl.MakeCallsStatic.CreateStaticImplsVisitor;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * All call sites that might result in virtual dispatch to a JSO must be
+ * rewritten to force static dispatch. This transform may NOT be run multiple
+ * times; it will create ever-expanding replacement expressions.
+ */
+public class JsoDevirtualizer {
+
+  /**
+   * Rewrite any virtual dispatches to Object or JavaScriptObject such that
+   * dispatch occurs statically for JSOs.
+   */
+  private class RewriteVirtualDispatches extends JModVisitor {
+
+    @Override
+    public void endVisit(JMethodCall x, Context ctx) {
+      JMethod method = x.getTarget();
+      JMethod newMethod;
+      if (virtualJsoMethods.contains(method)) {
+        /*
+         * Force a JSO call to be static. Really, this should never be necessary
+         * as long as MakeCallsStatic runs. However, we would rather not insist
+         * that normalization depends on optimization having been done.
+         */
+        newMethod = program.getStaticImpl(method);
+      } else if (objectMethodToJsoMethod.keySet().contains(method)) {
+        /*
+         * Map the object call to its appropriate devirtualizing method.
+         */
+        newMethod = objectMethodToJsoMethod.get(method);
+      } else {
+        return;
+      }
+      assert (newMethod != null);
+      ctx.replaceMe(MakeCallsStatic.makeStaticCall(x, newMethod));
+    }
+
+    @Override
+    public boolean visit(JMethod x, Context ctx) {
+      // Don't rewrite the polymorphic call inside of the devirtualizing method!
+      if (objectMethodToJsoMethod.values().contains(x)) {
+        return false;
+      }
+      return true;
+    }
+  }
+
+  public static void exec(JProgram program) {
+    new JsoDevirtualizer(program).execImpl();
+  }
+
+  /**
+   * Maps each Object instance methods (ie, {@link Object#equals(Object)}) onto
+   * its corresponding devirtualizing method.
+   */
+  protected Map<JMethod, JMethod> objectMethodToJsoMethod = new HashMap<JMethod, JMethod>();
+
+  /**
+   * Contains the set of live instance methods on JavaScriptObject; typically
+   * overrides of Object methods.
+   */
+  protected Set<JMethod> virtualJsoMethods = new HashSet<JMethod>();
+
+  /**
+   * Contains the set of devirtualizing methods that replace polymorphic calls
+   * to Object methods.
+   */
+  private Set<JMethod> devirtualMethods = new HashSet<JMethod>();
+
+  /**
+   * Contains the Cast.isJavaObject method.
+   */
+  private final JMethod isJavaObjectMethod;
+
+  private final JProgram program;
+
+  private JsoDevirtualizer(JProgram program) {
+    this.program = program;
+    this.isJavaObjectMethod = program.getIndexedMethod("Cast.isJavaObject");
+  }
+
+  /**
+   * Create a conditional method to discriminate between static and virtual
+   * dispatch.
+   * 
+   * <pre>
+   * static boolean equals__devirtual$(Object this, Object other) {
+   *   return Cast.isJavaObject(this) ? this.equals(other) : JavaScriptObject.equals$(this, other);
+   * }
+   * </pre>
+   */
+  private JMethod createDevirtualMethod(JMethod objectMethod, JMethod jsoImpl) {
+    JClassType jsoType = program.getJavaScriptObject();
+    SourceInfo sourceInfo = null;
+
+    // Create the new method.
+    String name = objectMethod.getName() + "__devirtual$";
+    JMethod newMethod = program.createMethod(sourceInfo, name.toCharArray(),
+        jsoType, objectMethod.getType(), false, true, true, false, false);
+
+    // Setup parameters.
+    JParameter thisParam = program.createParameter(null,
+        "this$static".toCharArray(), program.getTypeJavaLangObject(), true,
+        true, newMethod);
+    for (JParameter oldParam : objectMethod.params) {
+      program.createParameter(sourceInfo, oldParam.getName().toCharArray(),
+          oldParam.getType(), true, false, newMethod);
+    }
+    newMethod.freezeParamTypes();
+
+    // Build from bottom up.
+    JMethodCall condition = new JMethodCall(program, sourceInfo, null,
+        isJavaObjectMethod);
+    condition.getArgs().add(new JParameterRef(program, sourceInfo, thisParam));
+
+    JMethodCall thenValue = new JMethodCall(program, sourceInfo,
+        new JParameterRef(program, sourceInfo, thisParam), objectMethod);
+    for (JParameter param : newMethod.params) {
+      if (param != thisParam) {
+        thenValue.getArgs().add(new JParameterRef(program, sourceInfo, param));
+      }
+    }
+
+    JMethodCall elseValue = new JMethodCall(program, sourceInfo, null, jsoImpl);
+    for (JParameter param : newMethod.params) {
+      elseValue.getArgs().add(new JParameterRef(program, sourceInfo, param));
+    }
+
+    JConditional conditional = new JConditional(program, sourceInfo,
+        objectMethod.getType(), condition, thenValue, elseValue);
+
+    JReturnStatement returnStatement = new JReturnStatement(program,
+        sourceInfo, conditional);
+    ((JMethodBody) newMethod.getBody()).getStatements().add(returnStatement);
+    return newMethod;
+  }
+
+  private void execImpl() {
+    JClassType jsoType = program.getJavaScriptObject();
+    if (jsoType == null) {
+      return;
+    }
+
+    for (JMethod method : jsoType.methods) {
+      if (!method.isStatic()) {
+        virtualJsoMethods.add(method);
+      }
+    }
+
+    if (virtualJsoMethods.isEmpty()) {
+      return;
+    }
+
+    CreateStaticImplsVisitor creator = new CreateStaticImplsVisitor();
+    for (JMethod method : virtualJsoMethods) {
+      // Ensure staticImpls exist for any instance methods.
+      JMethod jsoStaticImpl = program.getStaticImpl(method);
+      if (jsoStaticImpl == null) {
+        creator.accept(method);
+        jsoStaticImpl = program.getStaticImpl(method);
+        assert (jsoStaticImpl != null);
+      }
+
+      // Find the object method this instance method overrides.
+      JMethod objectOverride = findObjectOverride(method);
+      if (objectOverride != null) {
+        JMethod devirtualizer = createDevirtualMethod(objectOverride,
+            jsoStaticImpl);
+        devirtualMethods.add(devirtualizer);
+        objectMethodToJsoMethod.put(objectOverride, devirtualizer);
+      }
+    }
+
+    RewriteVirtualDispatches rewriter = new RewriteVirtualDispatches();
+    rewriter.accept(program);
+    assert (rewriter.didChange());
+  }
+
+  /**
+   * Finds the object method this method overrides.
+   */
+  private JMethod findObjectOverride(JMethod method) {
+    Set<JMethod> overrides = program.typeOracle.getAllRealOverrides(method);
+    JMethod objectOverride = null;
+    for (JMethod override : overrides) {
+      if (override.getEnclosingType() == program.getTypeJavaLangObject()) {
+        objectOverride = override;
+        break;
+      }
+    }
+    return objectOverride;
+  }
+
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java b/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java
index e17dc6c..4ee80f6 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java
@@ -50,14 +50,12 @@
  * of itself, but it opens the door to other optimizations. The basic idea is
  * that you look for calls to instance methods that are not actually
  * polymorphic. In other words, the target method is (effectively) final, not
- * overriden anywhere in the compilation. We rewrite the single instance method
+ * overridden anywhere in the compilation. We rewrite the single instance method
  * as a static method that contains the implementation plus an instance method
  * that delegates to the static method. Then we update any call sites to call
  * the static method instead. This opens the door to further optimizations,
  * reduces use of the long "this" keyword in the resulting JavaScript, and in
  * most cases the polymorphic version can be pruned later.
- * 
- * TODO(later): make this work on JSNI methods!
  */
 public class MakeCallsStatic {
 
@@ -67,7 +65,7 @@
    * it. Sometimes the instance method can be pruned later since we update all
    * non-polymorphic call sites.
    */
-  private class CreateStaticImplsVisitor extends JVisitor {
+  static class CreateStaticImplsVisitor extends JVisitor {
 
     /**
      * When code is moved from an instance method to a static method, all
@@ -112,15 +110,15 @@
       @Override
       public void endVisit(JParameterRef x, Context ctx) {
         JParameter param = varMap.get(x.getTarget());
-        JParameterRef paramRef = new JParameterRef(program, x.getSourceInfo(),
-            param);
+        JParameterRef paramRef = new JParameterRef(x.getProgram(),
+            x.getSourceInfo(), param);
         ctx.replaceMe(paramRef);
       }
 
       @Override
       public void endVisit(JThisRef x, Context ctx) {
-        JParameterRef paramRef = new JParameterRef(program, x.getSourceInfo(),
-            thisParam);
+        JParameterRef paramRef = new JParameterRef(x.getProgram(),
+            x.getSourceInfo(), thisParam);
         ctx.replaceMe(paramRef);
       }
     }
@@ -141,6 +139,7 @@
        * Don't use the JProgram helper because it auto-adds the new method to
        * its enclosing class.
        */
+      JProgram program = x.getProgram();
       JMethod newMethod = new JMethod(program, sourceInfo, newName,
           enclosingType, returnType, false, true, true, x.isPrivate());
 
@@ -266,17 +265,7 @@
         return;
       }
 
-      // Update the call site
-      JMethodCall newCall = new JMethodCall(program, x.getSourceInfo(), null,
-          newMethod);
-
-      // The qualifier becomes the first arg
-      newCall.getArgs().add(x.getInstance());
-      // Copy the rest of the args
-      for (int i = 0; i < x.getArgs().size(); ++i) {
-        newCall.getArgs().add(x.getArgs().get(i));
-      }
-      ctx.replaceMe(newCall);
+      ctx.replaceMe(makeStaticCall(x, newMethod));
     }
   }
 
@@ -284,7 +273,18 @@
     return new MakeCallsStatic(program).execImpl();
   }
 
-  public Set<JMethod> toBeMadeStatic = new HashSet<JMethod>();
+  static JMethodCall makeStaticCall(JMethodCall x, JMethod newMethod) {
+    // Update the call site
+    JMethodCall newCall = new JMethodCall(x.getProgram(), x.getSourceInfo(),
+        null, newMethod);
+
+    // The qualifier becomes the first arg
+    newCall.getArgs().add(x.getInstance());
+    newCall.getArgs().addAll(x.getArgs());
+    return newCall;
+  }
+
+  protected Set<JMethod> toBeMadeStatic = new HashSet<JMethod>();
 
   private final JProgram program;
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java b/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
index e0257e7..186890f 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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,6 +23,7 @@
 import com.google.gwt.dev.jjs.ast.JBinaryOperation;
 import com.google.gwt.dev.jjs.ast.JBinaryOperator;
 import com.google.gwt.dev.jjs.ast.JBlock;
+import com.google.gwt.dev.jjs.ast.JCastOperation;
 import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JExpression;
 import com.google.gwt.dev.jjs.ast.JField;
@@ -619,11 +620,26 @@
     }
 
     @Override
+    public boolean visit(JCastOperation x, Context ctx) {
+      // Rescue any JavaScriptObject type that is the target of a cast.
+      JType targetType = x.getCastType();
+      if (program.isJavaScriptObject(targetType)) {
+        rescue((JReferenceType) targetType, true, true);
+      }
+      return true;
+    }
+
+    @Override
     public boolean visit(JMethodCall call, Context ctx) {
       JMethod target = call.getTarget();
-      // JLS 12.4.1: references to static methods rescue the enclosing class
-      if (target.isStatic()) {
-        rescue(target.getEnclosingType(), true, false);
+      JReferenceType enclosingType = target.getEnclosingType();
+      if (program.isJavaScriptObject(enclosingType)) {
+        // Calls to JavaScriptObject types rescue those types.
+        boolean instance = !target.isStatic() || program.isStaticImpl(target);
+        rescue(enclosingType, true, instance);
+      } else if (target.isStatic()) {
+        // JLS 12.4.1: references to static methods rescue the enclosing class
+        rescue(enclosingType, true, false);
       }
       rescue(target);
       return true;
@@ -716,20 +732,17 @@
     /**
      * Subclasses of JavaScriptObject are never instantiated directly. They are
      * created "magically" when a JSNI method passes a reference to an existing
-     * JS object into Java code. The point at which a subclass of JSO is passed
-     * into Java code constitutes "instantiation". We must identify these points
-     * and trigger a rescue and instantiation of that particular JSO subclass.
+     * JS object into Java code. If any point in the program can pass a value
+     * from JS into Java which could potentially be cast to JavaScriptObject, we
+     * must rescue JavaScriptObject.
      * 
      * @param type The type of the value passing from Java to JavaScript.
      * @see com.google.gwt.core.client.JavaScriptObject
      */
     private void maybeRescueJavaScriptObjectPassingIntoJava(JType type) {
-      if (type instanceof JReferenceType) {
-        JReferenceType refType = (JReferenceType) type;
-        if (program.typeOracle.canTriviallyCast(refType,
-            program.getJavaScriptObject())) {
-          rescue(refType, true, true);
-        }
+      if (program.isJavaScriptObject(type)
+          || type == program.getTypeJavaLangObject()) {
+        rescue((JReferenceType) type, true, true);
       }
     }
 
diff --git a/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java b/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
index 3690d82..6ed5e5e 100644
--- a/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
+++ b/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
@@ -18,21 +18,34 @@
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JParameter;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
 import com.google.gwt.dev.jdt.ByteCodeCompiler;
 import com.google.gwt.dev.jdt.CacheManager;
+import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter;
+import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.InstanceMethodMapper;
 import com.google.gwt.util.tools.Utility;
 
+import org.apache.commons.collections.map.ReferenceMap;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 /**
+ * An isolated {@link ClassLoader} for running all user code. All user files are
+ * compiled from source code byte a {@link ByteCodeCompiler}. After
+ * compilation, some byte code rewriting is performed to support
+ * <code>JavaScriptObject</code> and its subtypes.
  * 
- * TODO : we should refactor this class to move the getClassInfoByDispId,
+ * TODO: we should refactor this class to move the getClassInfoByDispId,
  * getDispId, getMethodDispatch and putMethodDispatch into a separate entity
  * since they really do not interact with the CompilingClassLoader
  * functionality.
@@ -85,9 +98,6 @@
     public synchronized int getDispId(String jsniMemberRef) {
       /*
        * Map JS toString() onto the Java toString() method.
-       * 
-       * TODO : is it true that tostring is valid in JavaScript? JavaScript is
-       * case sensitive.
        */
       if (jsniMemberRef.equals("toString")) {
         jsniMemberRef = "@java.lang.Object::toString()";
@@ -112,6 +122,13 @@
         String memberName = jsniMemberRef.substring(endClassName + 2);
         int memberId = dispClassInfo.getMemberId(memberName);
 
+        if (memberId < 0) {
+          logger.log(TreeLogger.WARN, "Member '" + memberName
+              + "' in JSNI reference '" + jsniMemberRef
+              + "' could not be found; expect subsequent failures",
+              new NoSuchFieldError(memberName));
+        }
+
         return synthesizeDispId(dispClassInfo.getClassId(), memberId);
       }
 
@@ -195,6 +212,11 @@
         return null;
       }
 
+      // Map JSO type references to the appropriate impl class.
+      if (classRewriter.isJsoIntf(cls.getName())) {
+        cls = getClassFromBinaryName(cls.getName() + "$");
+      }
+
       /*
        * we need to create a new DispatchClassInfo since we have never seen this
        * class before under any source or binary class name
@@ -225,6 +247,84 @@
     }
   }
 
+  private class MyMethodDeclarationMapper implements InstanceMethodMapper {
+
+    private final Map<String, Set<JClassType>> signatureToDeclaringClasses = new HashMap<String, Set<JClassType>>();
+
+    public MyMethodDeclarationMapper(Set<JClassType> jsoTypes,
+        JClassType javaLangObject) {
+      // Populate the map.
+      for (JClassType type : jsoTypes) {
+        for (JMethod method : type.getMethods()) {
+          if (!method.isStatic()) {
+            String signature = createSignature(method);
+            Set<JClassType> declaringClasses = signatureToDeclaringClasses.get(signature);
+            if (declaringClasses == null) {
+              declaringClasses = new HashSet<JClassType>();
+              signatureToDeclaringClasses.put(signature, declaringClasses);
+            }
+            declaringClasses.add(type);
+          }
+        }
+      }
+      // Object clobbers everything.
+      for (JMethod method : javaLangObject.getMethods()) {
+        if (!method.isStatic()) {
+          String signature = createSignature(method);
+          Set<JClassType> declaringClasses = new HashSet<JClassType>();
+          signatureToDeclaringClasses.put(signature, declaringClasses);
+          declaringClasses.add(javaLangObject);
+        }
+      }
+    }
+
+    public String findDeclaringClass(String desc, String signature) {
+      // Lookup the method.
+      Set<JClassType> declaringClasses = signatureToDeclaringClasses.get(signature);
+      if (declaringClasses.size() == 1) {
+        // Shortcut: if there's only one answer, it must be right.
+        return createDescriptor(declaringClasses.iterator().next());
+      }
+      // Must check for assignability.
+      String sourceName = desc.replace('/', '.');
+      sourceName = sourceName.replace('$', '.');
+      JClassType declaredType = typeOracle.findType(sourceName);
+
+      // Check if I declare this directly.
+      if (declaringClasses.contains(declaredType)) {
+        return desc;
+      }
+
+      // Check to see what type I am assignable to.
+      for (JClassType possibleSupertype : declaringClasses) {
+        if (declaredType.isAssignableTo(possibleSupertype)) {
+          return createDescriptor(possibleSupertype);
+        }
+      }
+      throw new IllegalArgumentException("Could not resolve signature '"
+          + signature + "' from class '" + desc + "'");
+    }
+
+    private String createDescriptor(JClassType type) {
+      String jniSignature = type.getJNISignature();
+      return jniSignature.substring(1, jniSignature.length() - 1);
+    }
+
+    private String createSignature(JMethod method) {
+      StringBuffer sb = new StringBuffer(method.getName());
+      sb.append('(');
+      for (JParameter param : method.getParameters()) {
+        sb.append(param.getType().getJNISignature());
+      }
+      sb.append(')');
+      sb.append(method.getReturnType().getJNISignature());
+      String signature = sb.toString();
+      return signature;
+    }
+  }
+
+  private final HostedModeClassRewriter classRewriter;
+
   private final ByteCodeCompiler compiler;
 
   private final DispatchClassInfoOracle dispClassInfoOracle = new DispatchClassInfoOracle();
@@ -235,6 +335,9 @@
 
   private final TypeOracle typeOracle;
 
+  private final ReferenceMap weakJsoCache = new ReferenceMap(ReferenceMap.HARD,
+      ReferenceMap.WEAK);
+
   public CompilingClassLoader(TreeLogger logger, ByteCodeCompiler compiler,
       TypeOracle typeOracle) throws UnableToCompleteException {
     super(null);
@@ -276,6 +379,41 @@
       }
     }
     compiler.removeStaleByteCode(logger);
+
+    // Create a class rewriter based on all the subtypes of the JSO class.
+    JClassType jsoType = typeOracle.findType(JsValueGlue.JSO_CLASS);
+    if (jsoType != null) {
+
+      // Create a set of binary names.
+      Set<JClassType> jsoTypes = new HashSet<JClassType>();
+      JClassType[] jsoSubtypes = jsoType.getSubtypes();
+      Collections.addAll(jsoTypes, jsoSubtypes);
+      jsoTypes.add(jsoType);
+
+      Set<String> jsoTypeNames = new HashSet<String>();
+      for (JClassType type : jsoTypes) {
+        jsoTypeNames.add(getBinaryName(type));
+      }
+
+      MyMethodDeclarationMapper mapper = new MyMethodDeclarationMapper(
+          jsoTypes, typeOracle.getJavaLangObject());
+      classRewriter = new HostedModeClassRewriter(jsoTypeNames, mapper);
+    } else {
+      // If we couldn't find the JSO class, we don't need to do any rewrites.
+      classRewriter = null;
+    }
+  }
+
+  /**
+   * Retrieves the mapped JSO for a given unique id, provided the id was
+   * previously cached and the JSO has not been garbage collected.
+   * 
+   * @param uniqueId the previously stored unique id
+   * @return the mapped JSO, or <code>null</code> if the id was not previously
+   *         mapped or if the JSO has been garbage collected
+   */
+  public Object getCachedJso(int uniqueId) {
+    return weakJsoCache.get(uniqueId);
   }
 
   /**
@@ -305,6 +443,17 @@
     }
   }
 
+  /**
+   * Weakly caches a given JSO by unique id. A cached JSO can be looked up by
+   * unique id until it is garbage collected.
+   * 
+   * @param uniqueId a unique id associated with the JSO
+   * @param jso the value to cache
+   */
+  public void putCachedJso(int uniqueId, Object jso) {
+    weakJsoCache.put(uniqueId, jso);
+  }
+
   public void putMethodDispatch(MethodAdaptor method, Object methodDispatch) {
     synchronized (methodToDispatch) {
       methodToDispatch.put(method, methodDispatch);
@@ -334,10 +483,17 @@
     }
 
     // Get the bytes, compiling if necessary.
-    // Define the class from bytes.
-    //
+    byte[] classBytes;
     try {
-      byte[] classBytes = compiler.getClassBytes(logger, className);
+      // A JSO impl class needs the class bytes for the original class.
+      String lookupClassName = className;
+      if (classRewriter != null && classRewriter.isJsoImpl(className)) {
+        lookupClassName = className.substring(0, className.length() - 1);
+      }
+      classBytes = compiler.getClassBytes(logger, lookupClassName);
+      if (classRewriter != null) {
+        classBytes = classRewriter.rewrite(className, classBytes);
+      }
       return defineClass(className, classBytes, 0, classBytes.length);
     } catch (UnableToCompleteException e) {
       throw new ClassNotFoundException(className);
@@ -345,6 +501,7 @@
   }
 
   void clear() {
+    weakJsoCache.clear();
     dispClassInfoOracle.clear();
 
     synchronized (methodToDispatch) {
@@ -352,6 +509,12 @@
     }
   }
 
+  private String getBinaryName(JClassType type) {
+    String name = type.getPackage().getName() + '.';
+    name += type.getName().replace('.', '$');
+    return name;
+  }
+
   private byte[] getClassBytesFromStream(InputStream is) throws IOException {
     try {
       byte classBytes[] = new byte[is.available()];
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 8526c7c..42eadf4 100644
--- a/dev/core/src/com/google/gwt/dev/shell/JavaScriptHost.java
+++ b/dev/core/src/com/google/gwt/dev/shell/JavaScriptHost.java
@@ -82,14 +82,6 @@
   }
 
   /**
-   * Invoke a native JavaScript function that returns a handle value.
-   */
-  public static Object invokeNativeHandle(String name, Object jthis,
-      Class<?> returnType, Class<?>[] types, Object[] args) throws Throwable {
-    return sHost.invokeNativeHandle(name, jthis, returnType, types, args);
-  }
-
-  /**
    * Invoke a native JavaScript function that returns an integer value.
    */
   public static int invokeNativeInt(String name, Object jthis,
@@ -122,14 +114,6 @@
   }
 
   /**
-   * Invoke a native JavaScript function that returns a string value.
-   */
-  public static String invokeNativeString(String name, Object jthis,
-      Class<?>[] types, Object[] args) throws Throwable {
-    return sHost.invokeNativeString(name, jthis, types, args);
-  }
-
-  /**
    * Invoke a native JavaScript function that returns no value.
    */
   public static void invokeNativeVoid(String name, Object jthis,
diff --git a/dev/core/src/com/google/gwt/dev/shell/JsValue.java b/dev/core/src/com/google/gwt/dev/shell/JsValue.java
index 6b2d9f4..7cda3d6 100644
--- a/dev/core/src/com/google/gwt/dev/shell/JsValue.java
+++ b/dev/core/src/com/google/gwt/dev/shell/JsValue.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Google Inc.
+ * Copyright 2008 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
@@ -105,6 +105,17 @@
   public abstract int getInt();
 
   /**
+   * Returns a unique value corresponding to the underlying JavaScript object.
+   * In general, two different JsValues will return the same value IFF the
+   * underlying JavaScript objects are identical (===).
+   * 
+   * @return a unique number corresponding to the underlying object, or
+   *         <code>0</code> if {@link #isJavaScriptObject()} is
+   *         <code>false</code>
+   */
+  public abstract int getJavaScriptObjectPointer();
+
+  /**
    * Get the value of the object as a double. May attempt to convert the value
    * to a double if it is not a double.
    * 
@@ -113,9 +124,9 @@
   public abstract double getNumber();
 
   /**
-   * Get the value of the object as a string. Tries very hard to return a
-   * reasonable value for any underyling type, but this should only be used for
-   * human consumption as the exact format is not reliable across platforms.
+   * Get the value of the object as a string. Will coerce the underlying type to
+   * a string, but stable cross-platform behavior is only guaranteed when
+   * {@link #isString()} is <code>true</code>.
    * 
    * @return the value of the underlying object as a string
    */
@@ -246,17 +257,6 @@
   public abstract void setValue(JsValue other);
 
   /**
-   * Wrap a Java method as a JavaScript function pointer.
-   * 
-   * @param string the name of the method
-   * @param dispMethod the DispatchMethod object describing the method tow wrap
-   */
-  // TODO(jat): platform-independent version of this?
-  // The problem is that each platform has different conventions for passing
-  // JavaScript values back into a Java method.
-  // public abstract void setWrappedFunction(String string,
-  // DispatchMethod dispMethod);
-  /**
    * Set the JS object to the supplied object, which will be wrapped in a
    * platform-dependent JavaScript class.
    * 
diff --git a/dev/core/src/com/google/gwt/dev/shell/JsValueGlue.java b/dev/core/src/com/google/gwt/dev/shell/JsValueGlue.java
index 4382e31..b93250f 100644
--- a/dev/core/src/com/google/gwt/dev/shell/JsValueGlue.java
+++ b/dev/core/src/com/google/gwt/dev/shell/JsValueGlue.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -26,60 +26,10 @@
  * Glue layer that performs GWT-specific operations on JsValues. Used to isolate
  * HostedModeExceptions/etc from JsValue code
  */
-public class JsValueGlue {
+public final class JsValueGlue {
+  public static final String HOSTED_MODE_REFERENCE = "hostedModeReference";
   public static final String JSO_CLASS = "com.google.gwt.core.client.JavaScriptObject";
-
-  /**
-   * Create a JavaScriptObject instance referring to this JavaScript object.
-   * 
-   * The caller is responsible for ensuring that the requested type is a
-   * subclass of JavaScriptObject.
-   * 
-   * @param type The subclass of JavaScriptObject to create
-   * @return the constructed JavaScriptObject
-   */
-  public static <T> T createJavaScriptObject(JsValue value,
-      Class<? extends T> type) {
-    try {
-      // checkThread();
-      if (!value.isJavaScriptObject()) {
-        throw new RuntimeException(
-            "Only Object type JavaScript objects can be made into JavaScriptObject");
-      }
-
-      /* find the JavaScriptObject type, while verifying this is a subclass */
-      Class<?> jsoType = getJavaScriptObjectSuperclass(type);
-      if (jsoType == null) {
-        throw new RuntimeException("Requested type " + type.getName()
-            + " not a subclass of JavaScriptObject");
-      }
-
-      /* create the object using the default constructor */
-      Constructor<? extends T> ctor = type.getDeclaredConstructor();
-      ctor.setAccessible(true);
-      T jso = ctor.newInstance();
-
-      /* set the hostedModeReference field to this JsValue using reflection */
-      Field referenceField = jsoType.getDeclaredField("hostedModeReference");
-      referenceField.setAccessible(true);
-      referenceField.set(jso, value);
-      return jso;
-    } catch (InstantiationException e) {
-      throw new RuntimeException("Error creating JavaScript object", e);
-    } catch (IllegalAccessException e) {
-      throw new RuntimeException("Error creating JavaScript object", e);
-    } catch (SecurityException e) {
-      throw new RuntimeException("Error creating JavaScript object", e);
-    } catch (NoSuchFieldException e) {
-      throw new RuntimeException("Error creating JavaScript object", e);
-    } catch (NoSuchMethodException e) {
-      throw new RuntimeException("Error creating JavaScript object", e);
-    } catch (IllegalArgumentException e) {
-      throw new RuntimeException("Error creating JavaScript object", e);
-    } catch (InvocationTargetException e) {
-      throw new RuntimeException("Error creating JavaScript object", e);
-    }
-  }
+  public static final String JSO_IMPL_CLASS = "com.google.gwt.core.client.JavaScriptObject$";
 
   /**
    * Return an object containing the value JavaScript object as a specified
@@ -92,73 +42,52 @@
    * @throws HostedModeException if the JavaScript object is not assignable to
    *           the supplied type.
    */
-  public static <T> T get(JsValue value, Class<? extends T> type,
-      String msgPrefix) {
-    double doubleVal;
-    if (value.isNull()) {
-      return null;
-    }
+  @SuppressWarnings("unchecked")
+  public static <T> T get(JsValue value, CompilingClassLoader cl,
+      Class<T> type, String msgPrefix) {
+
     if (value.isUndefined()) {
       // undefined is never legal to return from JavaScript into Java
       throw new HostedModeException(msgPrefix
           + ": JavaScript undefined, expected " + type.getName());
     }
-    if (value.isWrappedJavaObject()) {
-      T origObject = (T) value.getWrappedJavaObject();
-      if (!type.isAssignableFrom(origObject.getClass())) {
-        throw new HostedModeException(msgPrefix + ": Java object of type "
-            + origObject.getClass().getName() + ", expected " + type.getName());
+
+    if (type.isPrimitive()) {
+      if (value.isNull()) {
+        throw new HostedModeException("Expected primitive type " + type
+            + "; actual value was null");
       }
-      return origObject;
-    }
-    if (getJavaScriptObjectSuperclass(type) != null) {
-      if (!value.isJavaScriptObject()) {
-        throw new HostedModeException(msgPrefix + ": JS object of type "
-            + value.getTypeString() + ", expected " + type.getName());
-      }
-      return createJavaScriptObject(value, type);
-    }
-    switch (TypeInfo.classifyType(type)) {
-      case TypeInfo.TYPE_WRAP_BOOLEAN:
-      case TypeInfo.TYPE_PRIM_BOOLEAN:
+      if (type == Boolean.TYPE) {
         if (!value.isBoolean()) {
           throw new HostedModeException(msgPrefix + ": JS value of type "
               + value.getTypeString() + ", expected boolean");
         }
         return (T) Boolean.valueOf(value.getBoolean());
-
-      case TypeInfo.TYPE_WRAP_BYTE:
-      case TypeInfo.TYPE_PRIM_BYTE:
-        return (T) new Byte((byte) getIntRange(value, Byte.MIN_VALUE,
+      } else if (type == Byte.TYPE) {
+        return (T) Byte.valueOf((byte) getIntRange(value, Byte.MIN_VALUE,
             Byte.MAX_VALUE, "byte", msgPrefix));
-
-      case TypeInfo.TYPE_WRAP_CHAR:
-      case TypeInfo.TYPE_PRIM_CHAR:
-        return (T) new Character((char) getIntRange(value, Character.MIN_VALUE,
-            Character.MAX_VALUE, "char", msgPrefix));
-
-      case TypeInfo.TYPE_WRAP_DOUBLE:
-      case TypeInfo.TYPE_PRIM_DOUBLE:
+      } else if (type == Character.TYPE) {
+        return (T) Character.valueOf((char) getIntRange(value,
+            Character.MIN_VALUE, Character.MAX_VALUE, "char", msgPrefix));
+      } else if (type == Double.TYPE) {
         if (!value.isNumber()) {
           throw new HostedModeException(msgPrefix + ": JS value of type "
               + value.getTypeString() + ", expected double");
         }
-        return (T) new Double(value.getNumber());
-
-      case TypeInfo.TYPE_WRAP_FLOAT:
-      case TypeInfo.TYPE_PRIM_FLOAT:
+        return (T) Double.valueOf(value.getNumber());
+      } else if (type == Float.TYPE) {
         if (!value.isNumber()) {
           throw new HostedModeException(msgPrefix + ": JS value of type "
               + value.getTypeString() + ", expected float");
         }
-        doubleVal = value.getNumber();
+        double doubleVal = value.getNumber();
 
         // Check for small changes near MIN_VALUE and replace with the
-        // actual endpoint value, in case it is being used as a sentinel
+        // actual end point value, in case it is being used as a sentinel
         // value. This test works by the subtraction result rounding off to
         // zero if the delta is not representable in a float.
         // TODO(jat): add similar test for MAX_VALUE if we have a JS
-        // platform that munges the value while converting to/from strings.
+        // platform that alters the value while converting to/from strings.
         if ((float) (doubleVal - Float.MIN_VALUE) == 0.0f) {
           doubleVal = Float.MIN_VALUE;
         }
@@ -172,20 +101,16 @@
           throw new HostedModeException(msgPrefix + ": JS value " + doubleVal
               + " out of range for a float");
         }
-        return (T) new Float(floatVal);
-
-      case TypeInfo.TYPE_WRAP_INT:
-      case TypeInfo.TYPE_PRIM_INT:
-        return (T) new Integer(getIntRange(value, Integer.MIN_VALUE,
+        return (T) Float.valueOf(floatVal);
+      } else if (type == Integer.TYPE) {
+        return (T) Integer.valueOf(getIntRange(value, Integer.MIN_VALUE,
             Integer.MAX_VALUE, "int", msgPrefix));
-
-      case TypeInfo.TYPE_WRAP_LONG:
-      case TypeInfo.TYPE_PRIM_LONG:
+      } else if (type == Long.TYPE) {
         if (!value.isNumber()) {
           throw new HostedModeException(msgPrefix + ": JS value of type "
               + value.getTypeString() + ", expected long");
         }
-        doubleVal = value.getNumber();
+        double doubleVal = value.getNumber();
         if (doubleVal < Long.MIN_VALUE || doubleVal > Long.MAX_VALUE) {
           throw new HostedModeException(msgPrefix + ": JS double value "
               + doubleVal + " out of range for a long");
@@ -197,26 +122,24 @@
           ModuleSpace.getLogger().log(TreeLogger.WARN,
               msgPrefix + ": Loss of precision converting double to long", null);
         }
-        return (T) new Long(longVal);
-
-      case TypeInfo.TYPE_WRAP_SHORT:
-      case TypeInfo.TYPE_PRIM_SHORT:
-        return (T) new Short((short) getIntRange(value, Short.MIN_VALUE,
+        return (T) Long.valueOf(longVal);
+      } else if (type == Short.TYPE) {
+        return (T) Short.valueOf((short) getIntRange(value, Short.MIN_VALUE,
             Short.MAX_VALUE, "short", msgPrefix));
+      }
+    }
 
-      case TypeInfo.TYPE_WRAP_STRING:
-        if (!value.isString()) {
-          throw new HostedModeException(msgPrefix + ": JS value of type "
-              + value.getTypeString() + ", expected string");
-        }
-        return (T) value.getString();
-
-      case TypeInfo.TYPE_USER:
-        if (value.isString()) {
-          return (T) value.getString();
-        }
-        // if it isn't a String, it's an error, break to error
-        break;
+    if (value.isNull()) {
+      return null;
+    }
+    if (value.isWrappedJavaObject()) {
+      return type.cast(value.getWrappedJavaObject());
+    }
+    if (value.isString()) {
+      return type.cast(value.getString());
+    }
+    if (value.isJavaScriptObject()) {
+      return type.cast(createJavaScriptObject(value, cl));
     }
 
     // Just don't know what do to with this.
@@ -226,81 +149,50 @@
   }
 
   /**
-   * Returns the underlying JsValue from a JavaScriptObject instance.
-   * 
-   * The tricky part is that it is in a different classloader so therefore can't
-   * be specified directly. The type is specified as Object, and reflection is
-   * used to retrieve the hostedModeReference field.
-   * 
-   * @param jso the instance of JavaScriptObject to retrieve the JsValue from.
-   * @return the JsValue representing the JavaScript object
-   */
-  public static JsValue getUnderlyingObject(Object jso) {
-    try {
-      /*
-       * verify that jso is assignable to
-       * com.google.gwt.core.client.JavaScriptObject
-       */
-      Class<?> type = getJavaScriptObjectSuperclass(jso.getClass());
-
-      if (type == null) {
-        throw new HostedModeException(
-            "Underlying JSO not a subclass of JavaScriptObject");
-      }
-
-      Field referenceField = type.getDeclaredField("hostedModeReference");
-      referenceField.setAccessible(true);
-      return (JsValue) referenceField.get(jso);
-    } catch (IllegalAccessException e) {
-      throw new RuntimeException("Error reading handle", e);
-    } catch (SecurityException e) {
-      throw new RuntimeException("Error reading handle", e);
-    } catch (NoSuchFieldException e) {
-      throw new RuntimeException("Error reading handle", e);
-    }
-  }
-
-  /**
    * Set the underlying value.
    * 
    * @param value JsValue to set
    * @param type static type of the object
    * @param obj the object to store in the JS value
    */
-  public static <T> void set(JsValue value, CompilingClassLoader cl,
-      Class<?> type, T obj) {
-    if (obj == null) {
-      value.setNull();
-    } else if (type.equals(String.class)) {
-      value.setString((String) obj);
-    } else if (type.equals(boolean.class)) {
-      value.setBoolean(((Boolean) obj).booleanValue());
-    } else if (type.equals(short.class)) {
-      value.setInt(((Short) obj).shortValue());
-    } else if (type.equals(int.class)) {
-      value.setInt(((Integer) obj).intValue());
-    } else if (type.equals(byte.class)) {
-      value.setInt(((Byte) obj).byteValue());
-    } else if (type.equals(char.class)) {
-      value.setInt(((Character) obj).charValue());
-    } else if (type.equals(long.class)) {
-      long longVal = ((Long) obj).longValue();
-      double doubleVal = longVal;
-      if ((long) doubleVal != longVal) {
-        // TODO(jat): should this be an error or exception?
-        ModuleSpace.getLogger().log(TreeLogger.WARN,
-            "Loss of precision converting long to double", null);
+  public static void set(JsValue value, CompilingClassLoader cl, Class<?> type,
+      Object obj) {
+    if (type.isPrimitive()) {
+      if (type == Boolean.TYPE) {
+        value.setBoolean(((Boolean) obj).booleanValue());
+      } else if (type == Byte.TYPE) {
+        value.setInt(((Byte) obj).byteValue());
+      } else if (type == Character.TYPE) {
+        value.setInt(((Character) obj).charValue());
+      } else if (type == Double.TYPE) {
+        value.setDouble(((Double) obj).doubleValue());
+      } else if (type == Float.TYPE) {
+        value.setDouble(((Float) obj).floatValue());
+      } else if (type == Integer.TYPE) {
+        value.setInt(((Integer) obj).intValue());
+      } else if (type == Long.TYPE) {
+        long longVal = ((Long) obj).longValue();
+        double doubleVal = longVal;
+        if ((long) doubleVal != longVal) {
+          // TODO(jat): should this be an error or exception?
+          ModuleSpace.getLogger().log(TreeLogger.WARN,
+              "Loss of precision converting long to double", null);
+        }
+        value.setDouble(doubleVal);
+      } else if (type == Short.TYPE) {
+        value.setInt(((Short) obj).shortValue());
+      } else if (type == Void.TYPE) {
+        value.setUndefined();
+      } else {
+        throw new HostedModeException("Cannot marshal primitive type " + type);
       }
-      value.setDouble(doubleVal);
-    } else if (type.equals(float.class)) {
-      value.setDouble(((Float) obj).floatValue());
-    } else if (type.equals(double.class)) {
-      value.setDouble(((Double) obj).doubleValue());
+    } else if (obj == null) {
+      value.setNull();
     } else {
       // not a boxed primitive
       try {
-        Class<?> jso = Class.forName(JSO_CLASS, true, cl);
-        if (jso.isAssignableFrom(type) && jso.isAssignableFrom(obj.getClass())) {
+        Class<?> jsoType = Class.forName(JSO_IMPL_CLASS, true, cl);
+        if (jsoType == obj.getClass()) {
           JsValue jsObject = getUnderlyingObject(obj);
           value.setValue(jsObject);
           return;
@@ -310,15 +202,73 @@
         // don't have to worry about o being one
       }
 
-      // Fallthrough case: Object.
-      if (!type.isAssignableFrom(obj.getClass())) {
+      // Fall through case: Object.
+      if (!type.isInstance(obj)) {
         throw new HostedModeException("object is of type "
             + obj.getClass().getName() + ", expected " + type.getName());
       }
-      value.setWrappedJavaObject(cl, obj);
+      if (obj instanceof String) {
+        value.setString((String) obj);
+      } else {
+        value.setWrappedJavaObject(cl, obj);
+      }
     }
   }
 
+  /**
+   * Create a JavaScriptObject instance referring to this JavaScript object.
+   * 
+   * @param classLoader the classLoader to create from
+   * @return the constructed JavaScriptObject
+   */
+  private static Object createJavaScriptObject(JsValue value,
+      CompilingClassLoader classLoader) {
+    Throwable caught;
+    try {
+      // checkThread();
+      if (!value.isJavaScriptObject()) {
+        throw new RuntimeException(
+            "Only Object type JavaScript objects can be made into JavaScriptObject");
+      }
+
+      // See if there's already a wrapper object (assures identity comparison).
+      Object jso = classLoader.getCachedJso(value.getJavaScriptObjectPointer());
+      if (jso != null) {
+        return jso;
+      }
+
+      // Instantiate the JSO class.
+      Class<?> jsoType = Class.forName(JSO_IMPL_CLASS, true, classLoader);
+      Constructor<?> ctor = jsoType.getDeclaredConstructor();
+      ctor.setAccessible(true);
+      jso = ctor.newInstance();
+
+      // Set the reference field to this JsValue using reflection.
+      Field referenceField = jsoType.getField(HOSTED_MODE_REFERENCE);
+      referenceField.set(jso, value);
+
+      classLoader.putCachedJso(value.getJavaScriptObjectPointer(), jso);
+      return jso;
+    } catch (InstantiationException e) {
+      caught = e;
+    } catch (IllegalAccessException e) {
+      caught = e;
+    } catch (SecurityException e) {
+      caught = e;
+    } catch (NoSuchMethodException e) {
+      caught = e;
+    } catch (IllegalArgumentException e) {
+      caught = e;
+    } catch (InvocationTargetException e) {
+      caught = e;
+    } catch (ClassNotFoundException e) {
+      caught = e;
+    } catch (NoSuchFieldException e) {
+      caught = e;
+    }
+    throw new RuntimeException("Error creating JavaScript object", caught);
+  }
+
   private static int getIntRange(JsValue value, int low, int high,
       String typeName, String msgPrefix) {
     int intVal;
@@ -347,19 +297,31 @@
   }
 
   /**
-   * Verify that the supplied class is a subclass of
-   * com.google.gwt.core.client.JavaScriptObject, and return the
-   * JavaScriptObject class if it is. This is required since JavaScriptObject
-   * actually lives in a different classloader and can't be referenced directly.
+   * Returns the underlying JsValue from a JavaScriptObject instance.
    * 
-   * @param type class to test
-   * @return the JavaScriptObject class object if it is a subclass, or null if
-   *         not.
+   * The tricky part is that it is in a different ClassLoader so therefore can't
+   * be specified directly. The type is specified as Object, and reflection is
+   * used to retrieve the reference field.
+   * 
+   * @param jso the instance of JavaScriptObject to retrieve the JsValue from.
+   * @return the JsValue representing the JavaScript object
    */
-  private static Class<?> getJavaScriptObjectSuperclass(Class<?> type) {
-    while (type != null && !type.getName().equals(JSO_CLASS)) {
-      type = type.getSuperclass();
+  private static JsValue getUnderlyingObject(Object jso) {
+    Throwable caught;
+    try {
+      Field referenceField = jso.getClass().getField(HOSTED_MODE_REFERENCE);
+      referenceField.setAccessible(true);
+      return (JsValue) referenceField.get(jso);
+    } catch (IllegalAccessException e) {
+      caught = e;
+    } catch (SecurityException e) {
+      caught = e;
+    } catch (NoSuchFieldException e) {
+      caught = e;
     }
-    return type;
+    throw new RuntimeException("Error reading " + HOSTED_MODE_REFERENCE, caught);
+  }
+
+  private JsValueGlue() {
   }
 }
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 9ec1d9d..50805f5 100644
--- a/dev/core/src/com/google/gwt/dev/shell/JsniInjector.java
+++ b/dev/core/src/com/google/gwt/dev/shell/JsniInjector.java
@@ -23,7 +23,6 @@
 import com.google.gwt.core.ext.typeinfo.JParameter;
 import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
 import com.google.gwt.core.ext.typeinfo.JType;
-import com.google.gwt.core.ext.typeinfo.NotFoundException;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
 import com.google.gwt.dev.jdt.CompilationUnitProviderWithAlternateSource;
 import com.google.gwt.dev.js.ast.JsBlock;
@@ -45,37 +44,6 @@
 public class JsniInjector {
 
   /**
-   * A consolidated way to get all expected types and succeed or fail
-   * atomically.
-   */
-  private class CoreTypes {
-    static final String CLS_JSOBJECT = "JavaScriptObject";
-    static final String CLS_STRING = "String";
-    static final String PKG_JSOBJECT = "com.google.gwt.core.client";
-    static final String PKG_STRING = "java.lang";
-
-    public final JClassType javaLangString;
-
-    public final JClassType javaScriptObject;
-
-    public CoreTypes(TreeLogger logger) throws UnableToCompleteException {
-      javaScriptObject = getCoreType(logger, PKG_JSOBJECT, CLS_JSOBJECT);
-      javaLangString = getCoreType(logger, PKG_STRING, CLS_STRING);
-    }
-
-    private JClassType getCoreType(TreeLogger logger, String pkg, String cls)
-        throws UnableToCompleteException {
-      try {
-        return oracle.getType(pkg, cls);
-      } catch (NotFoundException e) {
-        String msg = "Unable to find core type '" + pkg + "." + cls + "'";
-        logger.log(TreeLogger.ERROR, msg, e);
-        throw new UnableToCompleteException();
-      }
-    }
-  }
-
-  /**
    * A chunk of replacement text and where to put it.
    */
   private static class Replacement implements Comparable<Replacement> {
@@ -104,8 +72,6 @@
     }
   }
 
-  private CoreTypes coreTypes;
-
   private final TypeOracle oracle;
 
   private final Map<JMethod, JsBlock> parsedJsByMethod = new IdentityHashMap<JMethod, JsBlock>();
@@ -121,11 +87,6 @@
     logger = logger.branch(TreeLogger.SPAM,
         "Checking for JavaScript native methods", null);
 
-    // Make sure the core types exist.
-    if (coreTypes == null) {
-      coreTypes = new CoreTypes(logger);
-    }
-
     // Analyze the source and build a list of changes.
     char[] source = cup.getSource();
     List<Replacement> changes = new ArrayList<Replacement>();
@@ -252,14 +213,8 @@
     // Write the Java call to the property invoke method, adding
     // downcasts where necessary.
     JType returnType = method.getReturnType();
-    boolean isJavaScriptObject = isJavaScriptObject(returnType);
-    JPrimitiveType primType;
-    if (isJavaScriptObject) {
-      // Add a downcast from Handle to the originally-declared type.
-      String returnTypeName = returnType.getParameterizedQualifiedSourceName();
-      sb.append("return (" + returnTypeName + ")" + Jsni.JAVASCRIPTHOST_NAME
-          + ".invokeNativeHandle");
-    } else if (null != (primType = returnType.isPrimitive())) {
+    JPrimitiveType primType = returnType.isPrimitive();
+    if (primType != null) {
       // Primitives have special overloads.
       char[] primTypeSuffix = primType.getSimpleSourceName().toCharArray();
       primTypeSuffix[0] = Character.toUpperCase(primTypeSuffix[0]);
@@ -270,10 +225,6 @@
       sb.append(Jsni.JAVASCRIPTHOST_NAME);
       sb.append(".");
       sb.append(invokeMethodName);
-    } else if (returnType == coreTypes.javaLangString) {
-      sb.append("return ");
-      sb.append(Jsni.JAVASCRIPTHOST_NAME);
-      sb.append(".invokeNativeString");
     } else {
       // Some reference type.
       // We need to add a downcast to the originally-declared type.
@@ -295,13 +246,6 @@
       sb.append("\", this, ");
     }
 
-    if (isJavaScriptObject) {
-      // Handle-oriented calls also need the return type as an argument.
-      String returnTypeName = returnType.getErasedType().getQualifiedSourceName();
-      sb.append(returnTypeName);
-      sb.append(".class, ");
-    }
-
     // Build an array of classes that tells the invoker how to adapt the
     // incoming arguments for calling into JavaScript.
     sb.append(Jsni.buildTypeList(method));
@@ -316,6 +260,8 @@
     sb.append("} catch (java.lang.Throwable __gwt_exception) {" + nl);
     sb.append("if (__gwt_exception instanceof java.lang.RuntimeException) throw (java.lang.RuntimeException) __gwt_exception;"
         + nl);
+    sb.append("if (__gwt_exception instanceof java.lang.Error) throw (java.lang.Error) __gwt_exception;"
+        + nl);
     JType[] throwTypes = method.getThrows();
     for (int i = 0; i < throwTypes.length; ++i) {
       String typeName = throwTypes[i].getQualifiedSourceName();
@@ -355,19 +301,6 @@
     return sb.toString();
   }
 
-  private boolean isJavaScriptObject(JType type) {
-    JClassType classType = type.isClass();
-    if (classType == null) {
-      return false;
-    }
-
-    if (classType.isAssignableTo(coreTypes.javaScriptObject)) {
-      return true;
-    } else {
-      return false;
-    }
-  }
-
   private void rewriteCompilationUnit(TreeLogger logger, char[] source,
       List<Replacement> changes, CompilationUnitProvider cup, boolean pretty)
       throws UnableToCompleteException {
@@ -376,7 +309,9 @@
     JClassType[] types = oracle.getTypesInCompilationUnit(cup);
     for (int i = 0; i < types.length; i++) {
       JClassType type = types[i];
-      rewriteType(logger, source, changes, type, pretty);
+      if (!type.getQualifiedSourceName().startsWith("java.")) {
+        rewriteType(logger, source, changes, type, pretty);
+      }
     }
   }
 
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 a65ab18..fcec6cf 100644
--- a/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java
+++ b/dev/core/src/com/google/gwt/dev/shell/ModuleSpace.java
@@ -172,7 +172,8 @@
       Class<?>[] types, Object[] args) throws Throwable {
     JsValue result = invokeNative(name, jthis, types, args);
     String msgPrefix = "invokeNativeBoolean(" + name + ")";
-    Boolean value = JsValueGlue.get(result, Boolean.class, msgPrefix);
+    Boolean value = JsValueGlue.get(result, getIsolatedClassLoader(),
+        boolean.class, msgPrefix);
     if (value == null) {
       throw new HostedModeException(msgPrefix
           + ": return value null received, expected a boolean");
@@ -184,7 +185,7 @@
       Object[] args) throws Throwable {
     JsValue result = invokeNative(name, jthis, types, args);
     String msgPrefix = "invokeNativeByte(" + name + ")";
-    Byte value = JsValueGlue.get(result, Byte.class, msgPrefix);
+    Byte value = JsValueGlue.get(result, null, Byte.TYPE, msgPrefix);
     if (value == null) {
       throw new HostedModeException(msgPrefix
           + ": return value null received, expected a byte");
@@ -196,7 +197,7 @@
       Object[] args) throws Throwable {
     JsValue result = invokeNative(name, jthis, types, args);
     String msgPrefix = "invokeNativeCharacter(" + name + ")";
-    Character value = JsValueGlue.get(result, Character.class, msgPrefix);
+    Character value = JsValueGlue.get(result, null, Character.TYPE, msgPrefix);
     if (value == null) {
       throw new HostedModeException(msgPrefix
           + ": return value null received, expected a char");
@@ -208,7 +209,7 @@
       Object[] args) throws Throwable {
     JsValue result = invokeNative(name, jthis, types, args);
     String msgPrefix = "invokeNativeDouble(" + name + ")";
-    Double value = JsValueGlue.get(result, Double.class, msgPrefix);
+    Double value = JsValueGlue.get(result, null, Double.TYPE, msgPrefix);
     if (value == null) {
       throw new HostedModeException(msgPrefix
           + ": return value null received, expected a double");
@@ -220,7 +221,7 @@
       Object[] args) throws Throwable {
     JsValue result = invokeNative(name, jthis, types, args);
     String msgPrefix = "invokeNativeFloat(" + name + ")";
-    Float value = JsValueGlue.get(result, Float.class, msgPrefix);
+    Float value = JsValueGlue.get(result, null, Float.TYPE, msgPrefix);
     if (value == null) {
       throw new HostedModeException(msgPrefix
           + ": return value null received, expected a float");
@@ -228,19 +229,11 @@
     return value.floatValue();
   }
 
-  public Object invokeNativeHandle(String name, Object jthis,
-      Class<?> returnType, Class<?>[] types, Object[] args) throws Throwable {
-
-    JsValue result = invokeNative(name, jthis, types, args);
-    return JsValueGlue.get(result, returnType, "invokeNativeHandle(" + name
-        + ")");
-  }
-
   public int invokeNativeInt(String name, Object jthis, Class<?>[] types,
       Object[] args) throws Throwable {
     JsValue result = invokeNative(name, jthis, types, args);
     String msgPrefix = "invokeNativeInteger(" + name + ")";
-    Integer value = JsValueGlue.get(result, Integer.class, msgPrefix);
+    Integer value = JsValueGlue.get(result, null, Integer.TYPE, msgPrefix);
     if (value == null) {
       throw new HostedModeException(msgPrefix
           + ": return value null received, expected an int");
@@ -252,7 +245,7 @@
       Object[] args) throws Throwable {
     JsValue result = invokeNative(name, jthis, types, args);
     String msgPrefix = "invokeNativeLong(" + name + ")";
-    Long value = JsValueGlue.get(result, Long.class, msgPrefix);
+    Long value = JsValueGlue.get(result, null, Long.TYPE, msgPrefix);
     if (value == null) {
       throw new HostedModeException(msgPrefix
           + ": return value null received, expected a long");
@@ -263,15 +256,15 @@
   public Object invokeNativeObject(String name, Object jthis, Class<?>[] types,
       Object[] args) throws Throwable {
     JsValue result = invokeNative(name, jthis, types, args);
-    return JsValueGlue.get(result, Object.class, "invokeNativeObject(" + name
-        + ")");
+    return JsValueGlue.get(result, getIsolatedClassLoader(), Object.class,
+        "invokeNativeObject(" + name + ")");
   }
 
   public short invokeNativeShort(String name, Object jthis, Class<?>[] types,
       Object[] args) throws Throwable {
     JsValue result = invokeNative(name, jthis, types, args);
     String msgPrefix = "invokeNativeShort(" + name + ")";
-    Short value = JsValueGlue.get(result, Short.class, msgPrefix);
+    Short value = JsValueGlue.get(result, null, Short.TYPE, msgPrefix);
     if (value == null) {
       throw new HostedModeException(msgPrefix
           + ": return value null received, expected a short");
@@ -279,13 +272,6 @@
     return value.shortValue();
   }
 
-  public String invokeNativeString(String name, Object jthis, Class<?>[] types,
-      Object[] args) throws Throwable {
-    JsValue result = invokeNative(name, jthis, types, args);
-    return JsValueGlue.get(result, String.class, "invokeNativeString(" + name
-        + ")");
-  }
-
   public void invokeNativeVoid(String name, Object jthis, Class<?>[] types,
       Object[] args) throws Throwable {
     JsValue result = invokeNative(name, jthis, types, args);
@@ -390,7 +376,7 @@
   }
 
   @SuppressWarnings("unchecked")
-  public Object rebindAndCreate(String requestedClassName)
+  public <T> T rebindAndCreate(String requestedClassName)
       throws UnableToCompleteException {
     Throwable caught = null;
     String msg = null;
@@ -407,7 +393,7 @@
       } else {
         Constructor<?> ctor = resolvedClass.getDeclaredConstructor();
         ctor.setAccessible(true);
-        return ctor.newInstance();
+        return (T) ctor.newInstance();
       }
     } catch (ClassNotFoundException e) {
       msg = "Could not load deferred binding result type '" + resultName + "'";
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 940bf9e..a22c4f3 100644
--- a/dev/core/src/com/google/gwt/dev/shell/ModuleSpacePropertyOracle.java
+++ b/dev/core/src/com/google/gwt/dev/shell/ModuleSpacePropertyOracle.java
@@ -92,7 +92,7 @@
       try {
         // Invoke the property provider function in JavaScript.
         //
-        value = space.invokeNativeString("__gwt_getProperty", null,
+        value = (String) space.invokeNativeObject("__gwt_getProperty", null,
             new Class[] {String.class}, new Object[] {prop.getName()});
       } catch (Throwable e) {
         // Treat as an unknown value.
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 95e9009..f4e4821 100644
--- a/dev/core/src/com/google/gwt/dev/shell/ShellJavaScriptHost.java
+++ b/dev/core/src/com/google/gwt/dev/shell/ShellJavaScriptHost.java
@@ -75,12 +75,6 @@
       Object[] args) throws Throwable;
 
   /**
-   * Invoke a native JavaScript function that returns a handle value.
-   */
-  Object invokeNativeHandle(String name, Object jthis, Class<?> returnType,
-      Class<?>[] types, Object[] args) throws Throwable;
-
-  /**
    * Invoke a native JavaScript function that returns an integer value.
    */
   int invokeNativeInt(String name, Object jthis, Class<?>[] types, Object[] args)
@@ -105,12 +99,6 @@
       Object[] args) throws Throwable;
 
   /**
-   * Invoke a native JavaScript function that returns a string value.
-   */
-  String invokeNativeString(String name, Object jthis, Class<?>[] types,
-      Object[] args) throws Throwable;
-
-  /**
    * Invoke a native JavaScript function that returns no value.
    */
   void invokeNativeVoid(String name, Object jthis, Class<?>[] types,
diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/HostedModeClassRewriter.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/HostedModeClassRewriter.java
new file mode 100644
index 0000000..836d2ba
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/shell/rewrite/HostedModeClassRewriter.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2008 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.shell.rewrite;
+
+import com.google.gwt.dev.shell.JsValueGlue;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * This class performs any and all byte code rewriting needed to make hosted
+ * mode work. Currently, it performs the following rewrites:
+ * <ol>
+ * <li>Rewrites all JSO types into an interface type (which retains the
+ * original name) and an implementation type (which has a $ appended).</li>
+ * <li>All JSO interface types are empty and mirror the original type
+ * hierarchy.</li>
+ * <li>All JSO impl types contain the guts of the original type, except that
+ * all instance methods are reimplemented as statics.</li>
+ * <li>Calls sites to JSO types rewritten to dispatch to impl types. Any
+ * virtual calls are also made static. Static field references to JSO types
+ * reference static fields in the the impl class.</li>
+ * <li>JavaScriptObject$ implements all the interface types and is the only
+ * instantiable type.</li>
+ * </ol>
+ * 
+ * @see RewriteRefsToJsoClasses
+ * @see WriteJsoInterface
+ * @see WriteJsoImpl
+ */
+public class HostedModeClassRewriter {
+
+  /**
+   * Maps instance methods to the class in which they are declared.
+   */
+  public interface InstanceMethodMapper {
+
+    /**
+     * For a given instance method and declared qualifying class, find the class
+     * in which that method was first declared. Methods declared on Object will
+     * return "java/lang/Object". Static methods will always return
+     * <code>declaredClass</code>.
+     * 
+     * @param desc a descriptor of the static type of the qualifier
+     * @param signature the binary signature of the method
+     * @return the descriptor of the class in which that method was declared
+     * @throws IllegalArgumentException if the method could not be found
+     */
+    String findDeclaringClass(String desc, String signature);
+  }
+
+  static final String REFERENCE_FIELD = JsValueGlue.HOSTED_MODE_REFERENCE;
+
+  static final String JAVASCRIPTOBJECT_DESC = JsValueGlue.JSO_CLASS.replace(
+      '.', '/');
+
+  static final String JAVASCRIPTOBJECT_IMPL_DESC = JsValueGlue.JSO_IMPL_CLASS.replace(
+      '.', '/');;
+
+  static String addSyntheticThisParam(String owner, String methodDescriptor) {
+    return "(L" + owner + ";" + methodDescriptor.substring(1);
+  }
+
+  private static String toDescriptor(String jsoSubtype) {
+    return jsoSubtype.replace('.', '/');
+  }
+
+  /**
+   * An unmodifiable set of descriptors containing the implementation form of
+   * <code>JavaScriptObject</code> and all subclasses.
+   */
+  private final Set<String> jsoImplDescriptors;
+
+  /**
+   * An unmodifiable set of descriptors containing the interface form of
+   * <code>JavaScriptObject</code> and all subclasses.
+   */
+  private final Set<String> jsoIntfDescriptors;
+
+  /**
+   * Maps methods to the class in which they are declared.
+   */
+  private InstanceMethodMapper mapper;
+
+  /**
+   * Creates a new {@link HostedModeClassRewriter} for a specified set of
+   * subclasses of JavaScriptObject.
+   * 
+   * @param jsoSubtypes a set of binary type names representing JavaScriptObject
+   *          and all of its subtypes of
+   * @param mapper maps methods to the class in which they are declared
+   */
+  public HostedModeClassRewriter(Set<String> jsoSubtypes,
+      InstanceMethodMapper mapper) {
+    Set<String> buildJsoIntfDescriptors = new HashSet<String>();
+    Set<String> buildJsoImplDescriptors = new HashSet<String>();
+    for (String jsoSubtype : jsoSubtypes) {
+      String desc = toDescriptor(jsoSubtype);
+      buildJsoIntfDescriptors.add(desc);
+      buildJsoImplDescriptors.add(desc + "$");
+    }
+    this.jsoIntfDescriptors = Collections.unmodifiableSet(buildJsoIntfDescriptors);
+    this.jsoImplDescriptors = Collections.unmodifiableSet(buildJsoImplDescriptors);
+    this.mapper = mapper;
+  }
+
+  /**
+   * Returns <code>true</code> if the class is the implementation class for a
+   * JSO subtype.
+   */
+  public boolean isJsoImpl(String className) {
+    return jsoImplDescriptors.contains(toDescriptor(className));
+  }
+
+  /**
+   * Returns <code>true</code> if the class is the interface class for a JSO
+   * subtype.
+   */
+  public boolean isJsoIntf(String className) {
+    return jsoIntfDescriptors.contains(toDescriptor(className));
+  }
+
+  /**
+   * Performs rewriting transformations on a class.
+   * 
+   * @param className the name of the class
+   * @param classBytes the bytes of the class
+   */
+  public byte[] rewrite(String className, byte[] classBytes) {
+    String desc = toDescriptor(className);
+
+    // The ASM model is to chain a bunch of visitors together.
+    ClassWriter writer = new ClassWriter(0);
+    ClassVisitor v = writer;
+
+    // v = new CheckClassAdapter(v);
+    // v = new TraceClassVisitor(v, new PrintWriter(System.out));
+
+    v = new RewriteRefsToJsoClasses(v, jsoIntfDescriptors, mapper);
+
+    if (jsoImplDescriptors.contains(desc)) {
+      v = new WriteJsoImpl(v, jsoIntfDescriptors, mapper);
+    } else if (jsoIntfDescriptors.contains(desc)) {
+      v = new WriteJsoInterface(v);
+    }
+
+    new ClassReader(classBytes).accept(v, 0);
+    return writer.toByteArray();
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteRefsToJsoClasses.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteRefsToJsoClasses.java
new file mode 100644
index 0000000..83afe22
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/shell/rewrite/RewriteRefsToJsoClasses.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2008 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.shell.rewrite;
+
+import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.InstanceMethodMapper;
+
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodAdapter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.commons.Remapper;
+
+import java.util.Set;
+
+/**
+ * Rewrites references to modified JSO subtypes.
+ * 
+ * <ol>
+ * <li>Changes the owner type for instructions that reference items in a JSO
+ * class to the implementation class.</li>
+ * <li>Rewrites instance calls to JSO classes into static calls.</li>
+ * <li>Updates the descriptor for such call sites to includes a synthetic
+ * <code>this</code> parameter. This modified method has same stack behavior
+ * as the original instance method.</li>
+ * </ol>
+ */
+class RewriteRefsToJsoClasses extends ClassAdapter {
+
+  /**
+   * A method body rewriter to actually rewrite call sites.
+   */
+  private class MyMethodAdapter extends MethodAdapter {
+
+    private Remapper remapper = new Remapper() {
+      @Override
+      public String map(String typeName) {
+        if (jsoDescriptors.contains(typeName)) {
+          return HostedModeClassRewriter.JAVASCRIPTOBJECT_IMPL_DESC;
+        }
+        return typeName;
+      }
+    };
+
+    public MyMethodAdapter(MethodVisitor mv) {
+      super(mv);
+    }
+
+    @Override
+    public void visitFieldInsn(int opcode, String owner, String name,
+        String desc) {
+      if (jsoDescriptors.contains(owner)) {
+        // Change the owner to the rewritten class.
+        owner += "$";
+      }
+      super.visitFieldInsn(opcode, owner, name, desc);
+    }
+
+    @Override
+    public void visitLdcInsn(Object cst) {
+      cst = remapper.mapValue(cst);
+      super.visitLdcInsn(cst);
+    }
+
+    @Override
+    public void visitMethodInsn(int opcode, String owner, String name,
+        String desc) {
+      if (jsoDescriptors.contains(owner)) {
+        // Find the class that actually declared the method.
+        if (opcode == Opcodes.INVOKEVIRTUAL) {
+          owner = mapper.findDeclaringClass(owner, name + desc);
+        }
+        if (!owner.equals("java/lang/Object")) {
+          if (opcode == Opcodes.INVOKEVIRTUAL
+              || opcode == Opcodes.INVOKESPECIAL) {
+            // Instance/super call to JSO; rewrite as static.
+            opcode = Opcodes.INVOKESTATIC;
+            desc = HostedModeClassRewriter.addSyntheticThisParam(owner, desc);
+            name += "$";
+          }
+          // Change the owner to implementation class.
+          owner += "$";
+        }
+      }
+      super.visitMethodInsn(opcode, owner, name, desc);
+    }
+
+    @Override
+    public void visitMultiANewArrayInsn(String desc, int dims) {
+      desc = remapper.mapType(desc);
+      super.visitMultiANewArrayInsn(desc, dims);
+    }
+
+    @Override
+    public void visitTypeInsn(int opcode, String type) {
+      if (opcode == Opcodes.ANEWARRAY) {
+        type = remapper.mapType(type);
+      }
+      super.visitTypeInsn(opcode, type);
+    }
+  }
+
+  /**
+   * An unmodifiable set of descriptors containing <code>JavaScriptObject</code>
+   * and all subclasses.
+   */
+  protected final Set<String> jsoDescriptors;
+
+  /**
+   * Maps methods to the class in which they are declared.
+   */
+  private InstanceMethodMapper mapper;
+
+  /**
+   * Construct a new rewriter instance.
+   * 
+   * @param cv the visitor to chain to
+   * @param jsoDescriptors an unmodifiable set of descriptors containing
+   *          <code>JavaScriptObject</code> and all subclasses
+   * @param mapper maps methods to the class in which they are declared
+   */
+  public RewriteRefsToJsoClasses(ClassVisitor cv, Set<String> jsoDescriptors,
+      InstanceMethodMapper mapper) {
+    super(cv);
+    this.jsoDescriptors = jsoDescriptors;
+    this.mapper = mapper;
+  }
+
+  @Override
+  public MethodVisitor visitMethod(int access, String name, String desc,
+      String signature, String[] exceptions) {
+    // Wrap the returned method visitor in my own.
+    MethodVisitor mv = super.visitMethod(access, name, desc, signature,
+        exceptions);
+    return new MyMethodAdapter(mv);
+  }
+
+}
diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/WriteJsoImpl.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/WriteJsoImpl.java
new file mode 100644
index 0000000..82ae49b
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/shell/rewrite/WriteJsoImpl.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2008 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.shell.rewrite;
+
+import com.google.gwt.dev.shell.rewrite.HostedModeClassRewriter.InstanceMethodMapper;
+
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.util.ArrayList;
+import java.util.Set;
+
+/**
+ * Writes the implementation class for a JSO type.
+ * 
+ * <ol>
+ * <li>The new type has the same name as the old type with a '$' appended.</li>
+ * <li>The new type's superclass is Object.</li>
+ * <li>All instance methods in the original type become static methods taking
+ * an explicit <code>this</code> parameter. Such methods have the same stack
+ * behavior as the original.</li>
+ * <li>JavaScriptObject itself gets a new synthetic field to store the
+ * underlying hosted mode reference.</li>
+ * </ol>
+ */
+class WriteJsoImpl extends ClassAdapter {
+
+  /**
+   * An unmodifiable set of descriptors containing <code>JavaScriptObject</code>
+   * and all subclasses.
+   */
+  protected final Set<String> jsoDescriptors;
+
+  /**
+   * Maps methods to the class in which they are declared.
+   */
+  private InstanceMethodMapper mapper;
+
+  /**
+   * The original name of the class being visited.
+   */
+  private String originalName;
+
+  /**
+   * Construct a new rewriter instance.
+   * 
+   * @param cv the visitor to chain to
+   * @param jsoDescriptors an unmodifiable set of descriptors containing
+   *          <code>JavaScriptObject</code> and all subclasses
+   * @param mapper maps methods to the class in which they are declared
+   */
+  public WriteJsoImpl(ClassVisitor cv, Set<String> jsoDescriptors,
+      InstanceMethodMapper mapper) {
+    super(cv);
+    this.jsoDescriptors = jsoDescriptors;
+    this.mapper = mapper;
+  }
+
+  @Override
+  public void visit(int version, int access, String name, String signature,
+      String superName, String[] interfaces) {
+    originalName = name;
+
+    // JavaScriptObject$ must implement all JSO interface types.
+    if (isJavaScriptObject()) {
+      ArrayList<String> jsoDescList = new ArrayList<String>();
+      jsoDescList.addAll(jsoDescriptors);
+      interfaces = jsoDescList.toArray(new String[jsoDescList.size()]);
+    } else {
+      interfaces = null;
+    }
+
+    super.visit(version, access, name + '$', signature, "java/lang/Object",
+        interfaces);
+
+    if (isJavaScriptObject()) {
+      FieldVisitor fv = visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC,
+          HostedModeClassRewriter.REFERENCE_FIELD, "Ljava/lang/Object;", null,
+          null);
+      if (fv != null) {
+        fv.visitEnd();
+      }
+    }
+  }
+
+  @Override
+  public MethodVisitor visitMethod(int access, String name, String desc,
+      String signature, String[] exceptions) {
+    boolean isCtor = "<init>".equals(name);
+    if (!isJavaScriptObject() && isCtor) {
+      // Don't copy over constructors except for JavaScriptObject itself.
+      return null;
+    }
+    if (!isCtor && !isStatic(access) && !isObjectMethod(name + desc)) {
+      access |= Opcodes.ACC_STATIC;
+      desc = HostedModeClassRewriter.addSyntheticThisParam(originalName, desc);
+      name = name + "$";
+    }
+    return super.visitMethod(access, name, desc, signature, exceptions);
+  }
+
+  private boolean isJavaScriptObject() {
+    return originalName.equals(HostedModeClassRewriter.JAVASCRIPTOBJECT_DESC);
+  }
+
+  private boolean isObjectMethod(String signature) {
+    return "java/lang/Object".equals(mapper.findDeclaringClass(originalName,
+        signature));
+  }
+
+  private boolean isStatic(int access) {
+    return (access & Opcodes.ACC_STATIC) != 0;
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/shell/rewrite/WriteJsoInterface.java b/dev/core/src/com/google/gwt/dev/shell/rewrite/WriteJsoInterface.java
new file mode 100644
index 0000000..de496fd
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/shell/rewrite/WriteJsoInterface.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2008 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.shell.rewrite;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Writes an empty interface to stand in for a JSO type.
+ */
+class WriteJsoInterface extends ClassAdapter {
+  /**
+   * Construct a instance.
+   * 
+   * @param cv the visitor to chain to
+   */
+  public WriteJsoInterface(ClassVisitor cv) {
+    super(cv);
+  }
+
+  @Override
+  public void visit(int version, int access, String name, String signature,
+      String superName, String[] interfaces) {
+    if ("java/lang/Object".equals(superName)) {
+      interfaces = null;
+    } else {
+      interfaces = new String[] {superName};
+    }
+    super.visit(version, Opcodes.ACC_PUBLIC | Opcodes.ACC_INTERFACE, name,
+        signature, "java/lang/Object", interfaces);
+  }
+
+  @Override
+  public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+    return null;
+  }
+
+  @Override
+  public void visitAttribute(Attribute attr) {
+  }
+
+  @Override
+  public void visitEnd() {
+    super.visitEnd();
+  }
+
+  @Override
+  public FieldVisitor visitField(int access, String name, String desc,
+      String signature, Object value) {
+    return null;
+  }
+
+  @Override
+  public void visitInnerClass(String name, String outerName, String innerName,
+      int access) {
+  }
+
+  @Override
+  public MethodVisitor visitMethod(int access, String name, String desc,
+      String signature, String[] exceptions) {
+    return null;
+  }
+
+  @Override
+  public void visitOuterClass(String owner, String name, String desc) {
+  }
+
+  @Override
+  public void visitSource(String source, String debug) {
+  }
+}
diff --git a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java
index 096090f..7c6e2b5 100644
--- a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java
+++ b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java
@@ -121,9 +121,13 @@
    * Performs an array assignment, checking for valid index and type.
    */
   public static Object setCheck(Array array, int index, Object value) {
-    if (value != null && array.queryId != 0
-        && !Cast.instanceOf(value, array.queryId)) {
-      throw new ArrayStoreException();
+    if (value != null) {
+      if (array.queryId > 0 && !Cast.canCastUnsafe(value.typeId, array.queryId)) {
+        throw new ArrayStoreException();
+      }
+      if (array.queryId < 0 && Cast.isJavaObject(value)) {
+        throw new ArrayStoreException();
+      }
     }
     return set(array, index, value);
   }
diff --git a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java
index f9c60d1..e0177fc 100644
--- a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java
+++ b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -15,8 +15,6 @@
  */
 package com.google.gwt.lang;
 
-import com.google.gwt.core.client.JavaScriptObject;
-
 // CHECKSTYLE_NAMING_OFF: Uses legacy conventions of underscore prefixes.
 
 /**
@@ -28,27 +26,57 @@
   // magic magic magic
   protected static Object typeIdArray;
 
-  protected static native boolean canCast(int srcId, int dstId) /*-{
-    // Force to boolean.
-    return !!(srcId && @com.google.gwt.lang.Cast::typeIdArray[srcId][dstId]);
+  static native boolean canCast(int srcId, int dstId) /*-{
+    return srcId && !!@com.google.gwt.lang.Cast::typeIdArray[srcId][dstId];
+  }-*/;
+
+  /**
+   * Danger: value not coerced to boolean; use the result only in a boolean
+   * context.
+   */
+  static native boolean canCastUnsafe(int srcId, int dstId) /*-{
+    return srcId && @com.google.gwt.lang.Cast::typeIdArray[srcId][dstId];
   }-*/;
 
   static native String charToString(char x) /*-{
     return String.fromCharCode(x);
   }-*/;
 
-  static native Object dynamicCast(Object src, int dstId) /*-{
-    if (src != null)
-      @com.google.gwt.lang.Cast::canCast(II)(src.@java.lang.Object::typeId,dstId)
-      || @com.google.gwt.lang.Cast::throwClassCastException()();
-
+  static Object dynamicCast(Object src, int dstId) {
+    if (src != null && !canCastUnsafe(src.typeId, dstId)) {
+      throw new ClassCastException();
+    }
     return src;
-  }-*/;
+  }
 
-  static native boolean instanceOf(Object src, int dstId) /*-{
-    return (src != null) &&
-        @com.google.gwt.lang.Cast::canCast(II)(src.@java.lang.Object::typeId,dstId);
-  }-*/;
+  /**
+   * Allow a cast to JSO only if there's no type ID.
+   */
+  static Object dynamicCastJso(Object src) {
+    if (src != null && isJavaObject(src)) {
+      throw new ClassCastException();
+    }
+    return src;
+  }
+
+  static boolean instanceOf(Object src, int dstId) {
+    return (src != null) && canCast(src.typeId, dstId);
+  }
+
+  /**
+   * Instance of JSO only if there's no type ID.
+   */
+  static boolean instanceOfJso(Object src) {
+    return (src != null) && isJavaScriptObject(src);
+  }
+
+  static boolean isJavaObject(Object src) {
+    return src.typeMarker == getNullMethod() || src.typeId == 2;
+  }
+
+  static boolean isJavaScriptObject(Object src) {
+    return src.typeMarker != getNullMethod() && src.typeId != 2;
+  }
 
   /**
    * See JLS 5.1.3.
@@ -120,14 +148,6 @@
   }
 
   /**
-   * Unconditionally throw a {@link ClassCastException}. Called from {#link
-   * {@link #dynamicCast(Object, int)}.
-   */
-  static Object throwClassCastException() throws ClassCastException {
-    throw new ClassCastException();
-  }
-
-  /**
    * Check a statically false cast, which can succeed if the argument is null.
    * Called by compiler-generated code based on static type information.
    */
@@ -139,26 +159,8 @@
     return o;
   }
 
-  static native JavaScriptObject wrapJSO(JavaScriptObject jso, Object seed) /*-{
-    _ = seed.prototype;
-
-    // WEIRD: The inequality below represents the fact that superclasses always
-    // have typeId < any subclass typeId.  This code lets us wrap the same JSO 
-    // "tighter" but never "looser". This would break if the compiler did not
-    // ensure that superclass ids are less than subclass ids.
-    //
-    // Note also that the inequality is false (and thus allows wrapping) if
-    // jso's typeId is undefined, because (undefined < positive int).
-
-    if (jso && !(jso.@java.lang.Object::typeId >= _.@java.lang.Object::typeId)) {
-      for (var i in _) {
-        // don't clobber toString
-        if (i != 'toString' ) {
-          jso[i] = _[i];
-        }
-      }
-    }
-    return jso;
+  private static native Object getNullMethod() /*-{
+    return @null::nullMethod();
   }-*/;
 
 }
diff --git a/dev/core/test/com/google/gwt/dev/jdt/test/ByteCodeCompilerTest.java b/dev/core/test/com/google/gwt/dev/jdt/ByteCodeCompilerTest.java
similarity index 99%
rename from dev/core/test/com/google/gwt/dev/jdt/test/ByteCodeCompilerTest.java
rename to dev/core/test/com/google/gwt/dev/jdt/ByteCodeCompilerTest.java
index 4d51f30..0f24786 100644
--- a/dev/core/test/com/google/gwt/dev/jdt/test/ByteCodeCompilerTest.java
+++ b/dev/core/test/com/google/gwt/dev/jdt/ByteCodeCompilerTest.java
@@ -13,7 +13,7 @@
  * License for the specific language governing permissions and limitations under
  * the License.
  */
-package com.google.gwt.dev.jdt.test;
+package com.google.gwt.dev.jdt;
 
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
diff --git a/dev/core/test/com/google/gwt/dev/jdt/JSORestrictionsTest.java b/dev/core/test/com/google/gwt/dev/jdt/JSORestrictionsTest.java
new file mode 100644
index 0000000..24da3e1
--- /dev/null
+++ b/dev/core/test/com/google/gwt/dev/jdt/JSORestrictionsTest.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2008 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.jdt;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.jdt.StaticCompilationUnitProvider;
+import com.google.gwt.dev.jdt.TypeOracleBuilder;
+
+import junit.framework.TestCase;
+
+public class JSORestrictionsTest extends TestCase {
+
+  public void testInstanceField() throws UnableToCompleteException {
+    StringBuffer buggyCode = new StringBuffer();
+    buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
+    buggyCode.append("public class Buggy extends JavaScriptObject {\n");
+    buggyCode.append("  protected Buggy() { }\n");
+    buggyCode.append("  int myStsate = 3;\n");
+    buggyCode.append("}\n");
+
+    shouldGenerateError(buggyCode, "Line 4:  "
+        + JSORestrictionsChecker.ERR_INSTANCE_FIELD);
+  }
+
+  public void testMultiArgConstructor() throws UnableToCompleteException {
+    StringBuffer buggyCode = new StringBuffer();
+    buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
+    buggyCode.append("public final class Buggy extends JavaScriptObject {\n");
+    buggyCode.append("  protected Buggy(int howBuggy) { }\n");
+    buggyCode.append("}\n");
+
+    shouldGenerateError(buggyCode, "Line 3:  "
+        + JSORestrictionsChecker.ERR_CONSTRUCTOR_WITH_PARAMETERS);
+  }
+
+  public void testNew() throws UnableToCompleteException {
+    StringBuffer buggyCode = new StringBuffer();
+    buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
+    buggyCode.append("public class Buggy {\n");
+    buggyCode.append("  public static class MyJSO extends JavaScriptObject { \n");
+    buggyCode.append("    protected MyJSO() { }\n");
+    buggyCode.append("  }\n");
+    buggyCode.append("  MyJSO makeOne() { return new MyJSO(); }\n");
+    buggyCode.append("}\n");
+
+    shouldGenerateError(buggyCode, "Line 6:  "
+        + JSORestrictionsChecker.ERR_NEW_JSO);
+  }
+
+  public void testNoConstructor() throws UnableToCompleteException {
+    StringBuffer buggyCode = new StringBuffer();
+    buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
+    buggyCode.append("public class Buggy extends JavaScriptObject {\n");
+    buggyCode.append("}\n");
+
+    // The public constructor is implicit.
+    shouldGenerateError(buggyCode, "Line 2:  "
+        + JSORestrictionsChecker.ERR_NONPROTECTED_CONSTRUCTOR);
+  }
+
+  public void testNoInterfaces() throws UnableToCompleteException {
+    StringBuffer buggyCode = new StringBuffer();
+    buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
+    buggyCode.append("public class Buggy {\n");
+    buggyCode.append("  static interface Squeaks {\n");
+    buggyCode.append("    public void squeak();\n");
+    buggyCode.append("  }\n");
+    buggyCode.append("  static class Squeaker extends JavaScriptObject implements Squeaks {\n");
+    buggyCode.append("    public final void squeak() { }\n");
+    buggyCode.append("    protected Squeaker() { }\n");
+    buggyCode.append("  }\n");
+    buggyCode.append("}\n");
+
+    shouldGenerateError(buggyCode, "Line 6:  "
+        + JSORestrictionsChecker.errInterfaceWithMethods("Buggy.Squeaks"));
+  }
+
+  public void testNonEmptyConstructor() throws UnableToCompleteException {
+    StringBuffer buggyCode = new StringBuffer();
+    buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
+    buggyCode.append("public class Buggy extends JavaScriptObject {\n");
+    buggyCode.append("  protected Buggy() { while(true) { } }\n");
+    buggyCode.append("}\n");
+
+    shouldGenerateError(buggyCode, "Line 3:  "
+        + JSORestrictionsChecker.ERR_NONEMPTY_CONSTRUCTOR);
+  }
+
+  public void testNonFinalMethod() throws UnableToCompleteException {
+    StringBuffer buggyCode = new StringBuffer();
+    buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
+    buggyCode.append("public class Buggy extends JavaScriptObject {\n");
+    buggyCode.append("  int nonfinal() { return 10; }\n");
+    buggyCode.append("  protected Buggy() { }\n");
+    buggyCode.append("}\n");
+
+    shouldGenerateError(buggyCode, "Line 3:  "
+        + JSORestrictionsChecker.ERR_INSTANCE_METHOD_NONFINAL);
+  }
+
+  public void testNonProtectedConstructor() throws UnableToCompleteException {
+    StringBuffer buggyCode = new StringBuffer();
+    buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
+    buggyCode.append("public class Buggy extends JavaScriptObject {\n");
+    buggyCode.append("  Buggy() { }\n");
+    buggyCode.append("}\n");
+
+    shouldGenerateError(buggyCode, "Line 3:  "
+        + JSORestrictionsChecker.ERR_NONPROTECTED_CONSTRUCTOR);
+  }
+
+  public void testNonStaticInner() throws UnableToCompleteException {
+    StringBuffer buggyCode = new StringBuffer();
+    buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
+    buggyCode.append("public class Buggy {\n");
+    buggyCode.append("  public class MyJSO extends JavaScriptObject {\n");
+    buggyCode.append("    protected MyJSO() { }\n");
+    buggyCode.append("  }\n");
+    buggyCode.append("}\n");
+
+    shouldGenerateError(buggyCode, "Line 3:  "
+        + JSORestrictionsChecker.ERR_IS_NONSTATIC_NESTED);
+  }
+
+  public void testNoOverride() throws UnableToCompleteException {
+    StringBuffer buggyCode = new StringBuffer();
+    buggyCode.append("import com.google.gwt.core.client.JavaScriptObject;\n");
+    buggyCode.append("public class Buggy extends JavaScriptObject {\n");
+    buggyCode.append("  protected Buggy() { }\n");
+    buggyCode.append("  public final Object clone() { return this; }\n");
+    buggyCode.append("}\n");
+
+    shouldGenerateError(buggyCode, "Line 4:  "
+        + JSORestrictionsChecker.ERR_OVERRIDDEN_METHOD);
+  }
+
+  private void addStandardCups(TypeOracleBuilder builder)
+      throws UnableToCompleteException {
+    StringBuffer code = new StringBuffer();
+    code.append("package java.lang;\n");
+    code.append("public class Object {\n");
+    code.append("  public String toString() { return \"Object\"; }\n");
+    code.append("  public Object clone() { return this; } ");
+    code.append("}\n");
+
+    StaticCompilationUnitProvider objectCup = new StaticCompilationUnitProvider(
+        "java.lang", "Object", code.toString().toCharArray());
+
+    code.setLength(0);
+    code.append("package java.lang;\n");
+    code.append("public final class String {\n");
+    code.append("  public int length() { return 0; }\n");
+    code.append("}\n");
+
+    StaticCompilationUnitProvider stringCup = new StaticCompilationUnitProvider(
+        "java.lang", "String", code.toString().toCharArray());
+
+    code.setLength(0);
+    code.append("package java.lang;\n");
+    code.append("public interface Serializable { }\n");
+
+    StaticCompilationUnitProvider serializableCup = new StaticCompilationUnitProvider(
+        "java.lang", "Serializable", code.toString().toCharArray());
+
+    code.setLength(0);
+    code.append("package com.google.gwt.core.client;\n");
+    code.append("public class JavaScriptObject {\n");
+    code.append("  protected JavaScriptObject() { }\n");
+    code.append("}\n");
+
+    StaticCompilationUnitProvider jsoCup = new StaticCompilationUnitProvider(
+        "com.google.gwt.core.client", "JavaScriptObject",
+        code.toString().toCharArray());
+
+    builder.addCompilationUnit(objectCup);
+    builder.addCompilationUnit(stringCup);
+    builder.addCompilationUnit(serializableCup);
+    builder.addCompilationUnit(jsoCup);
+  }
+
+  /**
+   * Test that when compiling buggyCode, the TypeOracleBuilder emits
+   * expectedError somewhere in its output. The code should define a class named
+   * Buggy.
+   */
+  private void shouldGenerateError(CharSequence buggyCode,
+      final String expectedError) throws UnableToCompleteException {
+    TypeOracleBuilder builder = new TypeOracleBuilder();
+    addStandardCups(builder);
+
+    StaticCompilationUnitProvider buggyCup = new StaticCompilationUnitProvider(
+        "", "Buggy", buggyCode.toString().toCharArray());
+    builder.addCompilationUnit(buggyCup);
+
+    final boolean[] gotExpectedError = new boolean[1];
+
+    TreeLogger testLogger = new TreeLogger() {
+      public TreeLogger branch(Type type, String msg, Throwable caught) {
+        return this;
+      }
+
+      public boolean isLoggable(Type type) {
+        return type == ERROR;
+      }
+
+      public void log(Type type, String msg, Throwable caught) {
+        if (type == ERROR && msg.startsWith("Line ")) {
+          assertEquals(null, caught);
+          assertEquals(expectedError, msg);
+          gotExpectedError[0] = true;
+        }
+      }
+    };
+
+    builder.build(testLogger);
+    assertTrue("Failed to get expected error: '" + expectedError + "'",
+        gotExpectedError[0]);
+  }
+}
diff --git a/dev/linux/src/com/google/gwt/dev/shell/moz/GeckoDispatchAdapter.java b/dev/linux/src/com/google/gwt/dev/shell/moz/GeckoDispatchAdapter.java
index c312f2e..7c98a96 100644
--- a/dev/linux/src/com/google/gwt/dev/shell/moz/GeckoDispatchAdapter.java
+++ b/dev/linux/src/com/google/gwt/dev/shell/moz/GeckoDispatchAdapter.java
@@ -110,7 +110,8 @@
       throw new RuntimeException("Cannot reassign method " + name);
     }
     Field field = javaDispatch.getField(dispId);
-    Object val = JsValueGlue.get(jsValue, field.getType(), "setField");
+    Object val = JsValueGlue.get(jsValue, classLoader, field.getType(),
+        "setField");
     javaDispatch.setFieldValue(dispId, val);
   }
 }
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 a488495..36f0798 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
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 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
@@ -156,6 +156,8 @@
 
   protected static native double _getNumber(int jsRootedValue);
 
+  protected static native int _getObjectPointer(int jsRootedValue);
+
   protected static native String _getString(int jsRootedValue);
 
   protected static native String _getTypeString(int jsRootedValue);
@@ -290,6 +292,11 @@
     return _getInt(jsRootedValue);
   }
 
+  @Override
+  public int getJavaScriptObjectPointer() {
+    return _getObjectPointer(jsRootedValue);
+  }
+
   /**
    * Returns the underlying JavaScript object pointer as an integer.
    */
@@ -433,8 +440,6 @@
    * (non-Javadoc)
    * 
    * @see com.google.gwt.dev.shell.JsValue#setByte(byte)
-   * 
-   * TODO(jat): remove this method
    */
   @Override
   public void setByte(byte val) {
@@ -445,8 +450,6 @@
    * (non-Javadoc)
    * 
    * @see com.google.gwt.dev.shell.JsValue#setChar(char)
-   * 
-   * TODO(jat): remove this method
    */
   @Override
   public void setChar(char val) {
@@ -487,8 +490,6 @@
    * (non-Javadoc)
    * 
    * @see com.google.gwt.dev.shell.JsValue#setShort(short)
-   * 
-   * TODO(jat): remove this method
    */
   @Override
   public void setShort(short val) {
@@ -543,7 +544,7 @@
    *      java.lang.Object)
    */
   @Override
-  public void setWrappedJavaObject(CompilingClassLoader cl, Object val) {
+  public <T> void setWrappedJavaObject(CompilingClassLoader cl, T val) {
     if (val == null) {
       setNull();
       return;
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 24f0079..716a39f 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
@@ -114,7 +114,7 @@
    * @param methodName the method name on jsthis to call
    * @param jsthis A wrapped java object as a JsRootedValue pointer
    * @param jsargs the arguments to pass to the method as JsRootedValue pointers
-   * 
+   * @param retval the jsvalue to write the result into
    * @throws RuntimeException if the invoke fails
    */
   public static void invoke(int scriptObject, String methodName, int jsthis,
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 165a34e..805e419 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
@@ -67,10 +67,12 @@
     }
     Object jthis = null;
     if (method.needsThis()) {
-      jthis = JsValueGlue.get(jsthis, method.getDeclaringClass(), "invoke this");
+      jthis = JsValueGlue.get(jsthis, classLoader, method.getDeclaringClass(),
+          "invoke this");
     }
     for (int i = 0; i < argc; ++i) {
-      args[i] = JsValueGlue.get(jsargs[i], paramTypes[i], "invoke arguments");
+      args[i] = JsValueGlue.get(jsargs[i], classLoader, paramTypes[i],
+          "invoke arguments");
     }
     try {
       Object result;
diff --git a/dev/mac/src/com/google/gwt/dev/shell/mac/JsValueSaf.java b/dev/mac/src/com/google/gwt/dev/shell/mac/JsValueSaf.java
index afedef1..6a0f4c1 100644
--- a/dev/mac/src/com/google/gwt/dev/shell/mac/JsValueSaf.java
+++ b/dev/mac/src/com/google/gwt/dev/shell/mac/JsValueSaf.java
@@ -35,8 +35,8 @@
 public class JsValueSaf extends JsValue {
 
   private static class JsCleanupSaf implements JsCleanup {
-    private final int jsval;
     private final Throwable creationStackTrace;
+    private final int jsval;
 
     /**
      * Create a cleanup object which takes care of cleaning up the underlying JS
@@ -59,16 +59,16 @@
     }
   }
 
-  /* 
-   * Underlying JSValue* as an integer.
-   */
-  private int jsval;
-  
   /*
    * Stores a stack trace of the creation site for debugging.
    */
   private Throwable creationStackTrace;
 
+  /*
+   * Underlying JSValue* as an integer.
+   */
+  private int jsval;
+
   /**
    * Create a Java wrapper around an undefined JSValue.
    */
@@ -95,7 +95,16 @@
   public int getInt() {
     int curExecState = LowLevelSaf.getExecState();
     return LowLevelSaf.coerceToInt(curExecState, jsval);
- }
+  }
+
+  @Override
+  public int getJavaScriptObjectPointer() {
+    if (isJavaScriptObject()) {
+      return jsval;
+    } else {
+      return 0;
+    }
+  }
 
   public int getJsValue() {
     return jsval;
@@ -195,8 +204,8 @@
   }
 
   /**
-   * Set a new value.  Unlock the previous value, but do *not* lock the new
-   * value (see class comment).
+   * Set a new value. Unlock the previous value, but do *not* lock the new value
+   * (see class comment).
    * 
    * @param jsval the new value to set
    */
@@ -204,7 +213,7 @@
     LowLevelSaf.gcUnlock(this.jsval, creationStackTrace);
     init(jsval);
   }
-  
+
   @Override
   public void setNull() {
     setJsVal(LowLevelSaf.jsNull());
@@ -227,10 +236,10 @@
 
   @Override
   public void setValue(JsValue other) {
-    int jsvalOther = ((JsValueSaf)other).jsval;
+    int jsvalOther = ((JsValueSaf) other).jsval;
     /*
-     * Add another lock to this jsval, since both the other object and this
-     * one will eventually release it.
+     * Add another lock to this jsval, since both the other object and this one
+     * will eventually release it.
      */
     LowLevelSaf.gcLock(jsvalOther);
     setJsVal(jsvalOther);
@@ -243,7 +252,7 @@
       setNull();
       return;
     } else if (val instanceof DispatchObject) {
-      dispObj = (DispatchObject)val;
+      dispObj = (DispatchObject) val;
     } else {
       dispObj = new WebKitDispatchAdapter(cl, val);
     }
@@ -257,11 +266,12 @@
 
   /**
    * Initialization helper method.
+   * 
    * @param jsval underlying JSValue*
    */
   private void init(int jsval) {
     this.jsval = jsval;
-    
+
     // only create and fill in the stack trace if we are debugging
     if (LowLevelSaf.debugObjectCreation) {
       this.creationStackTrace = new Throwable();
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 2d3152d..c6b5661 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
@@ -59,11 +59,12 @@
       }
       Object jthis = null;
       if (method.needsThis()) {
-        jthis = JsValueGlue.get(jsthis, method.getDeclaringClass(),
-            "invoke this");
+        jthis = JsValueGlue.get(jsthis, classLoader,
+            method.getDeclaringClass(), "invoke this");
       }
       for (int i = 0; i < argc; ++i) {
-        args[i] = JsValueGlue.get(jsargs[i], paramTypes[i], "invoke args");
+        args[i] = JsValueGlue.get(jsargs[i], classLoader, paramTypes[i],
+            "invoke args");
       }
       try {
         Object result;
diff --git a/dev/mac/src/com/google/gwt/dev/shell/mac/WebKitDispatchAdapter.java b/dev/mac/src/com/google/gwt/dev/shell/mac/WebKitDispatchAdapter.java
index 767a4bf..d1cb4de 100644
--- a/dev/mac/src/com/google/gwt/dev/shell/mac/WebKitDispatchAdapter.java
+++ b/dev/mac/src/com/google/gwt/dev/shell/mac/WebKitDispatchAdapter.java
@@ -61,9 +61,6 @@
     this.classLoader = cl;
   }
 
-  /* (non-Javadoc)
-   * @see com.google.gwt.dev.shell.mac.LowLevelSaf.DispatchObject#getField(java.lang.String)
-   */
   public int getField(String name) {
     int dispId = classLoader.getDispId(name);
     if (dispId < 0) {
@@ -87,16 +84,10 @@
     }
   }
 
-  /* (non-Javadoc)
-   * @see com.google.gwt.dev.shell.mac.LowLevelSaf.DispatchObject#getTarget()
-   */
   public Object getTarget() {
     return javaDispatch.getTarget();
   }
 
-  /* (non-Javadoc)
-   * @see com.google.gwt.dev.shell.mac.LowLevelSaf.DispatchObject#setField(java.lang.String, int)
-   */
   public void setField(String name, int value) {
     JsValue jsValue = new JsValueSaf(value);
     int dispId = classLoader.getDispId(name);
@@ -108,7 +99,8 @@
       throw new RuntimeException("Cannot reassign method " + name);
     }
     Field field = javaDispatch.getField(dispId);
-    Object val = JsValueGlue.get(jsValue, field.getType(), "setField");
+    Object val = JsValueGlue.get(jsValue, classLoader, field.getType(),
+        "setField");
     javaDispatch.setFieldValue(dispId, val);
   }
 
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 09103b1..9223168 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
@@ -116,7 +116,7 @@
       Variant[] params, MethodAdaptor method) throws InstantiationException,
       InvocationTargetException, HResultException {
     // TODO: make sure we have enough args! It's okay if there are too many.
-    Object[] javaParams = SwtOleGlue.convertVariantsToObjects(
+    Object[] javaParams = SwtOleGlue.convertVariantsToObjects(cl,
         method.getParameterTypes(), params, "Calling method '"
             + method.getName() + "'");
 
@@ -149,14 +149,7 @@
       }
     }
 
-    // Convert it to a variant (if the return type is void, return
-    // a VT_EMPTY variant -- 'undefined' in JavaScript).
-    //
-    Class<?> returnType = method.getReturnType();
-    if (returnType.equals(Void.TYPE)) {
-      return new Variant();
-    }
-    return SwtOleGlue.convertObjectToVariant(cl, returnType, result);
+    return SwtOleGlue.convertObjectToVariant(cl, method.getReturnType(), result);
   }
 
   protected int refCount;
diff --git a/dev/windows/src/com/google/gwt/dev/shell/ie/IDispatchProxy.java b/dev/windows/src/com/google/gwt/dev/shell/ie/IDispatchProxy.java
index 8c55c2a..05e83cc 100644
--- a/dev/windows/src/com/google/gwt/dev/shell/ie/IDispatchProxy.java
+++ b/dev/windows/src/com/google/gwt/dev/shell/ie/IDispatchProxy.java
@@ -170,7 +170,7 @@
                 field.getType(), javaDispatch.getFieldValue(dispId));
           } else if ((flags & (COM.DISPATCH_PROPERTYPUT | COM.DISPATCH_PROPERTYPUTREF)) != 0) {
             javaDispatch.setFieldValue(dispId, JsValueGlue.get(new JsValueIE6(
-                params[0]), field.getType(), "Setting field '"
+                params[0]), classLoader, field.getType(), "Setting field '"
                 + field.getName() + "'"));
             return new Variant();
           }
diff --git a/dev/windows/src/com/google/gwt/dev/shell/ie/JsValueIE6.java b/dev/windows/src/com/google/gwt/dev/shell/ie/JsValueIE6.java
index d4bad32..7682c8f 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
@@ -26,7 +26,6 @@
 import org.eclipse.swt.internal.ole.win32.IDispatch;
 import org.eclipse.swt.internal.ole.win32.IUnknown;
 import org.eclipse.swt.internal.win32.OS;
-import org.eclipse.swt.ole.win32.OleAutomation;
 import org.eclipse.swt.ole.win32.Variant;
 
 /**
@@ -42,7 +41,9 @@
     }
 
     public void doCleanup() {
-      variant.dispose();
+      if (variant != null) {
+        variant.dispose();
+      }
     }
   }
 
@@ -65,6 +66,52 @@
     return variant;
   }
 
+  /**
+   * Copied with modification from OleAutomation.getIDsOfNames(). The reason we
+   * don't use that method directly is that querying for typeInfo that occurs in
+   * the OleAutomation(IDispatch) constructor will cause a VM crash on some
+   * kinds of JavaScript objects, such as the window.alert function. So we do it
+   * by hand.
+   */
+  private static int[] oleAutomationGetIdsOfNames(IDispatch dispatch) {
+    String[] names = new String[] {"valueOf"};
+    int[] ids = new int[names.length];
+    int result = dispatch.GetIDsOfNames(new GUID(), names, names.length,
+        COM.LOCALE_USER_DEFAULT, ids);
+    if (result != COM.S_OK) {
+      return null;
+    }
+    return ids;
+  }
+
+  /**
+   * Copied with modification from OleAutomation.invoke(). The reason we don't
+   * use that method directly is that querying for typeInfo that occurs in the
+   * OleAutomation(IDispatch) constructor will cause a VM crash on some kinds of
+   * JavaScript objects, such as the window.alert function. So we do it by hand.
+   */
+  private static Variant oleAutomationInvoke(IDispatch dispatch, int dispId) {
+    int pVarResultAddress = 0;
+    try {
+      pVarResultAddress = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT,
+          Variant.sizeof);
+      int[] pArgErr = new int[1];
+      int hr = dispatch.Invoke(dispId, new GUID(), COM.LOCALE_USER_DEFAULT,
+          COM.DISPATCH_METHOD, new DISPPARAMS(), pVarResultAddress,
+          new EXCEPINFO(), pArgErr);
+
+      if (hr >= COM.S_OK) {
+        return Variant.win32_new(pVarResultAddress);
+      }
+    } finally {
+      if (pVarResultAddress != 0) {
+        COM.VariantClear(pVarResultAddress);
+        OS.GlobalFree(pVarResultAddress);
+      }
+    }
+    return null;
+  }
+
   // a null variant means the JsValue is undefined (void)
   private Variant variant;
 
@@ -104,6 +151,15 @@
     return variant.getInt();
   }
 
+  @Override
+  public int getJavaScriptObjectPointer() {
+    if (isJavaScriptObject()) {
+      return variant.getDispatch().getAddress();
+    } else {
+      return 0;
+    }
+  }
+
   /*
    * (non-Javadoc)
    * 
@@ -276,25 +332,22 @@
     if (variant.getType() != COM.VT_DISPATCH) {
       return false;
     }
-    OleAutomation auto = null;
+
+    // see if it has a valueOf method
+    IDispatch dispatch = variant.getDispatch();
+    int[] ids = oleAutomationGetIdsOfNames(dispatch);
+    if (ids == null) {
+      return false;
+    }
     Variant result = null;
     try {
-      auto = new OleAutomation(variant.getDispatch());
-      // see if it has a valueOf method
-      int[] ids = auto.getIDsOfNames(new String[] {"valueOf"});
-      if (ids == null) {
-        return false;
-      }
-      result = auto.invoke(ids[0]);
+      result = oleAutomationInvoke(dispatch, ids[0]);
       /*
        * If the return type of the valueOf method is string, we assume it is a
        * String wrapper object.
        */
       return result.getType() == COM.VT_BSTR;
     } finally {
-      if (auto != null) {
-        auto.dispose();
-      }
       if (result != null) {
         result.dispose();
       }
@@ -466,44 +519,26 @@
     variant = val;
   }
 
-  private <T> T tryToUnwrapWrappedJavaObject() {
-    /*
-     * This implementation copied from OleAutomation.invoke(). We used to have a
-     * varArg.getAutomation().invoke() implementation, but it turns out the
-     * querying for typeInfo that occurs in the OleAutomation(IDispatch)
-     * constructor will cause a VM crash on some kinds of JavaScript objects,
-     * such as the window.alert function. So we do it by hand.
-     */
-    IDispatch dispatch = variant.getDispatch();
-    Variant result = new Variant();
-    int pVarResultAddress = 0;
+  private Object tryToUnwrapWrappedJavaObject() {
     int globalRef = 0;
+    Variant result = null;
     try {
-      pVarResultAddress = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT,
-          Variant.sizeof);
-      int[] pArgErr = new int[1];
-      int hr = dispatch.Invoke(IDispatchProxy.DISPID_MAGIC_GETGLOBALREF,
-          new GUID(), COM.LOCALE_USER_DEFAULT, COM.DISPATCH_METHOD,
-          new DISPPARAMS(), pVarResultAddress, new EXCEPINFO(), pArgErr);
-
-      if (hr >= COM.S_OK) {
-        result = Variant.win32_new(pVarResultAddress);
+      result = oleAutomationInvoke(variant.getDispatch(),
+          IDispatchProxy.DISPID_MAGIC_GETGLOBALREF);
+      if (result != null) {
         globalRef = result.getInt();
-      }
-      if (globalRef != 0) {
-        // This is really a Java object being passed back via an IDispatchProxy.
-        IDispatchProxy proxy = (IDispatchProxy) LowLevel.objFromGlobalRefInt(globalRef);
-        return (T) proxy.getTarget();
+        if (globalRef != 0) {
+          // This is really a Java object being passed back via an
+          // IDispatchProxy.
+          IDispatchProxy proxy = (IDispatchProxy) LowLevel.objFromGlobalRefInt(globalRef);
+          return proxy.getTarget();
+        }
       }
       return null;
     } finally {
       if (result != null) {
         result.dispose();
       }
-      if (pVarResultAddress != 0) {
-        COM.VariantClear(pVarResultAddress);
-        OS.GlobalFree(pVarResultAddress);
-      }
     }
   }
 
diff --git a/dev/windows/src/com/google/gwt/dev/shell/ie/MethodDispatch.java b/dev/windows/src/com/google/gwt/dev/shell/ie/MethodDispatch.java
index 10aaaed..2d642e9 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
@@ -134,7 +134,7 @@
            * methods). If method is static, it can be null.
            */
           Object jthis = JsValueGlue.get(new JsValueIE6(params[0]),
-              method.getDeclaringClass(), "this");
+              classLoader, method.getDeclaringClass(), "this");
           Variant[] otherParams = new Variant[params.length - 1];
           System.arraycopy(params, 1, otherParams, 0, otherParams.length);
           return callMethod(classLoader, jthis, otherParams, method);
diff --git a/dev/windows/src/com/google/gwt/dev/shell/ie/SwtOleGlue.java b/dev/windows/src/com/google/gwt/dev/shell/ie/SwtOleGlue.java
index 432f8cd..5f09e9a 100644
--- a/dev/windows/src/com/google/gwt/dev/shell/ie/SwtOleGlue.java
+++ b/dev/windows/src/com/google/gwt/dev/shell/ie/SwtOleGlue.java
@@ -49,12 +49,12 @@
   /**
    * Converts an array of variants to their equivalent java objects.
    */
-  public static Object[] convertVariantsToObjects(Class<?>[] argTypes,
-      Variant[] varArgs, String msgPrefix) {
+  public static Object[] convertVariantsToObjects(CompilingClassLoader cl,
+      Class<?>[] argTypes, Variant[] varArgs, String msgPrefix) {
     Object[] javaArgs = new Object[Math.min(varArgs.length, argTypes.length)];
     for (int i = 0; i < javaArgs.length; i++) {
       try {
-        Object javaArg = JsValueGlue.get(new JsValueIE6(varArgs[i]),
+        Object javaArg = JsValueGlue.get(new JsValueIE6(varArgs[i]), cl,
             argTypes[i], msgPrefix);
         javaArgs[i] = javaArg;
       } catch (IllegalArgumentException e) {
@@ -169,11 +169,11 @@
   /**
    * Convert a Java string to a COM BSTR.
    * 
-   * Wrapper for the OS' SysAllocStringLen(), since SysAllocString() is not
-   * safe for embedded nulls.
+   * Wrapper for the OS' SysAllocStringLen(), since SysAllocString() is not safe
+   * for embedded nulls.
    */
   public static int sysAllocString(String s) {
     return COM.SysAllocStringLen(s.toCharArray(), s.length());
   }
- 
+
 }
diff --git a/eclipse/dev/linux/.classpath b/eclipse/dev/linux/.classpath
index 31ef93f..ab22cb4 100644
--- a/eclipse/dev/linux/.classpath
+++ b/eclipse/dev/linux/.classpath
@@ -4,11 +4,13 @@
 	<classpathentry kind="src" path="core/test"/>
 	<classpathentry kind="src" path="linux/src"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry sourcepath="/GWT_TOOLS/lib/apache/tapestry-util-text-4.0.2-src.zip" kind="var" path="GWT_TOOLS/lib/apache/tapestry-util-text-4.0.2.jar"/>
-	<classpathentry sourcepath="/GWT_TOOLS/lib/eclipse/org.eclipse.swt.gtk-linux-3.2.1.src.zip" kind="var" path="GWT_TOOLS/lib/eclipse/org.eclipse.swt.gtk-linux-3.2.1.jar"/>
-	<classpathentry sourcepath="/GWT_TOOLS/lib/eclipse/jdt-3.3.1-src.zip" kind="var" path="GWT_TOOLS/lib/eclipse/jdt-3.3.1.jar"/>
-	<classpathentry sourcepath="/GWT_TOOLS/lib/junit/junit-3.8.1-src.zip" kind="var" path="GWT_TOOLS/lib/junit/junit-3.8.1.jar"/>
-	<classpathentry sourcepath="/GWT_TOOLS/lib/apache/ant-1.6.5-src.zip" kind="var" path="GWT_TOOLS/lib/apache/ant-1.6.5.jar"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/eclipse/org.eclipse.swt.gtk-linux-3.2.1.jar" sourcepath="/GWT_TOOLS/lib/eclipse/org.eclipse.swt.gtk-linux-3.2.1.src.zip"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/apache/ant-1.6.5.jar" sourcepath="/GWT_TOOLS/lib/apache/ant-1.6.5-src.zip"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/apache/tapestry-util-text-4.0.2.jar" sourcepath="/GWT_TOOLS/lib/apache/tapestry-util-text-4.0.2-src.zip"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/eclipse/jdt-3.3.1.jar" sourcepath="/GWT_TOOLS/lib/eclipse/jdt-3.3.1-src.zip"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/junit/junit-3.8.1.jar" sourcepath="/GWT_TOOLS/lib/junit/junit-3.8.1-src.zip"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/objectweb/asm-3.1.jar" sourcepath="/GWT_TOOLS/lib/objectweb/asm-3.1-src.zip"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/objectweb/asm-commons-3.1.jar" sourcepath="/GWT_TOOLS/lib/objectweb/asm-3.1-src.zip"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/tomcat/ant-launcher-1.6.5.jar"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/tomcat/catalina-1.0.jar"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/tomcat/catalina-optional-1.0.jar"/>
diff --git a/eclipse/dev/mac/.classpath b/eclipse/dev/mac/.classpath
index 59479a0..27a6e1e 100644
--- a/eclipse/dev/mac/.classpath
+++ b/eclipse/dev/mac/.classpath
@@ -4,11 +4,13 @@
 	<classpathentry kind="src" path="core/test"/>
 	<classpathentry kind="src" path="mac/src"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry sourcepath="/GWT_TOOLS/lib/apache/tapestry-util-text-4.0.2-src.zip" kind="var" path="GWT_TOOLS/lib/apache/tapestry-util-text-4.0.2.jar"/>
-	<classpathentry sourcepath="/GWT_TOOLS/lib/eclipse/org.eclipse.swt.carbon-macosx-3.2.1.src.zip" kind="var" path="GWT_TOOLS/lib/eclipse/org.eclipse.swt.carbon-macosx-3.2.1.jar"/>
-	<classpathentry sourcepath="/GWT_TOOLS/lib/eclipse/jdt-3.3.1-src.zip" kind="var" path="GWT_TOOLS/lib/eclipse/jdt-3.3.1.jar"/>
-	<classpathentry sourcepath="/GWT_TOOLS/lib/junit/junit-3.8.1-src.zip" kind="var" path="GWT_TOOLS/lib/junit/junit-3.8.1.jar"/>
-	<classpathentry sourcepath="/GWT_TOOLS/lib/apache/ant-1.6.5-src.zip" kind="var" path="GWT_TOOLS/lib/apache/ant-1.6.5.jar"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/eclipse/org.eclipse.swt.carbon-macosx-3.2.1.jar" sourcepath="/GWT_TOOLS/lib/eclipse/org.eclipse.swt.carbon-macosx-3.2.1.src.zip"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/apache/ant-1.6.5.jar" sourcepath="/GWT_TOOLS/lib/apache/ant-1.6.5-src.zip"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/apache/tapestry-util-text-4.0.2.jar" sourcepath="/GWT_TOOLS/lib/apache/tapestry-util-text-4.0.2-src.zip"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/eclipse/jdt-3.3.1.jar" sourcepath="/GWT_TOOLS/lib/eclipse/jdt-3.3.1-src.zip"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/junit/junit-3.8.1.jar" sourcepath="/GWT_TOOLS/lib/junit/junit-3.8.1-src.zip"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/objectweb/asm-3.1.jar" sourcepath="/GWT_TOOLS/lib/objectweb/asm-3.1-src.zip"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/objectweb/asm-commons-3.1.jar" sourcepath="/GWT_TOOLS/lib/objectweb/asm-3.1-src.zip"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/tomcat/ant-launcher-1.6.5.jar"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/tomcat/catalina-1.0.jar"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/tomcat/catalina-optional-1.0.jar"/>
diff --git a/eclipse/dev/windows/.classpath b/eclipse/dev/windows/.classpath
index 57f7f6f..35c95e8 100644
--- a/eclipse/dev/windows/.classpath
+++ b/eclipse/dev/windows/.classpath
@@ -4,11 +4,13 @@
 	<classpathentry kind="src" path="core/test"/>
 	<classpathentry kind="src" path="windows/src"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry sourcepath="/GWT_TOOLS/lib/apache/tapestry-util-text-4.0.2-src.zip" kind="var" path="GWT_TOOLS/lib/apache/tapestry-util-text-4.0.2.jar"/>
-	<classpathentry sourcepath="/GWT_TOOLS/lib/eclipse/org.eclipse.swt.win32-win32-3.2.1.src.zip" kind="var" path="GWT_TOOLS/lib/eclipse/org.eclipse.swt.win32-win32-3.2.1.jar"/>
-	<classpathentry sourcepath="/GWT_TOOLS/lib/eclipse/jdt-3.3.1-src.zip" kind="var" path="GWT_TOOLS/lib/eclipse/jdt-3.3.1.jar"/>
-	<classpathentry sourcepath="/GWT_TOOLS/lib/junit/junit-3.8.1-src.zip" kind="var" path="GWT_TOOLS/lib/junit/junit-3.8.1.jar"/>
-	<classpathentry sourcepath="/GWT_TOOLS/lib/apache/ant-1.6.5-src.zip" kind="var" path="GWT_TOOLS/lib/apache/ant-1.6.5.jar"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/eclipse/org.eclipse.swt.win32-win32-3.2.1.jar" sourcepath="/GWT_TOOLS/lib/eclipse/org.eclipse.swt.win32-win32-3.2.1.src.zip"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/apache/ant-1.6.5.jar" sourcepath="/GWT_TOOLS/lib/apache/ant-1.6.5-src.zip"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/apache/tapestry-util-text-4.0.2.jar" sourcepath="/GWT_TOOLS/lib/apache/tapestry-util-text-4.0.2-src.zip"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/eclipse/jdt-3.3.1.jar" sourcepath="/GWT_TOOLS/lib/eclipse/jdt-3.3.1-src.zip"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/junit/junit-3.8.1.jar" sourcepath="/GWT_TOOLS/lib/junit/junit-3.8.1-src.zip"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/objectweb/asm-3.1.jar" sourcepath="/GWT_TOOLS/lib/objectweb/asm-3.1-src.zip"/>
+	<classpathentry kind="var" path="GWT_TOOLS/lib/objectweb/asm-commons-3.1.jar" sourcepath="/GWT_TOOLS/lib/objectweb/asm-3.1-src.zip"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/tomcat/ant-launcher-1.6.5.jar"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/tomcat/catalina-1.0.jar"/>
 	<classpathentry kind="var" path="GWT_TOOLS/lib/tomcat/catalina-optional-1.0.jar"/>
diff --git a/eclipse/jni/linux/.cdtbuild b/eclipse/jni/linux/.cdtbuild
deleted file mode 100644
index 95a1c73..0000000
--- a/eclipse/jni/linux/.cdtbuild
+++ /dev/null
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<?fileVersion 3.1.0?>
-
-<ManagedProjectBuildInfo>
-<project id="JNI.cdt.managedbuild.target.gnu.so.1993024758" name="Shared Library (Gnu)" projectType="cdt.managedbuild.target.gnu.so">
-<configuration artifactExtension="so" artifactName="gwt-ll" cleanCommand="rm -rf" description="" errorParsers="org.eclipse.cdt.core.MakeErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GLDErrorParser;org.eclipse.cdt.core.GASErrorParser" id="cdt.managedbuild.config.gnu.so.debug.398050485" name="Debug" parent="cdt.managedbuild.config.gnu.so.debug">
-<toolChain id="cdt.managedbuild.toolchain.gnu.so.debug.1364127860" name="GCC Tool Chain" superClass="cdt.managedbuild.toolchain.gnu.so.debug">
-<tool command="gcc" id="cdt.managedbuild.tool.gnu.cpp.compiler.so.debug.1760328332" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.so.debug">
-<option id="gnu.cpp.compiler.option.include.paths.1542704606" superClass="gnu.cpp.compiler.option.include.paths" valueType="includePath">
-<listOptionValue builtIn="false" value="/usr/lib/j2sdk1.5-sun/include"/>
-<listOptionValue builtIn="false" value="/usr/lib/j2sdk1.5-sun/include/linux"/>
-<listOptionValue builtIn="false" value="&quot;${workspace_loc:/gwt-tools/sdk/mozilla-1.7.12/include}&quot;"/>
-<listOptionValue builtIn="false" value="&quot;${workspace_loc:/gwt-tools/sdk/mozilla-1.7.12/include/extra}&quot;"/>
-<listOptionValue builtIn="false" value="/usr/local/google/home/jat/src/gwt-trunk/build/out/jni/linux"/>
-</option>
-<option id="gnu.cpp.compiler.option.preprocessor.def.1306763452" superClass="gnu.cpp.compiler.option.preprocessor.def" valueType="definedSymbols">
-<listOptionValue builtIn="false" value="_REENTRANT"/>
-<listOptionValue builtIn="false" value="NO_NSPR_10_SUPPORT"/>
-</option>
-<option id="gnu.cpp.compiler.option.optimization.flags.1738733922" superClass="gnu.cpp.compiler.option.optimization.flags" value="-fno-omit-frame-pointer -fno-strict-aliasing" valueType="string"/>
-<option id="gnu.cpp.compiler.option.other.other.284396253" superClass="gnu.cpp.compiler.option.other.other" value="-c -fmessage-length=0 -fPIC -MMD -MP -Wno-system-headers -Os" valueType="string"/>
-<option id="gnu.cpp.compiler.so.debug.option.optimization.level.1987818635" superClass="gnu.cpp.compiler.so.debug.option.optimization.level" value="gnu.cpp.compiler.optimization.level.optimize" valueType="enumerated"/>
-<option id="gnu.cpp.compiler.option.warnings.allwarn.1524758494" superClass="gnu.cpp.compiler.option.warnings.allwarn" value="false" valueType="boolean"/>
-</tool>
-<tool id="cdt.managedbuild.tool.gnu.c.compiler.so.debug.363855699" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.so.debug"/>
-<tool id="cdt.managedbuild.tool.gnu.c.linker.so.debug.44069761" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.so.debug"/>
-<tool id="cdt.managedbuild.tool.gnu.cpp.linker.so.debug.127620842" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.so.debug">
-<option id="gnu.cpp.link.option.strip.1852166367" superClass="gnu.cpp.link.option.strip" value="true" valueType="boolean"/>
-<option id="gnu.cpp.link.option.libs.842521175" superClass="gnu.cpp.link.option.libs" valueType="libs">
-<listOptionValue builtIn="false" value="xpcomglue_s"/>
-</option>
-<option id="gnu.cpp.link.option.paths.1454970074" superClass="gnu.cpp.link.option.paths" valueType="stringList">
-<listOptionValue builtIn="false" value="&quot;${workspace_loc:/gwt-tools/sdk/mozilla-1.7.12/lib}&quot;"/>
-</option>
-</tool>
-<tool id="cdt.managedbuild.tool.gnu.assembler.so.debug.455913220" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.so.debug"/>
-<macros/>
-</toolChain>
-</configuration>
-<configuration artifactExtension="so" artifactName="libgwt-ll" cleanCommand="rm -rf" description="" errorParsers="org.eclipse.cdt.core.MakeErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GLDErrorParser;org.eclipse.cdt.core.GASErrorParser" id="cdt.managedbuild.config.gnu.so.release.196519812" name="Release" parent="cdt.managedbuild.config.gnu.so.release">
-<toolChain id="cdt.managedbuild.toolchain.gnu.so.release.309670679" name="GCC Tool Chain" superClass="cdt.managedbuild.toolchain.gnu.so.release">
-<tool id="cdt.managedbuild.tool.gnu.cpp.compiler.so.release.639871433" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.so.release">
-<option id="gnu.cpp.compiler.option.preprocessor.def.225251886" superClass="gnu.cpp.compiler.option.preprocessor.def" valueType="definedSymbols">
-<listOptionValue builtIn="false" value="_REENTRANT"/>
-<listOptionValue builtIn="false" value="NO_NSPR_10_SUPPORT"/>
-</option>
-<option id="gnu.cpp.compiler.option.include.paths.2037074199" superClass="gnu.cpp.compiler.option.include.paths" valueType="includePath">
-<listOptionValue builtIn="false" value="&quot;${workspace_loc:/jni-linux/jdk-include}&quot;"/>
-<listOptionValue builtIn="false" value="&quot;${workspace_loc:/jni-linux/jdk-include/linux}&quot;"/>
-<listOptionValue builtIn="false" value="&quot;${workspace_loc:/jni-linux/headers-gen}&quot;"/>
-<listOptionValue builtIn="false" value="&quot;${workspace_loc:/jni-linux/mozilla-sdk/include}&quot;"/>
-<listOptionValue builtIn="false" value="&quot;${workspace_loc:/jni-linux/mozilla-sdk/include/extra}&quot;"/>
-</option>
-<option id="gnu.cpp.compiler.option.optimization.flags.324816514" superClass="gnu.cpp.compiler.option.optimization.flags" value="-Os -fPIC -fno-omit-frame-pointer -fno-strict-aliasing" valueType="string"/>
-<option id="gnu.cpp.compiler.option.warnings.allwarn.98479430" superClass="gnu.cpp.compiler.option.warnings.allwarn" value="false" valueType="boolean"/>
-<option id="gnu.cpp.compiler.option.other.other.1762086408" superClass="gnu.cpp.compiler.option.other.other" value="-c -fmessage-length=0 -Wno-system-headers" valueType="string"/>
-</tool>
-<tool id="cdt.managedbuild.tool.gnu.c.compiler.so.release.861896716" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.so.release"/>
-<tool id="cdt.managedbuild.tool.gnu.c.linker.so.release.1242435463" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.so.release"/>
-<tool id="cdt.managedbuild.tool.gnu.cpp.linker.so.release.696243428" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.so.release">
-<option id="gnu.cpp.link.option.strip.1678101635" superClass="gnu.cpp.link.option.strip" value="true" valueType="boolean"/>
-<option id="gnu.cpp.link.option.libs.1391450429" superClass="gnu.cpp.link.option.libs" valueType="libs">
-<listOptionValue builtIn="false" value="xpcomglue_s"/>
-</option>
-<option id="gnu.cpp.link.option.paths.1828386123" superClass="gnu.cpp.link.option.paths" valueType="stringList">
-<listOptionValue builtIn="false" value="&quot;${workspace_loc:/jni-linux/mozilla-sdk/lib}&quot;"/>
-</option>
-<option id="gnu.cpp.link.option.flags.1381564345" superClass="gnu.cpp.link.option.flags" value="-fPIC -Wl,shared-gcc" valueType="string"/>
-</tool>
-<tool id="cdt.managedbuild.tool.gnu.assembler.so.release.1277116837" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.so.release"/>
-<macros/>
-</toolChain>
-</configuration>
-<macros/>
-</project>
-</ManagedProjectBuildInfo>
diff --git a/eclipse/jni/linux/.cdtproject b/eclipse/jni/linux/.cdtproject
deleted file mode 100644
index fcb39a2..0000000
--- a/eclipse/jni/linux/.cdtproject
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<?eclipse-cdt version="2.0"?>
-
-<cdtproject id="org.eclipse.cdt.managedbuilder.core.managedMake">
-<extension id="org.eclipse.cdt.managedbuilder.core.ManagedBuildManager" point="org.eclipse.cdt.core.ScannerInfoProvider"/>
-<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
-<extension id="org.eclipse.cdt.core.GNU_ELF" point="org.eclipse.cdt.core.BinaryParser">
-<attribute key="addr2line" value="addr2line"/>
-<attribute key="c++filt" value="c++filt"/>
-</extension>
-<data>
-<item id="org.eclipse.cdt.core.pathentry">
-<pathentry kind="src" path=""/>
-<pathentry kind="out" path=""/>
-<pathentry kind="con" path="org.eclipse.cdt.managedbuilder.MANAGED_CONTAINER"/>
-</item>
-</data>
-</cdtproject>
diff --git a/eclipse/jni/linux/.cproject b/eclipse/jni/linux/.cproject
new file mode 100644
index 0000000..22bf39c
--- /dev/null
+++ b/eclipse/jni/linux/.cproject
@@ -0,0 +1,326 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?fileVersion 4.0.0?>
+
+<cproject>
+<storageModule moduleId="org.eclipse.cdt.core.settings">
+<cconfiguration id="cdt.managedbuild.config.gnu.so.debug.398050485">
+<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.config.gnu.so.debug.398050485" moduleId="org.eclipse.cdt.core.settings" name="Release">
+<extensions>
+<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+<extension id="org.eclipse.cdt.core.MakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+</extensions>
+</storageModule>
+<storageModule moduleId="cdtBuildSystem" version="4.0.0">
+<configuration artifactExtension="so" artifactName="gwt-ll" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.sharedLib" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.sharedLib,org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.debug" cleanCommand="rm -rf" description="" errorParsers="org.eclipse.cdt.core.MakeErrorParser" id="cdt.managedbuild.config.gnu.so.debug.398050485" name="Release" parent="cdt.managedbuild.config.gnu.so.debug" postannouncebuildStep="" postbuildStep="" preannouncebuildStep="" prebuildStep="">
+<folderInfo id="cdt.managedbuild.config.gnu.so.debug.398050485.561354823" name="/" resourcePath="">
+<toolChain errorParsers="" id="cdt.managedbuild.toolchain.gnu.so.debug.1364127860" name="GCC Tool Chain" nonInternalBuilderId="cdt.managedbuild.target.gnu.builder.so.debug" superClass="cdt.managedbuild.toolchain.gnu.so.debug">
+<targetPlatform binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.target.gnu.platform.so.debug.761504216" name="Debug Platform" superClass="cdt.managedbuild.target.gnu.platform.so.debug"/>
+<builder autoBuildTarget="all" buildPath="${workspace_loc:/jni-linux/Release}" cleanBuildTarget="clean" enableAutoBuild="true" enableCleanBuild="true" enabledIncrementalBuild="true" errorParsers="" id="org.eclipse.cdt.build.core.internal.builder.1220428271" incrementalBuildTarget="all" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="CDT Internal Builder" parallelizationNumber="1" stopOnErr="false" superClass="org.eclipse.cdt.build.core.internal.builder"/>
+<tool command="g++" commandLinePattern="${COMMAND} ${FLAGS} ${OUTPUT_FLAG}${OUTPUT_PREFIX}${OUTPUT} ${INPUTS}" errorParsers="org.eclipse.cdt.core.GCCErrorParser" id="cdt.managedbuild.tool.gnu.cpp.compiler.so.debug.1760328332" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.so.debug">
+<option id="gnu.cpp.compiler.option.include.paths.1542704606" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" valueType="includePath">
+<listOptionValue builtIn="false" value="&quot;${workspace_loc:/jni-linux/build}&quot;"/>
+<listOptionValue builtIn="false" value="&quot;${workspace_loc:/jni-linux/jdk-include}&quot;"/>
+<listOptionValue builtIn="false" value="&quot;${workspace_loc:/jni-linux/jdk-include/linux}&quot;"/>
+<listOptionValue builtIn="false" value="&quot;${workspace_loc:/jni-linux/mozilla-sdk/include}&quot;"/>
+<listOptionValue builtIn="false" value="&quot;${workspace_loc:/jni-linux/mozilla-sdk/include/extra}&quot;"/>
+</option>
+<option id="gnu.cpp.compiler.option.preprocessor.def.1306763452" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" valueType="definedSymbols">
+<listOptionValue builtIn="false" value="_REENTRANT"/>
+<listOptionValue builtIn="false" value="NO_NSPR_10_SUPPORT"/>
+</option>
+<option id="gnu.cpp.compiler.option.optimization.flags.1738733922" name="Other optimization flags" superClass="gnu.cpp.compiler.option.optimization.flags" value="-fno-omit-frame-pointer -fno-strict-aliasing" valueType="string"/>
+<option id="gnu.cpp.compiler.option.other.other.284396253" name="Other flags" superClass="gnu.cpp.compiler.option.other.other" value="-c -ggdb -m32 -fPIC -Wno-system-headers -Os" valueType="string"/>
+<option id="gnu.cpp.compiler.so.debug.option.optimization.level.1987818635" name="Optimization Level" superClass="gnu.cpp.compiler.so.debug.option.optimization.level" value="gnu.cpp.compiler.optimization.level.none" valueType="enumerated"/>
+<option id="gnu.cpp.compiler.option.warnings.allwarn.1524758494" name="All warnings (-Wall)" superClass="gnu.cpp.compiler.option.warnings.allwarn" value="false" valueType="boolean"/>
+<option id="gnu.cpp.compiler.so.debug.option.debugging.level.416622328" name="Debug Level" superClass="gnu.cpp.compiler.so.debug.option.debugging.level" value="gnu.cpp.compiler.debugging.level.none" valueType="enumerated"/>
+<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.44585156" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
+</tool>
+<tool command="gcc" commandLinePattern="${COMMAND} ${FLAGS} ${OUTPUT_FLAG}${OUTPUT_PREFIX}${OUTPUT} ${INPUTS}" errorParsers="org.eclipse.cdt.core.GCCErrorParser" id="cdt.managedbuild.tool.gnu.c.compiler.so.debug.363855699" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.so.debug">
+<option defaultValue="gnu.c.optimization.level.none" id="gnu.c.compiler.so.debug.option.optimization.level.1969495705" name="Optimization Level" superClass="gnu.c.compiler.so.debug.option.optimization.level" valueType="enumerated"/>
+<option id="gnu.c.compiler.so.debug.option.debugging.level.1292178444" name="Debug Level" superClass="gnu.c.compiler.so.debug.option.debugging.level" value="gnu.c.debugging.level.max" valueType="enumerated"/>
+<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1996564179" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
+</tool>
+<tool id="cdt.managedbuild.tool.gnu.c.linker.so.debug.44069761" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.so.debug">
+<option defaultValue="true" id="gnu.c.link.so.debug.option.shared.2039191396" name="Shared (-shared)" superClass="gnu.c.link.so.debug.option.shared" valueType="boolean"/>
+</tool>
+<tool command="g++" commandLinePattern="${COMMAND} ${FLAGS} ${OUTPUT_FLAG}${OUTPUT_PREFIX}${OUTPUT} ${INPUTS}" errorParsers="org.eclipse.cdt.core.GLDErrorParser" id="cdt.managedbuild.tool.gnu.cpp.linker.so.debug.127620842" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.so.debug">
+<option id="gnu.cpp.link.option.strip.1852166367" name="Omit all symbol information (-s)" superClass="gnu.cpp.link.option.strip" value="true" valueType="boolean"/>
+<option id="gnu.cpp.link.option.libs.842521175" name="Libraries (-l)" superClass="gnu.cpp.link.option.libs" valueType="libs">
+<listOptionValue builtIn="false" value="xpcomglue_s"/>
+</option>
+<option id="gnu.cpp.link.option.paths.1454970074" name="Library search path (-L)" superClass="gnu.cpp.link.option.paths" valueType="stringList">
+<listOptionValue builtIn="false" value="&quot;${workspace_loc:/jni-linux/mozilla-sdk/lib}&quot;"/>
+</option>
+<option defaultValue="true" id="gnu.cpp.link.so.debug.option.shared.1661107847" name="Shared (-shared)" superClass="gnu.cpp.link.so.debug.option.shared" valueType="boolean"/>
+<option id="gnu.cpp.link.option.flags.502453637" name="Linker flags" superClass="gnu.cpp.link.option.flags" value="-fPIC -Wl,-shared-gcc" valueType="string"/>
+</tool>
+<tool command="as" commandLinePattern="${COMMAND} ${FLAGS} ${OUTPUT_FLAG}${OUTPUT_PREFIX}${OUTPUT} ${INPUTS}" errorParsers="org.eclipse.cdt.core.GASErrorParser" id="cdt.managedbuild.tool.gnu.assembler.so.debug.455913220" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.so.debug">
+<inputType id="cdt.managedbuild.tool.gnu.assembler.input.1120557201" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
+</tool>
+<tool id="cdt.managedbuild.tool.gnu.archiver.base.883660164" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/>
+</toolChain>
+</folderInfo>
+</configuration>
+</storageModule>
+<storageModule moduleId="scannerConfiguration">
+<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile"/>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="makefileGenerator">
+<runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.so.debug.398050485;cdt.managedbuild.config.gnu.so.debug.398050485.561354823;cdt.managedbuild.tool.gnu.c.compiler.so.debug.363855699;cdt.managedbuild.tool.gnu.c.compiler.input.1996564179">
+<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC"/>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="makefileGenerator">
+<runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="false" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+</scannerConfigBuildInfo>
+<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.so.debug.398050485;cdt.managedbuild.config.gnu.so.debug.398050485.561354823;cdt.managedbuild.tool.gnu.cpp.compiler.so.debug.1760328332;cdt.managedbuild.tool.gnu.cpp.compiler.input.44585156">
+<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP"/>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="true"/>
+</buildOutputProvider>
+<scannerInfoProvider id="makefileGenerator">
+<runAction arguments="-f ${project_name}_scd.mk" command="make" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileCPP">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.cpp" command="g++" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfileC">
+<buildOutputProvider>
+<openAction enabled="true" filePath=""/>
+<parser enabled="false"/>
+</buildOutputProvider>
+<scannerInfoProvider id="specsFile">
+<runAction arguments="-E -P -v -dD ${plugin_state_location}/specs.c" command="gcc" useDefault="true"/>
+<parser enabled="true"/>
+</scannerInfoProvider>
+</profile>
+</scannerConfigBuildInfo>
+</storageModule>
+<storageModule moduleId="org.eclipse.cdt.core.language.mapping">
+<project-mappings/>
+</storageModule>
+<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"/>
+</cconfiguration>
+</storageModule>
+<storageModule moduleId="cdtBuildSystem" version="4.0.0">
+<project id="JNI.cdt.managedbuild.target.gnu.so.1993024758" name="Shared Library (Gnu)" projectType="cdt.managedbuild.target.gnu.so"/>
+</storageModule>
+</cproject>
diff --git a/eclipse/jni/linux/.project b/eclipse/jni/linux/.project
index a5e1233..5acdfff 100644
--- a/eclipse/jni/linux/.project
+++ b/eclipse/jni/linux/.project
@@ -9,13 +9,75 @@
 		<buildCommand>
 			<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
 			<arguments>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.fullBuildTarget</key>
+					<value>all</value>
+				</dictionary>
+				<dictionary>
+					<key>?name?</key>
+					<value></value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.enableAutoBuild</key>
+					<value>true</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.buildLocation</key>
+					<value>${workspace_loc:/jni-linux/Release}</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.enableFullBuild</key>
+					<value>true</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.enableCleanBuild</key>
+					<value>true</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.cleanBuildTarget</key>
+					<value>clean</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.append_environment</key>
+					<value>true</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.contents</key>
+					<value>org.eclipse.cdt.make.core.activeConfigSettings</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.useDefaultBuildCmd</key>
+					<value>true</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.buildArguments</key>
+					<value></value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.buildCommand</key>
+					<value>make</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.autoBuildTarget</key>
+					<value>all</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.stopOnError</key>
+					<value>false</value>
+				</dictionary>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
+			<arguments>
 			</arguments>
 		</buildCommand>
 	</buildSpec>
 	<natures>
 		<nature>org.eclipse.cdt.core.cnature</nature>
-		<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
+		<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
 		<nature>org.eclipse.cdt.core.ccnature</nature>
+		<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
 	</natures>
 	<linkedResources>
 		<link>
@@ -29,6 +91,11 @@
 			<locationURI>JAVA_HOME/include</locationURI>
 		</link>
 		<link>
+			<name>build</name>
+			<type>2</type>
+			<locationURI>GWT_ROOT/build/out/jni/linux</locationURI>
+		</link>
+		<link>
 			<name>mozilla-sdk</name>
 			<type>2</type>
 			<locationURI>GWT_TOOLS/sdk/mozilla-1.7.12</locationURI>
@@ -38,10 +105,5 @@
 			<type>2</type>
 			<locationURI>GWT_ROOT/jni/core</locationURI>
 		</link>
-		<link>
-			<name>headers-gen</name>
-			<type>2</type>
-			<locationURI>GWT_ROOT/build/out/jni/linux</locationURI>
-		</link>
 	</linkedResources>
 </projectDescription>
diff --git a/eclipse/jni/linux/.settings/org.eclipse.cdt.core.prefs b/eclipse/jni/linux/.settings/org.eclipse.cdt.core.prefs
deleted file mode 100644
index cbbb52e..0000000
--- a/eclipse/jni/linux/.settings/org.eclipse.cdt.core.prefs
+++ /dev/null
@@ -1,3 +0,0 @@
-#Thu Jan 25 19:11:27 GMT-05:00 2007
-eclipse.preferences.version=1
-indexerId=org.eclipse.cdt.core.fastIndexer
diff --git a/eclipse/jni/linux/.settings/org.eclipse.cdt.managedbuilder.core.prefs b/eclipse/jni/linux/.settings/org.eclipse.cdt.managedbuilder.core.prefs
deleted file mode 100644
index 7ee397d..0000000
--- a/eclipse/jni/linux/.settings/org.eclipse.cdt.managedbuilder.core.prefs
+++ /dev/null
@@ -1,13 +0,0 @@
-#Thu Jan 25 19:30:13 GMT-05:00 2007
-cdt.managedbuild.config.gnu.so.debug.398050485/internalBuilder/enabled=false
-cdt.managedbuild.config.gnu.so.debug.398050485/internalBuilder/ignoreErr=true
-cdt.managedbuild.config.gnu.so.release.196519812/internalBuilder/enabled=false
-cdt.managedbuild.config.gnu.so.release.196519812/internalBuilder/ignoreErr=true
-eclipse.preferences.version=1
-environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.so.debug.398050485=<?xml version\="1.0" encoding\="UTF-8"?>\n<environment>\n<variable name\="CPATH" operation\="remove"/>\n<variable name\="C_INCLUDE_PATH" operation\="remove"/>\n<variable name\="CPLUS_INCLUDE_PATH" operation\="remove"/>\n</environment>\n
-environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.so.release.196519812=<?xml version\="1.0" encoding\="UTF-8"?>\n<environment>\n<variable name\="CPATH" operation\="remove"/>\n<variable name\="C_INCLUDE_PATH" operation\="remove"/>\n<variable name\="CPLUS_INCLUDE_PATH" operation\="remove"/>\n</environment>\n
-environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.so.debug.398050485=<?xml version\="1.0" encoding\="UTF-8"?>\n<environment>\n<variable name\="LIBRARY_PATH" operation\="remove"/>\n</environment>\n
-environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.so.release.196519812=<?xml version\="1.0" encoding\="UTF-8"?>\n<environment>\n<variable name\="LIBRARY_PATH" operation\="remove"/>\n</environment>\n
-environment/project=<?xml version\="1.0" encoding\="UTF-8"?>\n<environment/>\n
-environment/project/cdt.managedbuild.config.gnu.so.debug.398050485=<?xml version\="1.0" encoding\="UTF-8"?>\n<environment/>\n
-environment/project/cdt.managedbuild.config.gnu.so.release.196519812=<?xml version\="1.0" encoding\="UTF-8"?>\n<environment/>\n
diff --git a/eclipse/user/.classpath b/eclipse/user/.classpath
index e7b1f4c..7002c3f 100644
--- a/eclipse/user/.classpath
+++ b/eclipse/user/.classpath
@@ -1,12 +1,13 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
 	<classpathentry kind="src" path="core/src"/>
+	<classpathentry kind="src" path="gen"/>
 	<classpathentry kind="src" path="core/javadoc"/>
 	<classpathentry kind="src" path="core/test"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
 	<classpathentry exported="true" kind="var" path="GWT_TOOLS/lib/apache/tapestry-util-text-4.0.2.jar" sourcepath="/GWT_TOOLS/lib/apache/tapestry-util-text-4.0.2-src.zip"/>
 	<classpathentry exported="true" kind="var" path="GWT_TOOLS/lib/junit/junit-3.8.1.jar" sourcepath="/GWT_TOOLS/lib/junit/junit-3.8.1-src.zip"/>
 	<classpathentry exported="true" kind="var" path="GWT_TOOLS/lib/tomcat/servlet-api-2.4.jar" sourcepath="/GWT_TOOLS/lib/tomcat/jakarta-tomcat-5.0.28-src.zip"/>
-	<classpathentry combineaccessrules="false" kind="src" path="/gwt-dev-windows"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/gwt-dev-linux"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/jni/linux/JsValueMoz.cpp b/jni/linux/JsValueMoz.cpp
index be30ea4..1faa49f 100644
--- a/jni/linux/JsValueMoz.cpp
+++ b/jni/linux/JsValueMoz.cpp
@@ -381,6 +381,24 @@
 }
 
 /**
+ * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
+ * Method:    _getObjectPointer()
+ * Signature: (I)I
+ */
+extern "C" JNIEXPORT jint JNICALL
+Java_com_google_gwt_dev_shell_moz_JsValueMoz__1getObjectPointer
+  (JNIEnv* jniEnv, jclass, jint jsRootedValueInt)
+{
+  JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
+      (jsRootedValueInt);
+  Tracer tracer("JsValueMoz._getObjectPointer", jsRootedValue);
+  JSObject* ptr = jsRootedValue->getObject();
+  int val = reinterpret_cast<int>(ptr);
+  tracer.log("value=%d", val);
+  return val;
+}
+
+/**
  * Return a Javascript string as a Java string.
  * 
  * Class:     com_google_gwt_dev_shell_moz_JsValueMoz
@@ -454,12 +472,11 @@
   JsRootedValue* jsRootedValue = reinterpret_cast<JsRootedValue*>
       (jsRootedValueInt);
   Tracer tracer("JsValueMoz._getWrappedJavaObject", jsRootedValue);
-  jsval val = jsRootedValue->getValue();
-  if(!JSVAL_IS_OBJECT(val)) {
+  JSObject* jsObject = jsRootedValue->getObject();
+  if(!jsObject) {
     tracer.throwHostedModeException(jniEnv, "Javascript value not an object");
     return 0;
   }
-  JSObject* jsObject = JSVAL_TO_OBJECT(val);
   JSContext* cx = JsRootedValue::currentContext();
   if(!JS_InstanceOf(cx, jsObject, &gwt_nativewrapper_class, 0)) {
     tracer.throwHostedModeException(jniEnv,
diff --git a/jni/linux/prebuilt/libgwt-ll.so b/jni/linux/prebuilt/libgwt-ll.so
index 48351ee..e941084 100755
--- a/jni/linux/prebuilt/libgwt-ll.so
+++ b/jni/linux/prebuilt/libgwt-ll.so
Binary files differ
diff --git a/user/src/com/google/gwt/core/client/Impl.java b/user/src/com/google/gwt/core/client/Impl.java
index cb37219..8cb8903 100644
--- a/user/src/com/google/gwt/core/client/Impl.java
+++ b/user/src/com/google/gwt/core/client/Impl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Google Inc.
+ * Copyright 2008 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,19 +23,8 @@
     return ++sNextHashId;
   }
 
-  /*
-   * We need a separate overload for JavaScriptObject, so that the hosted mode
-   * JSNI invocation system will know to unwrap the Java object back to its
-   * underlying JavaScript object.
-   */
-  static native int getHashCode(JavaScriptObject o) /*-{
-    return (o == null) ? 0 : 
-     (o.$H ? o.$H : (o.$H = @com.google.gwt.core.client.Impl::getNextHashId()()));
-  }-*/;
-
   static native int getHashCode(Object o) /*-{
-    return (o == null) ? 0 : 
-     (o.$H ? o.$H : (o.$H = @com.google.gwt.core.client.Impl::getNextHashId()()));
+    return o.$H || (o.$H = @com.google.gwt.core.client.Impl::getNextHashId()());
   }-*/;
 
   static native String getHostPageBaseURL() /*-{
diff --git a/user/src/com/google/gwt/core/client/JavaScriptObject.java b/user/src/com/google/gwt/core/client/JavaScriptObject.java
index f9524c2..274d2da 100644
--- a/user/src/com/google/gwt/core/client/JavaScriptObject.java
+++ b/user/src/com/google/gwt/core/client/JavaScriptObject.java
@@ -51,52 +51,54 @@
     return {};
   }-*/;
 
-  private static native boolean equalsImpl(JavaScriptObject o,
-      JavaScriptObject other) /*-{
-    return o === other;
-  }-*/;
-
-  private static native String toStringImpl(JavaScriptObject o) /*-{
-    if (o.toString)
-      return o.toString();
-    return "[object]";
-  }-*/;
-
   /**
-   * The underlying JavaScript object. This is used internally and should never
-   * be accessed by client code.
-   */
-  protected Object hostedModeReference;
-
-  /**
-   * Not directly instantiable.  Subclasses should also define a protected
-   * no-arg constructor to prevent client code from directly instantiating
-   * the class.
+   * Not directly instantiable. All subclasses must also define a protected,
+   * empty, no-arg constructor.
    */
   protected JavaScriptObject() {
   }
 
-  @Override
-  public boolean equals(Object other) {
-    if (!(other instanceof JavaScriptObject)) {
-      return false;
-    }
-    return equalsImpl(this, (JavaScriptObject) other);
-  }
-
-  @Override
-  public int hashCode() {
-    return Impl.getHashCode(this);
+  /**
+   * A helper method to enable cross-casting from any {@link JavaScriptObject}
+   * type to any other {@link JavaScriptObject} type.
+   * 
+   * @param <T> the target type
+   * @return this object as a different type
+   */
+  @SuppressWarnings("unchecked")
+  public final <T extends JavaScriptObject> T cast() {
+    return (T) this;
   }
   
+  /**
+   * Returns <code>true</code> if the objects are JavaScript identical
+   * (triple-equals).
+   */
   @Override
-  public String toString() {
-    /*
-     * Hosted mode will marshal an explicit argument from a JavaScriptObject
-     * back to its underlying object, but it won't currently do that for the
-     * implicit "this" arg. For now, can't implement instance methods on JSO
-     * directly as natives, so use a delegator.
-     */
-    return toStringImpl(this);
+  public final boolean equals(Object other) {
+    return super.equals(other);
   }
+
+  /**
+   * Uses a monotonically increasing counter to assign a hash code to the
+   * underlying JavaScript object. Do not call this method on non-modifiable
+   * JavaScript objects.
+   * 
+   * TODO: if the underlying object defines a 'hashCode' method maybe use that?
+   * 
+   * @return the hash code of the object
+   */
+  @Override
+  public final int hashCode() {
+    return Impl.getHashCode(this);
+  }
+
+  /**
+   * Returns the results of calling <code>toString</code> in JavaScript on the
+   * object, if the object implements toString; otherwise returns "[JavaScriptObject]".
+   */
+  @Override
+  public final native String toString() /*-{
+    return this.toString ? this.toString() : "[JavaScriptObject]";
+  }-*/;
 }
diff --git a/user/src/com/google/gwt/json/client/JSONArray.java b/user/src/com/google/gwt/json/client/JSONArray.java
index 5a47234..b9d24a2 100644
--- a/user/src/com/google/gwt/json/client/JSONArray.java
+++ b/user/src/com/google/gwt/json/client/JSONArray.java
@@ -58,7 +58,12 @@
     }
     JSONValue wrapped = null;
     if (rawTest(index)) {
-      wrapped = JSONParser.buildValue(rawGet(index));
+      Object o = rawGet(index);
+      if (o instanceof String) {
+        wrapped = new JSONString((String) o);
+      } else {
+        wrapped = JSONParser.buildValue((JavaScriptObject) o);
+      }
       rawSet(index, null);
     }
     wrappedSet(index, wrapped);
@@ -122,16 +127,16 @@
     return [];
   }-*/;
 
-  private native JavaScriptObject rawGet(int index) /*-{
+  private native Object rawGet(int index) /*-{
     var x = this.@com.google.gwt.json.client.JSONArray::javascriptArray[index];
-    if (typeof x == 'number' || typeof x == 'string' || typeof x == 'array' || typeof x == 'boolean') {
+    if (typeof x == 'number' || typeof x == 'array' || typeof x == 'boolean') {
       x = (Object(x));
     }
     return x;
   }-*/;
 
-  private native void rawSet(int index, JavaScriptObject jsObject) /*-{
-    this.@com.google.gwt.json.client.JSONArray::javascriptArray[index] = jsObject; 
+  private native void rawSet(int index, Object value) /*-{
+    this.@com.google.gwt.json.client.JSONArray::javascriptArray[index] = value;
   }-*/;
 
   private native boolean rawTest(int index) /*-{
diff --git a/user/src/com/google/gwt/json/client/JSONObject.java b/user/src/com/google/gwt/json/client/JSONObject.java
index b6462a7..b1fe522 100644
--- a/user/src/com/google/gwt/json/client/JSONObject.java
+++ b/user/src/com/google/gwt/json/client/JSONObject.java
@@ -48,7 +48,7 @@
     frontStore[String(key)] = jsonValue;
   }-*/;
 
-  private static native JavaScriptObject removeBack(JavaScriptObject backStore, String key) /*-{
+  private static native Object removeBack(JavaScriptObject backStore, String key) /*-{
     key = String(key);
     var result = backStore[key];
     delete backStore[key];
@@ -104,8 +104,12 @@
     }
     JSONValue result = getFront(frontStore, key);
     if (result == null && containsBack(backStore, key)) {
-      JavaScriptObject jso = removeBack(backStore, key);
-      result = JSONParser.buildValue(jso);
+      Object o = removeBack(backStore, key);
+      if (o instanceof String) {
+        result = new JSONString((String) o);
+      } else {
+        result = JSONParser.buildValue((JavaScriptObject) o);
+      }
       putFront(frontStore, key, result);
     }
     return result;
diff --git a/user/src/com/google/gwt/json/client/JSONParser.java b/user/src/com/google/gwt/json/client/JSONParser.java
index 597e379..ffda1c6 100644
--- a/user/src/com/google/gwt/json/client/JSONParser.java
+++ b/user/src/com/google/gwt/json/client/JSONParser.java
@@ -46,8 +46,12 @@
       throw new IllegalArgumentException("empty argument");
     }
     try {
-      JavaScriptObject jsonObject = evaluate(jsonString);
-      return buildValue(jsonObject);
+      Object object = evaluate(jsonString);
+      if (object instanceof String) {
+        return new JSONString((String) object);
+      } else {
+        return buildValue((JavaScriptObject) object);
+      }
     } catch (JavaScriptException ex) {
       throw new JSONException(ex);
     }
@@ -74,10 +78,6 @@
       return JSONBoolean.getInstance(asBoolean(jsValue));
     }
 
-    if (isString(jsValue)) {
-      return new JSONString(asString(jsValue));
-    }
-
     if (isDouble(jsValue)) {
       return new JSONNumber(asDouble(jsValue));
     }
@@ -117,23 +117,12 @@
   }-*/;
 
   /**
-   * Returns the Javascript String as a Java String. This method assumes that
-   * {@link #isString(JavaScriptObject)} returned <code>true</code>.
-   * 
-   * @param jsValue JavaScript object to convert
-   * @return the String represented by the jsValue
+   * This method converts the json string into either a String or a a by simply
+   * evaluating the string in JavaScript.
    */
-  private static native String asString(JavaScriptObject jsValue) /*-{
-    return jsValue;
-  }-*/;
-
-  /*
-   * This method converts the json string into a JavaScriptObject inside of JSNI
-   * method by simply evaluating the string in JavaScript.
-   */
-  private static native JavaScriptObject evaluate(String jsonString) /*-{
+  private static native Object evaluate(String jsonString) /*-{
     var x = eval('(' + jsonString + ')');
-    if (typeof x == 'number' || typeof x == 'string' || typeof x == 'array' || typeof x == 'boolean') {
+    if (typeof x == 'number' || typeof x == 'array' || typeof x == 'boolean') {
       x = (Object(x));
     }
     return x;
@@ -196,17 +185,6 @@
   }-*/;
 
   /**
-   * Returns <code>true</code> if the {@link JavaScriptObject} is a JavaScript
-   * String.
-   * 
-   * @param jsValue JavaScript object to test
-   * @return <code>true</code> if jsValue is a JavaScript String
-   */
-  private static native boolean isString(JavaScriptObject jsValue) /*-{
-    return jsValue instanceof String;
-  }-*/;
-
-  /**
    * Not instantiable.
    */
   private JSONParser() {
diff --git a/user/src/com/google/gwt/user/client/Element.java b/user/src/com/google/gwt/user/client/Element.java
index bfee634..b6c5be7 100644
--- a/user/src/com/google/gwt/user/client/Element.java
+++ b/user/src/com/google/gwt/user/client/Element.java
@@ -35,38 +35,4 @@
    */
   protected Element() {
   }
-
-  /*
-   * (non-Javadoc)
-   * 
-   * @see java.lang.Object#equals(java.lang.Object)
-   */
-  @Override
-  public boolean equals(Object other) {
-    if (other instanceof Element) {
-      return DOM.compare(this, (Element) other);
-    }
-
-    return super.equals(other);
-  }
-
-  /*
-   * (non-Javadoc)
-   * 
-   * @see java.lang.Object#hashCode()
-   */
-  @Override
-  public int hashCode() {
-    return super.hashCode();
-  }
-
-  /*
-   * (non-Javadoc)
-   * 
-   * @see java.lang.Object#toString()
-   */
-  @Override
-  public String toString() {
-    return DOM.toString(this);
-  }
 }
diff --git a/user/src/com/google/gwt/user/client/Event.java b/user/src/com/google/gwt/user/client/Event.java
index 08ad57b..09cf5af 100644
--- a/user/src/com/google/gwt/user/client/Event.java
+++ b/user/src/com/google/gwt/user/client/Event.java
@@ -167,34 +167,4 @@
    */
   protected Event() {
   }
-
-  /*
-   * (non-Javadoc)
-   *
-   * @see java.lang.Object#equals(java.lang.Object)
-   */
-  @Override
-  public boolean equals(Object other) {
-    return super.equals(other);
-  }
-
-  /*
-   * (non-Javadoc)
-   *
-   * @see java.lang.Object#hashCode()
-   */
-  @Override
-  public int hashCode() {
-    return super.hashCode();
-  }
-
-  /*
-   * (non-Javadoc)
-   *
-   * @see java.lang.Object#toString()
-   */
-  @Override
-  public String toString() {
-    return DOM.eventToString(this);
-  }
 }
diff --git a/user/super/com/google/gwt/emul/java/lang/Object.java b/user/super/com/google/gwt/emul/java/lang/Object.java
index 5f0a917..13bbca5 100644
--- a/user/super/com/google/gwt/emul/java/lang/Object.java
+++ b/user/super/com/google/gwt/emul/java/lang/Object.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -27,11 +27,18 @@
    * 
    * @skip
    */
-  protected transient int typeId;
+  public transient int typeId;
 
-  public native boolean equals(Object other) /*-{
-    return this === other;
-  }-*/;
+  /**
+   * magic magic magic.
+   * 
+   * @skip
+   */
+  public transient Object typeMarker;
+
+  public boolean equals(Object other) {
+    return this == other;
+  }
 
   /*
    * Magic; unlike the real JDT, we don't spec this method as final.  The
@@ -42,9 +49,9 @@
     return Object.class;
   }
 
-  public int hashCode() {
-    return System.identityHashCode(this);
-  }
+  public native int hashCode() /*-{
+    return @com.google.gwt.core.client.Impl::getHashCode(Ljava/lang/Object;)(this);
+  }-*/;
 
   public String toString() {
     return getClass().getName() + '@' + Integer.toHexString(hashCode());
diff --git a/user/super/com/google/gwt/emul/java/lang/System.java b/user/super/com/google/gwt/emul/java/lang/System.java
index cc6a2e8..1811bee 100644
--- a/user/super/com/google/gwt/emul/java/lang/System.java
+++ b/user/super/com/google/gwt/emul/java/lang/System.java
@@ -98,7 +98,7 @@
   }-*/;
 
   public static native int identityHashCode(Object o) /*-{
-    return @com.google.gwt.core.client.Impl::getHashCode(Ljava/lang/Object;)(o);
+    return (o == null) ? 0 : @com.google.gwt.core.client.Impl::getHashCode(Ljava/lang/Object;)(o);
   }-*/;
 
   public static native void setErr(PrintStream err) /*-{
diff --git a/user/test/com/google/gwt/dev/jjs/CompilerSuite.java b/user/test/com/google/gwt/dev/jjs/CompilerSuite.java
index acbeb46..b2a71ad 100644
--- a/user/test/com/google/gwt/dev/jjs/CompilerSuite.java
+++ b/user/test/com/google/gwt/dev/jjs/CompilerSuite.java
@@ -28,6 +28,7 @@
 import com.google.gwt.dev.jjs.test.InnerClassTest;
 import com.google.gwt.dev.jjs.test.InnerOuterSuperTest;
 import com.google.gwt.dev.jjs.test.JsniConstructorTest;
+import com.google.gwt.dev.jjs.test.JsoTest;
 import com.google.gwt.dev.jjs.test.MemberShadowingTest;
 import com.google.gwt.dev.jjs.test.MethodBindTest;
 import com.google.gwt.dev.jjs.test.MethodCallTest;
@@ -61,6 +62,7 @@
     suite.addTestSuite(InnerClassTest.class);
     suite.addTestSuite(InnerOuterSuperTest.class);
     suite.addTestSuite(JsniConstructorTest.class);
+    suite.addTestSuite(JsoTest.class);
     suite.addTestSuite(MemberShadowingTest.class);
     suite.addTestSuite(MethodBindTest.class);
     suite.addTestSuite(MethodCallTest.class);
diff --git a/user/test/com/google/gwt/dev/jjs/test/HostedTest.java b/user/test/com/google/gwt/dev/jjs/test/HostedTest.java
index bac67d4..9df5500 100644
--- a/user/test/com/google/gwt/dev/jjs/test/HostedTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/HostedTest.java
@@ -94,10 +94,6 @@
     return new Number(v);
   }-*/;
 
-  private native static JavaScriptObject getBoxedStringAsObject(String v) /*-{
-    return new String(v);
-  }-*/;
-
   private native static String getBoxedStringAsString(String v) /*-{
     return new String(v);
   }-*/;
@@ -210,8 +206,6 @@
     assertEquals(getJSOAsString(bvo), "true");
     JavaScriptObject nvo = getBoxedNumberAsObject(42);
     assertEquals(getJSOAsString(nvo), "42");
-    JavaScriptObject svo = getBoxedStringAsObject("test");
-    assertEquals(getJSOAsString(svo), "test");
     String sv = getBoxedStringAsString("test");
     assertEquals(sv, "test");
   }
diff --git a/user/test/com/google/gwt/dev/jjs/test/JsoTest.java b/user/test/com/google/gwt/dev/jjs/test/JsoTest.java
new file mode 100644
index 0000000..21bd74d
--- /dev/null
+++ b/user/test/com/google/gwt/dev/jjs/test/JsoTest.java
@@ -0,0 +1,606 @@
+/*
+ * Copyright 2008 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.jjs.test;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.junit.client.GWTTestCase;
+
+/**
+ * Tests {@link JavaScriptObject} and subclasses.
+ */
+public class JsoTest extends GWTTestCase {
+
+  private static class Bar extends JavaScriptObject {
+    public static int field;
+    
+    public static native String staticNative() /*-{
+      return "nativeBar";
+    }-*/;
+
+    public static String staticValue() {
+      return "Bar" + field;
+    }
+
+    protected Bar() {
+    }
+    
+    public final native String getBar() /*-{
+      return this.bar;
+    }-*/;
+
+    public final String value() {
+      return "Bar";
+    }
+  }
+
+  private static class Foo extends JavaScriptObject {
+    public static int field;
+
+    public static native String staticNative() /*-{
+      return "nativeFoo";
+    }-*/;
+
+    public static String staticValue() {
+      return "Foo" + field;
+    }
+
+    protected Foo() {
+    }
+    
+    public final native String getFoo() /*-{
+      return this.foo;
+    }-*/;
+
+    public final String value() {
+      return "Foo";
+    }
+  }
+
+  private static class FooSub extends Foo {
+    protected FooSub() {
+    }
+
+    public final String anotherValue() {
+      return "Still Foo";
+    }
+
+    public final String superCall() {
+      return super.value();
+    }
+  }
+
+  private static class JsArray<T> extends JavaScriptObject {
+    public static native <T> JsArray<T> create() /*-{
+      return [];
+    }-*/;
+
+    protected JsArray() {
+    }
+
+    public final native T get(int index) /*-{
+      return this[index];
+    }-*/;
+
+    public final native int length() /*-{
+      return this.length;
+    }-*/;
+
+    public final native void put(int index, T value) /*-{
+      this[index] = value;
+    }-*/;
+  }
+
+  private static class MethodMangleClash {
+    @SuppressWarnings("unused")
+    public static String func(JavaScriptObject this_) {
+      return "funcJavaScriptObject";
+    }
+
+    @SuppressWarnings("unused")
+    public static String func(MethodMangleClash this_) {
+      return "funcMethodMangleClash";
+    }
+
+    @SuppressWarnings("unused")
+    public String func() {
+      return "func";
+    }
+  }
+
+  private static class Overloads {
+    @SuppressWarnings("unused")
+    private static String sFunc(Bar b) {
+      return "sFunc Bar";
+    }
+
+    @SuppressWarnings("unused")
+    private static String sFunc(Bar[][] b) {
+      return "sFunc Bar[][]";
+    }
+
+    @SuppressWarnings("unused")
+    private static String sFunc(Foo f) {
+      return "sFunc Foo";
+    }
+
+    @SuppressWarnings("unused")
+    private static String sFunc(Foo[][] f) {
+      return "sFunc Foo[][]";
+    }
+
+    @SuppressWarnings("unused")
+    public Overloads(Bar b) {
+    }
+
+    @SuppressWarnings("unused")
+    public Overloads(Bar[][] b) {
+    }
+
+    @SuppressWarnings("unused")
+    public Overloads(Foo f) {
+    }
+
+    @SuppressWarnings("unused")
+    public Overloads(Foo[][] f) {
+    }
+
+    @SuppressWarnings("unused")
+    private String func(Bar b) {
+      return "func Bar";
+    }
+
+    @SuppressWarnings("unused")
+    private String func(Bar[][] b) {
+      return "func Bar[][]";
+    }
+
+    @SuppressWarnings("unused")
+    private String func(Foo f) {
+      return "func Foo";
+    }
+
+    @SuppressWarnings("unused")
+    private String func(Foo[][] f) {
+      return "func Foo[][]";
+    }
+  }
+
+  private static native Bar makeBar() /*-{
+    return {
+      toString:function() {
+        return "bar";
+      },
+      bar: "this is bar",
+    };
+  }-*/;
+
+  private static native Foo makeFoo() /*-{
+    return {
+      toString:function() {
+        return "foo";
+      },
+      foo: "this is foo",
+    };
+  }-*/;
+
+  private static native JavaScriptObject makeJSO() /*-{
+    return {
+      toString:function() {
+        return "jso";
+      },
+      foo: "jso foo",
+      bar: "jso bar",
+    };
+  }-*/;
+
+  private static native JavaScriptObject makeMixedArray() /*-{
+    return [
+      @com.google.gwt.dev.jjs.test.JsoTest::makeJSO()(),
+      "foo",
+      @com.google.gwt.dev.jjs.test.JsoTest::makeObject()(),
+      null
+    ];
+  }-*/;
+
+  private static Object makeObject() {
+    return new Object() {
+      @Override
+      public String toString() {
+        return "myObject";
+      }
+    };
+  }
+
+  private static native JavaScriptObject returnMe(JavaScriptObject jso) /*-{
+    return jso;
+  }-*/;
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.dev.jjs.CompilerSuite";
+  }
+
+  public void testArrayInit() {
+    Object[] array = {makeJSO(), new Object(), ""};
+    assertTrue(array[0] instanceof JavaScriptObject);
+    assertFalse(array[1] instanceof JavaScriptObject);
+    assertFalse(array[2] instanceof JavaScriptObject);
+  }
+
+  public void testArrayStore() {
+    JavaScriptObject[] jsoArray = new JavaScriptObject[1];
+    jsoArray[0] = makeJSO();
+    jsoArray[0] = makeFoo();
+    jsoArray[0] = makeBar();
+
+    Foo[] fooArray = new Foo[1];
+    fooArray[0] = (Foo) makeJSO();
+    fooArray[0] = makeFoo();
+    fooArray[0] = makeBar().cast();
+
+    Bar[] barArray = new Bar[1];
+    barArray[0] = (Bar) makeJSO();
+    barArray[0] = makeBar();
+    barArray[0] = makeFoo().cast();
+
+    Object[] objArray = jsoArray;
+    try {
+      objArray[0] = new Object();
+      fail("Expected ArrayStoreException");
+    } catch (ArrayStoreException expected) {
+    }
+  }
+
+  public void testBasic() {
+    JavaScriptObject jso = makeJSO();
+    assertEquals("jso", jso.toString());
+
+    Foo foo = (Foo) jso;
+    assertEquals("jso", foo.toString());
+    assertEquals("jso foo", foo.getFoo());
+    assertEquals("Foo", foo.value());
+
+    Bar bar = (Bar) jso;
+    assertEquals("jso", bar.toString());
+    assertEquals("jso bar", bar.getBar());
+    assertEquals("Bar", bar.value());
+
+    foo = makeFoo();
+    assertEquals("foo", foo.toString());
+    assertEquals("this is foo", foo.getFoo());
+    assertEquals("Foo", foo.value());
+
+    bar = makeBar();
+    assertEquals("bar", bar.toString());
+    assertEquals("this is bar", bar.getBar());
+    assertEquals("Bar", bar.value());
+  }
+
+  @SuppressWarnings("cast")
+  public void testCasts() {
+    JavaScriptObject jso = makeJSO();
+    assertTrue(jso instanceof JavaScriptObject);
+    assertTrue(jso instanceof Foo);
+    assertTrue(jso instanceof Bar);
+
+    Foo foo = (Foo) jso;
+    foo = makeFoo();
+    assertTrue((JavaScriptObject) foo instanceof Bar);
+    Bar bar = (Bar) (JavaScriptObject) makeFoo();
+    bar = makeFoo().cast();
+
+    bar = (Bar) jso;
+    bar = makeBar();
+    assertTrue((JavaScriptObject) bar instanceof Foo);
+    foo = (Foo) (JavaScriptObject) makeBar();
+    foo = makeBar().cast();
+
+    // Implicit
+    jso = foo;
+    jso = bar;
+
+    Object o = new Object();
+    assertFalse(o instanceof JavaScriptObject);
+    assertFalse(o instanceof Foo);
+    assertFalse(o instanceof Bar);
+    try {
+      jso = (JavaScriptObject) o;
+      fail("Expected ClassCastException");
+    } catch (ClassCastException expected) {
+    }
+
+    o = "foo";
+    assertFalse(o instanceof JavaScriptObject);
+    assertFalse(o instanceof Foo);
+    assertFalse(o instanceof Bar);
+    try {
+      jso = (JavaScriptObject) o;
+      fail("Expected ClassCastException");
+    } catch (ClassCastException expected) {
+    }
+
+    o = jso;
+    assertFalse(o instanceof String);
+    try {
+      String s = (String) o;
+      s.toString();
+      fail("Expected ClassCastException");
+    } catch (ClassCastException expected) {
+    }
+  }
+
+  @SuppressWarnings("cast")
+  public void testCastsArray() {
+    JavaScriptObject[][] jso = new JavaScriptObject[0][0];
+    assertTrue(jso instanceof JavaScriptObject[][]);
+    assertTrue(jso instanceof Foo[][]);
+    assertTrue(jso instanceof Bar[][]);
+
+    Foo[][] foo = (Foo[][]) jso;
+    foo = new Foo[0][0];
+    assertTrue((JavaScriptObject[][]) foo instanceof Bar[][]);
+    Bar[][] bar = (Bar[][]) (JavaScriptObject[][]) new Foo[0][0];
+
+    bar = (Bar[][]) jso;
+    bar = new Bar[0][0];
+    assertTrue((JavaScriptObject[][]) bar instanceof Foo[][]);
+    foo = (Foo[][]) (JavaScriptObject[][]) new Bar[0][0];
+
+    Object[][] o = new Object[0][0];
+    assertFalse(o instanceof JavaScriptObject[][]);
+    assertFalse(o instanceof Foo[][]);
+    assertFalse(o instanceof Bar[][]);
+    try {
+      jso = (JavaScriptObject[][]) o;
+      fail("Expected ClassCastException");
+    } catch (ClassCastException expected) {
+    }
+
+    o = jso;
+    assertFalse(o instanceof String[][]);
+    try {
+      String[][] s = (String[][]) o;
+      s.toString();
+      fail("Expected ClassCastException");
+    } catch (ClassCastException expected) {
+    }
+  }
+
+  public void testClassLiterals() {
+    JavaScriptObject jso = makeJSO();
+    Foo foo = makeFoo();
+    Bar bar = makeBar();
+    assertEquals(JavaScriptObject.class, jso.getClass());
+    assertEquals(Foo.class, jso.getClass());
+    assertEquals(Bar.class, jso.getClass());
+    assertEquals(JavaScriptObject.class, foo.getClass());
+    assertEquals(Foo.class, foo.getClass());
+    assertEquals(Bar.class, foo.getClass());
+    assertEquals(JavaScriptObject.class, bar.getClass());
+    assertEquals(Foo.class, bar.getClass());
+    assertEquals(Bar.class, bar.getClass());
+    assertEquals(JavaScriptObject.class, Foo.class);
+    assertEquals(JavaScriptObject.class, Bar.class);
+    assertEquals(Foo.class, Bar.class);
+
+    assertEquals("com.google.gwt.core.client.JavaScriptObject$",
+        JavaScriptObject.class.getName());
+  }
+
+  public void testClassLiteralsArray() {
+    JavaScriptObject[][] jso = new JavaScriptObject[0][0];
+    Foo[][] foo = new Foo[0][0];
+    Bar[][] bar = new Bar[0][0];
+    assertEquals(JavaScriptObject[][].class, jso.getClass());
+    assertEquals(Foo[][].class, jso.getClass());
+    assertEquals(Bar[][].class, jso.getClass());
+    assertEquals(JavaScriptObject[][].class, foo.getClass());
+    assertEquals(Foo[][].class, foo.getClass());
+    assertEquals(Bar[][].class, foo.getClass());
+    assertEquals(JavaScriptObject[][].class, bar.getClass());
+    assertEquals(Foo[][].class, bar.getClass());
+    assertEquals(Bar[][].class, bar.getClass());
+    assertEquals(JavaScriptObject[][].class, Foo[][].class);
+    assertEquals(JavaScriptObject[][].class, Bar[][].class);
+    assertEquals(Foo[][].class, Bar[][].class);
+
+    assertEquals("[[Lcom.google.gwt.core.client.JavaScriptObject$;",
+        JavaScriptObject[][].class.getName());
+  }
+  
+  public void testEquality() {
+    JavaScriptObject jso = makeJSO();
+    assertEquals(jso, jso);
+
+    JavaScriptObject jso2 = makeJSO();
+    assertFalse(jso.equals(jso2));
+    assertFalse(jso2.equals(jso));
+
+    jso2 = returnMe(jso);
+    assertEquals(jso, jso2);
+  }
+
+  public void testGenericsJsos() {
+    JsArray<JavaScriptObject> a = JsArray.create();
+    a.put(0, makeJSO());
+    a.put(1, makeFoo());
+    a.put(2, makeBar());
+    a.put(3, null);
+    assertEquals(4, a.length());
+    assertEquals("jso", a.get(0).toString());
+    assertEquals("foo", a.get(1).toString());
+    assertEquals("bar", a.get(2).toString());
+    assertEquals(null, a.get(3));
+  }
+
+  public void testGenericsMixed() {
+    JsArray<Object> a = JsArray.create();
+    a.put(0, makeJSO());
+    a.put(1, "foo");
+    a.put(2, makeObject());
+    a.put(3, null);
+    assertEquals(4, a.length());
+    assertEquals("jso", a.get(0).toString());
+    assertEquals("foo", a.get(1));
+    assertEquals("myObject", a.get(2).toString());
+    assertEquals(null, a.get(3));
+  }
+
+  @SuppressWarnings("unchecked")
+  public void testGenericsRawJson() {
+    JsArray a = (JsArray) makeMixedArray();
+    assertEquals(4, a.length());
+    assertEquals("jso", a.get(0).toString());
+    assertEquals("foo", a.get(1));
+    assertEquals("myObject", a.get(2).toString());
+    assertEquals(null, a.get(3));
+  }
+
+  public void testGenericsStrings() {
+    JsArray<String> a = JsArray.create();
+    a.put(0, "foo");
+    a.put(1, "bar");
+    a.put(2, "baz");
+    a.put(3, null);
+    assertEquals(4, a.length());
+    assertEquals("foo", a.get(0));
+    assertEquals("bar", a.get(1));
+    assertEquals("baz", a.get(2));
+    assertEquals(null, a.get(3));
+  }
+
+  public void testHashCode() {
+    // TODO: make this better.
+    JavaScriptObject jso = makeJSO();
+    int jsoHashCode = jso.hashCode();
+    Foo foo = makeFoo();
+    Bar bar = makeBar();
+    Object o = new Object() {
+      @Override
+      public int hashCode() {
+        // Return something unlikely so as not to collide with the JSOs.
+        return 0xDEADBEEF;
+      }
+    };
+
+    assertEquals(jsoHashCode, jso.hashCode());
+    assertFalse(jsoHashCode == foo.hashCode());
+    assertFalse(jsoHashCode == bar.hashCode());
+    assertFalse(jsoHashCode == o.hashCode());
+    assertFalse(foo.hashCode() == bar.hashCode());
+    assertFalse(foo.hashCode() == o.hashCode());
+    assertFalse(bar.hashCode() == o.hashCode());
+
+    o = jso;
+    assertEquals(jsoHashCode, o.hashCode());
+
+    String s = "foo";
+    int stringHashCode = s.hashCode();
+    o = s;
+    assertEquals(stringHashCode, o.hashCode());
+  }
+
+  public void testIdentity() {
+    JavaScriptObject jso = makeJSO();
+    assertSame(jso, jso);
+
+    JavaScriptObject jso2 = makeJSO();
+    assertNotSame(jso, jso2);
+
+    jso2 = returnMe(jso);
+    assertSame(jso, jso2);
+  }
+
+  public void testInheritance() {
+    Foo foo = makeFoo();
+    FooSub fooSub = (FooSub) foo;
+    assertEquals("Foo", fooSub.value());
+    assertEquals("Still Foo", fooSub.anotherValue());
+    assertEquals("Foo", fooSub.superCall());
+  }
+
+  public void testMethodMangleClash() {
+    assertEquals("funcJavaScriptObject",
+        MethodMangleClash.func((JavaScriptObject) null));
+    assertEquals("funcMethodMangleClash",
+        MethodMangleClash.func((MethodMangleClash) null));
+    assertEquals("func", new MethodMangleClash().func());
+  }
+
+  public void testOverloads() {
+    Foo foo = makeFoo();
+    assertEquals("func Foo", new Overloads(foo).func(foo));
+    assertEquals("sFunc Foo", Overloads.sFunc(foo));
+
+    Bar bar = makeBar();
+    assertEquals("func Bar", new Overloads(bar).func(bar));
+    assertEquals("sFunc Bar", Overloads.sFunc(bar));
+  }
+
+  public void testOverloadsArray() {
+    Foo[][] foo = new Foo[0][0];
+    assertEquals("func Foo[][]", new Overloads(foo).func(foo));
+    assertEquals("sFunc Foo[][]", Overloads.sFunc(foo));
+
+    Bar[][] bar = new Bar[0][0];
+    assertEquals("func Bar[][]", new Overloads(bar).func(bar));
+    assertEquals("sFunc Bar[][]", Overloads.sFunc(bar));
+  }
+
+  public native void testOverloadsArrayNative() /*-{
+    var o = @com.google.gwt.dev.jjs.test.JsoTest.Overloads::new([[Lcom/google/gwt/dev/jjs/test/JsoTest$Foo;)(null);
+    @junit.framework.Assert::assertEquals(Ljava/lang/Object;Ljava/lang/Object;)("func Foo[][]", o.@com.google.gwt.dev.jjs.test.JsoTest.Overloads::func([[Lcom/google/gwt/dev/jjs/test/JsoTest$Foo;)(null));
+    @junit.framework.Assert::assertEquals(Ljava/lang/Object;Ljava/lang/Object;)("sFunc Foo[][]", @com.google.gwt.dev.jjs.test.JsoTest.Overloads::sFunc([[Lcom/google/gwt/dev/jjs/test/JsoTest$Foo;)(null));
+
+    var o = @com.google.gwt.dev.jjs.test.JsoTest.Overloads::new([[Lcom/google/gwt/dev/jjs/test/JsoTest$Bar;)(null);
+    @junit.framework.Assert::assertEquals(Ljava/lang/Object;Ljava/lang/Object;)("func Bar[][]", o.@com.google.gwt.dev.jjs.test.JsoTest.Overloads::func([[Lcom/google/gwt/dev/jjs/test/JsoTest$Bar;)(null));
+    @junit.framework.Assert::assertEquals(Ljava/lang/Object;Ljava/lang/Object;)("sFunc Bar[][]", @com.google.gwt.dev.jjs.test.JsoTest.Overloads::sFunc([[Lcom/google/gwt/dev/jjs/test/JsoTest$Bar;)(null));
+  }-*/;
+
+  public native void testOverloadsNative() /*-{
+    var o = @com.google.gwt.dev.jjs.test.JsoTest.Overloads::new(Lcom/google/gwt/dev/jjs/test/JsoTest$Foo;)(null);
+    @junit.framework.Assert::assertEquals(Ljava/lang/Object;Ljava/lang/Object;)("func Foo", o.@com.google.gwt.dev.jjs.test.JsoTest.Overloads::func(Lcom/google/gwt/dev/jjs/test/JsoTest$Foo;)(null));
+    @junit.framework.Assert::assertEquals(Ljava/lang/Object;Ljava/lang/Object;)("sFunc Foo", @com.google.gwt.dev.jjs.test.JsoTest.Overloads::sFunc(Lcom/google/gwt/dev/jjs/test/JsoTest$Foo;)(null));
+
+    var o = @com.google.gwt.dev.jjs.test.JsoTest.Overloads::new(Lcom/google/gwt/dev/jjs/test/JsoTest$Bar;)(null);
+    @junit.framework.Assert::assertEquals(Ljava/lang/Object;Ljava/lang/Object;)("func Bar", o.@com.google.gwt.dev.jjs.test.JsoTest.Overloads::func(Lcom/google/gwt/dev/jjs/test/JsoTest$Bar;)(null));
+    @junit.framework.Assert::assertEquals(Ljava/lang/Object;Ljava/lang/Object;)("sFunc Bar", @com.google.gwt.dev.jjs.test.JsoTest.Overloads::sFunc(Lcom/google/gwt/dev/jjs/test/JsoTest$Bar;)(null));
+  }-*/;
+
+  public void testStaticAccess() {
+    Foo.field = 3;
+    assertEquals(3, Foo.field--);
+    assertEquals("Foo2", Foo.staticValue());
+    assertEquals("nativeFoo", Foo.staticNative());
+
+    Bar.field = 10;
+    assertEquals(11, ++Bar.field);
+    assertEquals("Bar11", Bar.staticValue());
+    assertEquals("nativeBar", Bar.staticNative());
+  }
+
+  public native void testStaticAccessNative() /*-{
+    @com.google.gwt.dev.jjs.test.JsoTest.Foo::field = 3;
+    @junit.framework.Assert::assertEquals(II)(3, @com.google.gwt.dev.jjs.test.JsoTest.Foo::field--);
+    @junit.framework.Assert::assertEquals(Ljava/lang/Object;Ljava/lang/Object;)("Foo2", @com.google.gwt.dev.jjs.test.JsoTest.Foo::staticValue()());
+    @junit.framework.Assert::assertEquals(Ljava/lang/Object;Ljava/lang/Object;)("nativeFoo", @com.google.gwt.dev.jjs.test.JsoTest.Foo::staticNative()());
+
+    @com.google.gwt.dev.jjs.test.JsoTest.Bar::field = 10;
+    @junit.framework.Assert::assertEquals(II)(11, ++@com.google.gwt.dev.jjs.test.JsoTest.Bar::field);
+    @junit.framework.Assert::assertEquals(Ljava/lang/Object;Ljava/lang/Object;)("Bar11", @com.google.gwt.dev.jjs.test.JsoTest.Bar::staticValue()());
+    @junit.framework.Assert::assertEquals(Ljava/lang/Object;Ljava/lang/Object;)("nativeBar", @com.google.gwt.dev.jjs.test.JsoTest.Bar::staticNative()());
+  }-*/;
+}
diff --git a/user/test/com/google/gwt/dev/jjs/test/MiscellaneousTest.java b/user/test/com/google/gwt/dev/jjs/test/MiscellaneousTest.java
index d11c7fe..4949d47 100644
--- a/user/test/com/google/gwt/dev/jjs/test/MiscellaneousTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/MiscellaneousTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2007 Google Inc.
+ * Copyright 2008 Google Inc.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -16,7 +16,6 @@
 package com.google.gwt.dev.jjs.test;
 
 import com.google.gwt.core.client.JavaScriptException;
-import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.junit.client.GWTTestCase;
 
 /**
@@ -45,12 +44,6 @@
     }
   }
 
-  private static class Foo extends JavaScriptObject {
-    public String toString() {
-      return "Foo";
-    }
-  }
-
   private static class HasClinit {
     public static int i = 1;
 
@@ -93,14 +86,6 @@
     @com.google.gwt.dev.jjs.test.MiscellaneousTest$HasClinit::i = 5;
   }-*/;
 
-  private static native Foo getFoo() /*-{
-    return {};
-  }-*/;
-
-  private static native JavaScriptObject getJso() /*-{
-    return {toString:function(){return 'jso';}}; 
-  }-*/;
-
   private static native void throwNativeException() /*-{
     var a; a.asdf();
   }-*/;
@@ -214,6 +199,7 @@
     b[2] = null;
   }
 
+  @SuppressWarnings("cast")
   public void testCasts() {
     Object o = noOptimizeFalse() ? (Object) new PolyA() : (Object) new PolyB();
     assertTrue(o instanceof I);
@@ -281,50 +267,6 @@
     assertEquals(5, i);
   }
 
-  public void testJso() {
-    Foo foo = getFoo();
-    assertEquals("Foo", foo.toString());
-    JavaScriptObject jso = foo;
-    assertEquals("Foo", jso.toString());
-    Object y = noOptimizeFalse() ? new Object() : foo;
-    assertEquals("Foo", y.toString());
-    jso = getJso();
-    assertEquals("jso", jso.toString());
-  }
-
-  public void testJsoArrayInit() {
-    Object[] jsos = {getJso(), ""};
-    JavaScriptObject jso = (JavaScriptObject) jsos[0];
-  }
-
-  public void testJsoArrayStore() {
-    // Verify that a JSO stored into an array was correctly wrapped
-    String[] strings = {""};
-    JavaScriptObject[] jsos = {getJso()};
-    Object[] objArray = noOptimizeFalse() ? (Object[]) strings : jsos;
-    JavaScriptObject jso = (JavaScriptObject) objArray[0];
-
-    // Verify that ArrayStoreExceptions are generated in the correct cases
-    try {
-      JavaScriptObject[] typeTightenedFooArray = new Foo[3];
-      typeTightenedFooArray[0] = getJso();
-      fail();
-    } catch (ArrayStoreException e) {
-    }
-
-    try {
-      JavaScriptObject[] fooArray = noOptimizeFalse() ? new JavaScriptObject[3]
-          : new Foo[3];
-      fooArray[0] = getJso();
-      fail();
-    } catch (ArrayStoreException e) {
-    }
-
-    JavaScriptObject[] jsoArray = noOptimizeFalse() ? new Foo[3]
-        : new JavaScriptObject[3];
-    jsoArray[0] = getJso();
-  }
-
   public void testString() {
     String x = "hi";
     assertEquals("hi", x);