JConstructor redesign.

Implementation notes:

- new type JConstructor extends JMethod

- JNewInstance now extends JMethodCall

- The wonky "Ctor(new TypeName(), arg1, arg2)" construction is gone in favor of "new Ctor(arg1, arg2)"

- A lot of calls to JMethod.isStatic() have been replaced by JMethod.needsVtable().  This is because what the code really wanted to ask was "can this method participate in instance dispatch?"  And it used to be true that this was exactly "!isStatic()".  But constructors muddy the water.  It seemed wrong to model constructors as being static, especially since they can contain this references, so I went with the needsVtable() approach.

Follow-on work:

- I can reduce some of the code size with a subsequent patch that merges empty constructors and seed functions, but I ran afoul of DeRPC's expectations so I'll do this in a follow-up.

- I also have another follow-on that can inline super/this constructors into calling constructors at the JS layer; and it also improves a few other places where "function.call()" is used. I doubt this will be a size win, but probably a speed win.

- One last tiny size improvement would be to avoid setting up a prototype on constructors that are used only for super calls.

http://gwt-code-reviews.appspot.com/159803
Review by: spoon


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7714 8db76d5a-ed1c-0410-87a9-c151d255dfc7
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 13e2845..1fcdba2 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -82,6 +82,7 @@
 import com.google.gwt.dev.jjs.impl.PostOptimizationCompoundAssignmentNormalizer;
 import com.google.gwt.dev.jjs.impl.Pruner;
 import com.google.gwt.dev.jjs.impl.RecordRebinds;
+import com.google.gwt.dev.jjs.impl.RemoveEmptySuperCalls;
 import com.google.gwt.dev.jjs.impl.ReplaceRebinds;
 import com.google.gwt.dev.jjs.impl.ReplaceRunAsyncs;
 import com.google.gwt.dev.jjs.impl.ResolveRebinds;
@@ -246,6 +247,8 @@
         optimize(options, jprogram);
       }
 
+      RemoveEmptySuperCalls.exec(jprogram);
+
       // (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.
@@ -737,8 +740,8 @@
   }
 
   private static JMethodCall createReboundModuleLoad(TreeLogger logger,
-      JDeclaredType reboundEntryType, String originalMainClassName)
-      throws UnableToCompleteException {
+      JDeclaredType reboundEntryType, String originalMainClassName,
+      JDeclaredType enclosingType) throws UnableToCompleteException {
     if (!(reboundEntryType instanceof JClassType)) {
       logger.log(TreeLogger.ERROR, "Module entry point class '"
           + originalMainClassName + "' must be a class", null);
@@ -772,7 +775,7 @@
     JExpression qualifier = null;
     if (!entryMethod.isStatic()) {
       qualifier = JGwtCreate.createInstantiationExpression(sourceInfo,
-          entryClass);
+          entryClass, enclosingType);
 
       if (qualifier == null) {
         logger.log(
@@ -838,7 +841,7 @@
         }
 
         JMethodCall onModuleLoadCall = createReboundModuleLoad(logger,
-            resultType, mainClassName);
+            resultType, mainClassName, bootStrapMethod.getEnclosingType());
         resultTypes.add((JClassType) resultType);
         entryCalls.add(onModuleLoadCall);
       }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JConstructor.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JConstructor.java
new file mode 100644
index 0000000..1ad7379
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JConstructor.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2010 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.ast;
+
+import com.google.gwt.dev.jjs.SourceInfo;
+
+import java.util.List;
+
+/**
+ * A Java constructor method.
+ */
+public class JConstructor extends JMethod {
+
+  /**
+   * Caches whether or not this constructor does any work. Once true, we never
+   * have to recheck, but we keep rechecking as long as it's false.
+   */
+  private boolean isEmpty = false;
+
+  private JNonNullType newType;
+
+  /**
+   * These are only supposed to be constructed by JProgram.
+   */
+  JConstructor(SourceInfo info, JClassType enclosingType, JNonNullType newType) {
+    super(info, enclosingType.getShortName(), enclosingType,
+        JPrimitiveType.VOID, false, false, true, false);
+    this.newType = newType;
+  }
+
+  @Override
+  public boolean canBePolymorphic() {
+    return false;
+  }
+
+  @Override
+  public JMethodBody getBody() {
+    return (JMethodBody) super.getBody();
+  }
+
+  @Override
+  public JClassType getEnclosingType() {
+    return (JClassType) super.getEnclosingType();
+  }
+
+  public JNonNullType getNewType() {
+    return newType;
+  }
+
+  /**
+   * Returns <code>true</code> if this constructor does no real work.
+   * 
+   * NOTE: this method does NOT account for any clinits that would be triggered
+   * if this constructor is the target of a new instance operation from an
+   * external class.
+   * 
+   * TODO(scottb): make this method less expensive by computing in an external
+   * visitor.
+   */
+  public boolean isEmpty() {
+    if (isEmpty) {
+      return true;
+    }
+    JMethodBody body = getBody();
+    List<JStatement> statements = body.getStatements();
+    if (statements.isEmpty()) {
+      return (isEmpty = true);
+    }
+    if (statements.size() > 1) {
+      return false;
+    }
+    // Only one statement. Check to see if it's an empty super() or this() call.
+    JStatement stmt = statements.get(0);
+    if (stmt instanceof JExpressionStatement) {
+      JExpressionStatement exprStmt = (JExpressionStatement) stmt;
+      JExpression expr = exprStmt.getExpr();
+      if (expr instanceof JMethodCall && !(expr instanceof JNewInstance)) {
+        JMethodCall call = (JMethodCall) expr;
+        JMethod target = call.getTarget();
+        if (target instanceof JConstructor) {
+          return isEmpty = ((JConstructor) target).isEmpty();
+        }
+      }
+    }
+    return false;
+  }
+
+  @Override
+  public boolean needsVtable() {
+    return false;
+  }
+
+  @Override
+  public void traverse(JVisitor visitor, Context ctx) {
+    String before = traceBefore(visitor);
+    if (visitor.visit(this, ctx)) {
+      visitChildren(visitor);
+    }
+    visitor.endVisit(this, ctx);
+    traceAfter(visitor, before);
+  }
+
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JGwtCreate.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JGwtCreate.java
index 1c867e2..3accec7 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JGwtCreate.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JGwtCreate.java
@@ -28,16 +28,16 @@
 public class JGwtCreate extends JExpression {
 
   public static JExpression createInstantiationExpression(SourceInfo info,
-      JClassType classType) {
+      JClassType classType, JDeclaredType enclosingType) {
     /*
      * Find the appropriate (noArg) constructor. In our AST, constructors are
      * instance methods that should be qualified with a new expression.
      */
-    JMethod noArgCtor = null;
+    JConstructor noArgCtor = null;
     for (JMethod ctor : classType.getMethods()) {
-      if (ctor.getName().equals(classType.getShortName())) {
+      if (ctor instanceof JConstructor) {
         if (ctor.getParams().size() == 0) {
-          noArgCtor = ctor;
+          noArgCtor = (JConstructor) ctor;
           break;
         }
       }
@@ -46,16 +46,15 @@
       return null;
     }
     // Call it, using a new expression as a qualifier
-    JNewInstance newInstance = new JNewInstance(info,
-        (JNonNullType) noArgCtor.getType());
-    return new JMethodCall(info, newInstance, noArgCtor);
+    return new JNewInstance(info, noArgCtor, enclosingType);
   }
 
   private static ArrayList<JExpression> createInstantiationExpressions(
-      SourceInfo info, List<JClassType> classTypes) {
+      SourceInfo info, List<JClassType> classTypes, JDeclaredType enclosingType) {
     ArrayList<JExpression> exprs = new ArrayList<JExpression>();
     for (JClassType classType : classTypes) {
-      JExpression expr = createInstantiationExpression(info, classType);
+      JExpression expr = createInstantiationExpression(info, classType,
+          enclosingType);
       assert expr != null;
       exprs.add(expr);
     }
@@ -75,9 +74,9 @@
    * Public constructor used during AST creation.
    */
   public JGwtCreate(SourceInfo info, JReferenceType sourceType,
-      List<JClassType> resultTypes, JType type) {
+      List<JClassType> resultTypes, JType type, JDeclaredType enclosingType) {
     this(info, sourceType, resultTypes, type, createInstantiationExpressions(
-        info, resultTypes));
+        info, resultTypes, enclosingType));
   }
 
   /**
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
index a28357c..3feb55d 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethod.java
@@ -30,9 +30,8 @@
 /**
  * A Java method implementation.
  */
-public final class JMethod extends JNode implements HasAnnotations,
-    HasEnclosingType, HasName, HasType, CanBeAbstract, CanBeSetFinal,
-    CanBeNative, CanBeStatic {
+public class JMethod extends JNode implements HasAnnotations, HasEnclosingType,
+    HasName, HasType, CanBeAbstract, CanBeSetFinal, CanBeNative, CanBeStatic {
 
   private static final String TRACE_METHOD_WILDCARD = "*";
 
@@ -96,6 +95,7 @@
    * Add a method that this method overrides.
    */
   public void addOverride(JMethod toAdd) {
+    assert canBePolymorphic();
     overrides = Lists.add(overrides, toAdd);
   }
 
@@ -103,6 +103,7 @@
    * Add methods that this method overrides.
    */
   public void addOverrides(List<JMethod> toAdd) {
+    assert canBePolymorphic();
     overrides = Lists.addAll(overrides, toAdd);
   }
 
@@ -121,6 +122,15 @@
     thrownExceptions = Lists.addAll(thrownExceptions, exceptionTypes);
   }
 
+  /**
+   * Returns true if this method can participate in virtual dispatch. Returns
+   * true for non-private instance methods; false for static methods, private
+   * instance methods, and constructors.
+   */
+  public boolean canBePolymorphic() {
+    return !isStatic() && !isPrivate();
+  }
+
   public JAnnotation findAnnotation(String className) {
     return JAnnotation.findAnnotation(this, className);
   }
@@ -215,6 +225,14 @@
   }
 
   /**
+   * Returns <code>true</code> if this method can participate in instance
+   * dispatch.
+   */
+  public boolean needsVtable() {
+    return !isStatic();
+  }
+
+  /**
    * Removes the parameter at the specified index.
    */
   public void removeParam(int index) {
@@ -272,21 +290,15 @@
 
   public void traverse(JVisitor visitor, Context ctx) {
     String before = null;
-    if (trace && visitor instanceof JModVisitor) {
-      before = this.toSource();
-      if (traceFirst) {
-        traceFirst = false;
-        trace("JAVA INITIAL", before);
-      }
-    }
+    before = traceBefore(visitor);
     if (visitor.visit(this, ctx)) {
-      annotations = visitor.acceptImmutable(annotations);
-      params = visitor.acceptImmutable(params);
-      if (body != null) {
-        body = (JAbstractMethodBody) visitor.accept(body);
-      }
+      visitChildren(visitor);
     }
     visitor.endVisit(this, ctx);
+    traceAfter(visitor, before);
+  }
+
+  protected void traceAfter(JVisitor visitor, String before) {
     if (trace && visitor instanceof JModVisitor) {
       String after = this.toSource();
       if (!after.equals(before)) {
@@ -296,6 +308,26 @@
     }
   }
 
+  protected String traceBefore(JVisitor visitor) {
+    if (trace && visitor instanceof JModVisitor) {
+      String source = this.toSource();
+      if (traceFirst) {
+        traceFirst = false;
+        trace("JAVA INITIAL", source);
+      }
+      return source;
+    }
+    return null;
+  }
+
+  protected void visitChildren(JVisitor visitor) {
+    annotations = visitor.acceptImmutable(annotations);
+    params = visitor.acceptImmutable(params);
+    if (body != null) {
+      body = (JAbstractMethodBody) visitor.accept(body);
+    }
+  }
+
   /**
    * See {@link #writeBody(ObjectOutputStream)}.
    * 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethodCall.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethodCall.java
index 42559f7..25ebf7d 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethodCall.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethodCall.java
@@ -35,7 +35,7 @@
 
   /**
    * Initialize a new method call equivalent to another one. A new instance must
-   * be specified, and the new object has not arguments on initialization. This
+   * be specified, and the new object has no arguments on initialization. This
    * forces the caller to potentially deal with cloning objects if needed.
    */
   public JMethodCall(JMethodCall other, JExpression instance) {
@@ -50,7 +50,7 @@
   public JMethodCall(SourceInfo info, JExpression instance, JMethod method) {
     super(info);
     assert (method != null);
-    assert (instance != null || method.isStatic());
+    assert (instance != null || method.isStatic() || this instanceof JNewInstance);
     this.instance = instance;
     this.method = method;
     this.overrideReturnType = null;
@@ -108,7 +108,7 @@
 
   public boolean canBePolymorphic() {
     return !cannotBePolymorphic && !staticDispatchOnly && !method.isFinal()
-        && !method.isStatic();
+        && method.canBePolymorphic();
   }
 
   /**
@@ -168,12 +168,16 @@
 
   public void traverse(JVisitor visitor, Context ctx) {
     if (visitor.visit(this, ctx)) {
-      if (instance != null) {
-        instance = visitor.accept(instance);
-      }
-      args = visitor.acceptImmutable(args);
+      visitChildren(visitor);
     }
     visitor.endVisit(this, ctx);
   }
 
+  protected void visitChildren(JVisitor visitor) {
+    if (instance != null) {
+      instance = visitor.accept(instance);
+    }
+    args = visitor.acceptImmutable(args);
+  }
+
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JNewInstance.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JNewInstance.java
index eeea366..aeebb67 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JNewInstance.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JNewInstance.java
@@ -18,36 +18,71 @@
 import com.google.gwt.dev.jjs.SourceInfo;
 
 /**
- * A new instance expression. This differs from a standard Java new operation in
- * that no constructor is implied. Rather, a new operation creates an
- * uninitialized Object which is passed as an argument to a constructor method.
+ * A new instance expression.
  */
-public class JNewInstance extends JExpression {
+public class JNewInstance extends JMethodCall {
 
-  private final JNonNullType type;
+  /**
+   * The enclosing type of this new operation, used to compute clinit.
+   */
+  private JDeclaredType enclosingType;
 
-  public JNewInstance(SourceInfo info, JNonNullType type) {
-    super(info);
-    assert type.getUnderlyingType() instanceof JClassType;
-    this.type = type;
+  /**
+   * Initialize a new instance operation equivalent to another one. The new
+   * object has no arguments on initialization. This forces the caller to
+   * potentially deal with cloning objects if needed.
+   */
+  public JNewInstance(JNewInstance other) {
+    super(other, null);
+    this.enclosingType = other.enclosingType;
+  }
+
+  public JNewInstance(SourceInfo info, JConstructor ctor,
+      JDeclaredType enclosingType) {
+    super(info, null, ctor);
+    this.enclosingType = enclosingType;
+    setStaticDispatchOnly();
   }
 
   public JClassType getClassType() {
-    return (JClassType) type.getUnderlyingType();
+    return getTarget().getEnclosingType();
   }
 
+  public JDeclaredType getEnclosingType() {
+    return enclosingType;
+  }
+
+  @Override
+  public JConstructor getTarget() {
+    return (JConstructor) super.getTarget();
+  }
+
+  @Override
   public JNonNullType getType() {
-    return type;
+    return getTarget().getNewType();
+  }
+
+  public boolean hasClinit() {
+    return getEnclosingType().checkClinitTo(getTarget().getEnclosingType());
   }
 
   @Override
   public boolean hasSideEffects() {
-    // The actual new operation itself has no side effects (see class comment).
-    return false;
+    if (hasClinit()) {
+      return true;
+    }
+    for (JExpression arg : getArgs()) {
+      if (arg.hasSideEffects()) {
+        return true;
+      }
+    }
+    return !getTarget().isEmpty();
   }
 
+  @Override
   public void traverse(JVisitor visitor, Context ctx) {
     if (visitor.visit(this, ctx)) {
+      visitChildren(visitor);
     }
     visitor.endVisit(this, ctx);
   }
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 c45cbe6..59b74c5 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
@@ -439,6 +439,20 @@
     return x;
   }
 
+  public JConstructor createConstructor(SourceInfo info,
+      JClassType enclosingType) {
+    JConstructor x = new JConstructor(info, enclosingType,
+        getNonNullType(enclosingType));
+    x.setBody(new JMethodBody(info));
+    if (indexedTypes.containsValue(enclosingType)) {
+      indexedMethods.put(enclosingType.getShortName() + '.'
+          + enclosingType.getShortName(), x);
+    }
+
+    enclosingType.addMethod(x);
+    return x;
+  }
+
   public JEnumType createEnum(SourceInfo info, char[][] name) {
     String sname = dotify(name);
     JEnumType x = new JEnumType(info, sname);
@@ -987,7 +1001,7 @@
     if (nullMethod == null) {
       nullMethod = new JMethod(createSourceInfoSynthetic(JProgram.class,
           "Null method"), "nullMethod", null, JNullType.INSTANCE, false, false,
-          true, true);
+          true, false);
       nullMethod.setSynthetic();
     }
     return nullMethod;
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 8255315..835b20d 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
@@ -126,9 +126,18 @@
       return false;
     }
 
+    @Override
+    public boolean visit(JNewInstance x, Context ctx) {
+      if (x.hasSideEffects()) {
+        hasLiveCode = true;
+      }
+      return false;
+    }
+
     private boolean mightBeDeadCode(JExpression expr) {
       // Must have a visit method for every subtype that answers yes!
-      return expr instanceof JMultiExpression || expr instanceof JMethodCall;
+      return expr instanceof JMultiExpression || expr instanceof JMethodCall
+          || expr instanceof JNewInstance;
     }
 
     private boolean mightBeDeadCode(JStatement stmt) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java
index 99f7304..8ad3d3d 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JVisitor.java
@@ -198,6 +198,10 @@
     endVisit((JExpression) x, ctx);
   }
 
+  public void endVisit(JConstructor x, Context ctx) {
+    endVisit((JMethod) x, ctx);
+  }
+
   public void endVisit(JContinueStatement x, Context ctx) {
     endVisit((JStatement) x, ctx);
   }
@@ -317,7 +321,7 @@
   }
 
   public void endVisit(JNewInstance x, Context ctx) {
-    endVisit((JExpression) x, ctx);
+    endVisit((JMethodCall) x, ctx);
   }
 
   public void endVisit(JNode x, Context ctx) {
@@ -510,6 +514,10 @@
     return visit((JExpression) x, ctx);
   }
 
+  public boolean visit(JConstructor x, Context ctx) {
+    return visit((JMethod) x, ctx);
+  }
+
   public boolean visit(JContinueStatement x, Context ctx) {
     return visit((JNode) x, ctx);
   }
@@ -629,7 +637,7 @@
   }
 
   public boolean visit(JNewInstance x, Context ctx) {
-    return visit((JExpression) x, ctx);
+    return visit((JMethodCall) x, ctx);
   }
 
   public boolean visit(JNode x, Context ctx) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java b/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java
index 4c7bf5e..232bc13 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java
@@ -20,6 +20,7 @@
 import com.google.gwt.dev.jjs.InternalCompilerException;
 import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.jjs.ast.JClassType;
+import com.google.gwt.dev.jjs.ast.JConstructor;
 import com.google.gwt.dev.jjs.ast.JDeclaredType;
 import com.google.gwt.dev.jjs.ast.JEnumType;
 import com.google.gwt.dev.jjs.ast.JField;
@@ -27,9 +28,7 @@
 import com.google.gwt.dev.jjs.ast.JLocal;
 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.JNewInstance;
-import com.google.gwt.dev.jjs.ast.JNonNullType;
 import com.google.gwt.dev.jjs.ast.JParameter;
 import com.google.gwt.dev.jjs.ast.JParameterRef;
 import com.google.gwt.dev.jjs.ast.JPrimitiveType;
@@ -166,26 +165,23 @@
       try {
         MethodBinding b = ctorDecl.binding;
         JClassType enclosingType = (JClassType) typeMap.get(scope.enclosingSourceType());
-        String name = enclosingType.getShortName();
         SourceInfo info = makeSourceInfo(ctorDecl, enclosingType);
-        JMethod newMethod = program.createMethod(info, name.toCharArray(),
-            enclosingType, program.getNonNullType(enclosingType), false, false,
-            true, b.isPrivate(), false);
+        JConstructor newCtor = program.createConstructor(info, enclosingType);
 
         // Enums have hidden arguments for name and value
         if (enclosingType.isEnumOrSubclass() != null) {
           program.createParameter(info, "enum$name".toCharArray(),
-              program.getTypeJavaLangString(), true, false, newMethod);
+              program.getTypeJavaLangString(), true, false, newCtor);
           program.createParameter(info, "enum$ordinal".toCharArray(),
-              program.getTypePrimitiveInt(), true, false, newMethod);
+              program.getTypePrimitiveInt(), true, false, newCtor);
         }
 
         // user args
-        mapParameters(newMethod, ctorDecl);
-        addThrownExceptions(ctorDecl.binding, newMethod);
+        mapParameters(newCtor, ctorDecl);
+        addThrownExceptions(ctorDecl.binding, newCtor);
         // original params are now frozen
 
-        info.addCorrelation(program.getCorrelator().by(newMethod));
+        info.addCorrelation(program.getCorrelator().by(newCtor));
 
         int syntheticParamCount = 0;
         ReferenceBinding declaringClass = b.declaringClass;
@@ -200,7 +196,7 @@
               if (alreadyNamedVariables.contains(argName)) {
                 argName += "_" + i;
               }
-              createParameter(arg, argName, newMethod);
+              createParameter(arg, argName, newCtor);
               ++syntheticParamCount;
               alreadyNamedVariables.add(argName);
             }
@@ -213,14 +209,14 @@
               if (alreadyNamedVariables.contains(argName)) {
                 argName += "_" + i;
               }
-              createParameter(arg, argName, newMethod);
+              createParameter(arg, argName, newCtor);
               ++syntheticParamCount;
               alreadyNamedVariables.add(argName);
             }
           }
         }
 
-        typeMap.put(b, newMethod);
+        typeMap.put(b, newCtor);
 
         // Now let's implicitly create a static function called 'new' that will
         // allow construction from JSNI methods
@@ -228,7 +224,7 @@
           ReferenceBinding enclosingBinding = ctorDecl.binding.declaringClass.enclosingType();
           JReferenceType outerType = enclosingBinding == null ? null
               : (JReferenceType) typeMap.get(enclosingBinding);
-          createSyntheticConstructor(newMethod,
+          createSyntheticConstructor(newCtor,
               ctorDecl.binding.declaringClass.isStatic(), outerType);
         }
 
@@ -403,9 +399,9 @@
      *          constructed. This may be <code>null</code> if the class is a
      *          top-level type.
      */
-    private JMethod createSyntheticConstructor(JMethod constructor,
+    private JMethod createSyntheticConstructor(JConstructor constructor,
         boolean staticClass, JReferenceType enclosingType) {
-      JClassType type = (JClassType) constructor.getEnclosingType();
+      JClassType type = constructor.getEnclosingType();
 
       // Define the method
       JMethod synthetic = program.createMethod(type.getSourceInfo().makeChild(
@@ -419,12 +415,8 @@
       // new Foo() : Create the instance
       JNewInstance newInstance = new JNewInstance(
           type.getSourceInfo().makeChild(BuildDeclMapVisitor.class,
-              "new instance"), (JNonNullType) synthetic.getType());
+              "new instance"), constructor, type);
 
-      // (new Foo()).Foo() : Invoke the constructor method on the instance
-      JMethodCall call = new JMethodCall(type.getSourceInfo().makeChild(
-          BuildDeclMapVisitor.class, "constructor invocation"), newInstance,
-          constructor);
       /*
        * If the type isn't static, make the first parameter a reference to the
        * instance of the enclosing class. It's the first instance to allow the
@@ -449,16 +441,16 @@
          * implicitly as the last argument to the constructor.
          */
         if (enclosingInstance != null && !i.hasNext()) {
-          call.addArg(new JParameterRef(synthetic.getSourceInfo().makeChild(
-              BuildDeclMapVisitor.class, "enclosing instance"),
-              enclosingInstance));
+          newInstance.addArg(new JParameterRef(
+              synthetic.getSourceInfo().makeChild(BuildDeclMapVisitor.class,
+                  "enclosing instance"), enclosingInstance));
         } else {
           JParameter syntheticParam = program.createParameter(
               synthetic.getSourceInfo().makeChild(BuildDeclMapVisitor.class,
                   "Argument " + param.getName()),
               param.getName().toCharArray(), param.getType(), true, false,
               synthetic);
-          call.addArg(new JParameterRef(
+          newInstance.addArg(new JParameterRef(
               syntheticParam.getSourceInfo().makeChild(
                   BuildDeclMapVisitor.class, "reference"), syntheticParam));
         }
@@ -467,10 +459,10 @@
       // Lock the method.
       synthetic.freezeParamTypes();
 
-      // return (new Foo()).Foo() : The only statement in the function
+      // return new Foo() : The only statement in the function
       JReturnStatement ret = new JReturnStatement(
           synthetic.getSourceInfo().makeChild(BuildDeclMapVisitor.class,
-              "Return statement"), call);
+              "Return statement"), newInstance);
 
       // Add the return statement to the method body
       JMethodBody body = (JMethodBody) synthetic.getBody();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java
index e1da519..8f132e4 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CloneExpressionVisitor.java
@@ -242,7 +242,9 @@
 
   @Override
   public boolean visit(JNewInstance x, Context ctx) {
-    expression = new JNewInstance(x.getSourceInfo(), x.getType());
+    JNewInstance newInstance = new JNewInstance(x);
+    newInstance.addArgs(cloneExpressions(x.getArgs()));
+    expression = newInstance;
     return false;
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
index a40f2c7..66c76f2 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CodeSplitter.java
@@ -595,7 +595,7 @@
 
     cfa.traverseFromInstantiationOf(typeArray);
     for (JMethod method : typeArray.getMethods()) {
-      if (!method.isStatic()) {
+      if (method.needsVtable()) {
         cfa.traverseFrom(method);
       }
     }
@@ -948,7 +948,7 @@
          * must be in the same one.
          */
         for (JMethod method : type.getMethods()) {
-          if (!method.isStatic() && methodsInJavaScript.contains(method)) {
+          if (method.needsVtable() && methodsInJavaScript.contains(method)) {
             int methodFrag = getOrZero(fragmentMap.methods, method);
             if (methodFrag != typeFrag) {
               fragmentMap.types.put(type, 0);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
index 35d1616..408e2e0 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
@@ -365,10 +365,10 @@
     }
 
     @Override
-    public boolean visit(JNewInstance newInstance, Context ctx) {
+    public boolean visit(JNewInstance x, Context ctx) {
       // rescue and instantiate the target class!
-      rescue(newInstance.getClassType(), true, true);
-      return true;
+      rescue(x.getClassType(), true, true);
+      return super.visit(x, ctx);
     }
 
     @Override
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java b/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
index 45b757c..b378371 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
@@ -43,6 +43,7 @@
 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.JNewInstance;
 import com.google.gwt.dev.jjs.ast.JNode;
 import com.google.gwt.dev.jjs.ast.JParameterRef;
 import com.google.gwt.dev.jjs.ast.JPostfixOperation;
@@ -454,6 +455,23 @@
     }
 
     @Override
+    public void endVisit(JNewInstance x, Context ctx) {
+      super.endVisit(x, ctx);
+      if (!ignoringExpressionOutput.contains(x) || !x.getTarget().isEmpty()) {
+        return;
+      }
+      // Replace the new operation with a multi.
+      JMultiExpression multi = new JMultiExpression(x.getSourceInfo());
+      multi.exprs.addAll(x.getArgs());
+      JMethodCall clinit = maybeCreateClinitCall(x);
+      if (clinit != null) {
+        multi.exprs.add(clinit);
+      }
+
+      ctx.replaceMe(accept(multi));
+    }
+
+    @Override
     public void endVisit(JParameterRef x, Context ctx) {
       JLiteral literal = tryGetConstant(x);
       if (literal != null) {
@@ -1194,6 +1212,15 @@
       return null;
     }
 
+    private JMethodCall maybeCreateClinitCall(JNewInstance x) {
+      if (x.hasClinit()) {
+        JMethod clinit = x.getTarget().getEnclosingType().getMethods().get(0);
+        assert (JProgram.isClinit(clinit));
+        return new JMethodCall(x.getSourceInfo(), null, clinit);
+      }
+      return null;
+    }
+
     private int numRemovableExpressions(JMultiExpression x) {
       if (ignoringExpressionOutput.contains(x)) {
         // The result doesn't matter: all expressions can be removed.
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ExpressionAnalyzer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ExpressionAnalyzer.java
index 18005f0..3d3dac6 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ExpressionAnalyzer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ExpressionAnalyzer.java
@@ -193,11 +193,8 @@
 
   @Override
   public void endVisit(JNewInstance x, Context ctx) {
-    /*
-     * Due to implementation details, a new instance operation has no other
-     * possible side-effects.
-     */
     createsObject = true;
+    endVisit((JMethodCall) x, ctx);
   }
 
   @Override
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/Finalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/Finalizer.java
index 85533ae..68b57e8 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/Finalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/Finalizer.java
@@ -19,6 +19,7 @@
 import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JBinaryOperation;
 import com.google.gwt.dev.jjs.ast.JClassType;
+import com.google.gwt.dev.jjs.ast.JConstructor;
 import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
 import com.google.gwt.dev.jjs.ast.JExpression;
 import com.google.gwt.dev.jjs.ast.JField;
@@ -72,6 +73,11 @@
     }
 
     @Override
+    public void endVisit(JConstructor x, Context ctx) {
+      // Not applicable.
+    }
+
+    @Override
     public void endVisit(JField x, Context ctx) {
       if (!x.isVolatile()) {
         maybeFinalize(x);
@@ -135,6 +141,11 @@
     }
 
     @Override
+    public void endVisit(JConstructor x, Context ctx) {
+      // Never overridden.
+    }
+    
+    @Override
     public void endVisit(JDeclarationStatement x, Context ctx) {
       // This is not a reassignment, the target may still be final.
     }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
index fbe8748..9942bab 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/FragmentExtractor.java
@@ -17,12 +17,15 @@
 
 import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.jjs.ast.JClassType;
+import com.google.gwt.dev.jjs.ast.JConstructor;
 import com.google.gwt.dev.jjs.ast.JDeclaredType;
 import com.google.gwt.dev.jjs.ast.JField;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.js.JsHoister.Cloner;
 import com.google.gwt.dev.js.ast.JsBinaryOperation;
 import com.google.gwt.dev.js.ast.JsBinaryOperator;
+import com.google.gwt.dev.js.ast.JsContext;
 import com.google.gwt.dev.js.ast.JsEmpty;
 import com.google.gwt.dev.js.ast.JsExprStmt;
 import com.google.gwt.dev.js.ast.JsExpression;
@@ -30,6 +33,8 @@
 import com.google.gwt.dev.js.ast.JsInvocation;
 import com.google.gwt.dev.js.ast.JsName;
 import com.google.gwt.dev.js.ast.JsNameRef;
+import com.google.gwt.dev.js.ast.JsNew;
+import com.google.gwt.dev.js.ast.JsObjectLiteral;
 import com.google.gwt.dev.js.ast.JsProgram;
 import com.google.gwt.dev.js.ast.JsStatement;
 import com.google.gwt.dev.js.ast.JsVars;
@@ -269,8 +274,18 @@
         }
 
         boolean keepIt;
-
-        if (containsRemovableVars(stat)) {
+        JClassType vtableTypeAssigned = vtableTypeAssigned(stat);
+        if (vtableTypeAssigned != null
+            && livenessPredicate.isLive(vtableTypeAssigned)) {
+          JsExprStmt result = extractPrototypeSetup(livenessPredicate,
+              alreadyLoadedPredicate, stat, vtableTypeAssigned);
+          if (result != null) {
+            stat = result;
+            keepIt = true;
+          } else {
+            keepIt = false;
+          }
+        } else if (containsRemovableVars(stat)) {
           stat = removeSomeVars((JsVars) stat, livenessPredicate,
               alreadyLoadedPredicate);
           keepIt = !(stat instanceof JsEmpty);
@@ -282,8 +297,8 @@
         statementLogger.logStatement(stat, keepIt);
 
         if (keepIt) {
-          if (vtableTypeAssigned(stat) != null) {
-            currentVtableType = vtableTypeAssigned(stat);
+          if (vtableTypeAssigned != null) {
+            currentVtableType = vtableTypeAssigned;
           }
           JClassType vtableType = vtableTypeNeeded(stat);
           if (vtableType != null && vtableType != currentVtableType) {
@@ -358,6 +373,60 @@
   }
 
   /**
+   * Weird case: the seed function's liveness is associated with the type
+   * itself. However, individual constructors can have a liveness that is a
+   * subset of the type's liveness. We essentially have to break up the
+   * prototype chain according to exactly what's newly live.
+   */
+  private JsExprStmt extractPrototypeSetup(
+      final LivenessPredicate livenessPredicate,
+      final LivenessPredicate alreadyLoadedPredicate, JsStatement stat,
+      final JClassType vtableTypeAssigned) {
+    final boolean[] anyLiveCode = new boolean[1];
+    Cloner c = new Cloner() {
+      @Override
+      public void endVisit(JsBinaryOperation x, JsContext<JsExpression> ctx) {
+        JsExpression rhs = stack.pop();
+        JsNameRef lhs = (JsNameRef) stack.pop();
+        if (rhs instanceof JsNew || rhs instanceof JsObjectLiteral) {
+          // The super op is being assigned to the seed prototype.
+          if (alreadyLoadedPredicate.isLive(vtableTypeAssigned)) {
+            stack.push(lhs);
+            return;
+          } else {
+            anyLiveCode[0] = true;
+          }
+        } else if (lhs.getQualifier() == null) {
+          // The underscore is being assigned to.
+          assert "_".equals(lhs.getIdent());
+        } else {
+          // A constructor function is being assigned to.
+          assert "prototype".equals(lhs.getIdent());
+          JsNameRef ctorRef = (JsNameRef) lhs.getQualifier();
+          JConstructor ctor = (JConstructor) map.nameToMethod(ctorRef.getName());
+          assert ctor != null;
+          if (livenessPredicate.isLive(ctor)
+              && !alreadyLoadedPredicate.isLive(ctor)) {
+            anyLiveCode[0] = true;
+          } else {
+            stack.push(rhs);
+            return;
+          }
+        }
+
+        JsBinaryOperation toReturn = new JsBinaryOperation(x.getSourceInfo(),
+            x.getOperator());
+        toReturn.setArg2(rhs);
+        toReturn.setArg1(lhs);
+        stack.push(toReturn);
+      }
+    };
+    c.accept(((JsExprStmt) stat).getExpression());
+    JsExprStmt result = anyLiveCode[0] ? c.getExpression().makeStmt() : null;
+    return result;
+  }
+
+  /**
    * Check whether the statement invokes an entry method. Detect JavaScript code
    * of the form foo() where foo is a the JavaScript function corresponding to
    * an entry method.
@@ -398,7 +467,7 @@
       }
       // The method is live. Check that its enclosing type is instantiable.
       // TODO(spoon): this check should not be needed once the CFA is updated
-      return meth.isStatic()
+      return !meth.needsVtable()
           || livenessPredicate.isLive(meth.getEnclosingType());
     }
 
@@ -554,7 +623,7 @@
   private JClassType vtableTypeNeeded(JsStatement stat) {
     JMethod meth = map.vtableInitToMethod(stat);
     if (meth != null) {
-      if (!meth.isStatic()) {
+      if (meth.needsVtable()) {
         return (JClassType) meth.getEnclosingType();
       }
     }
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 901950c..77de903 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
@@ -38,6 +38,7 @@
 import com.google.gwt.dev.jjs.ast.JCharLiteral;
 import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JConditional;
+import com.google.gwt.dev.jjs.ast.JConstructor;
 import com.google.gwt.dev.jjs.ast.JContinueStatement;
 import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
 import com.google.gwt.dev.jjs.ast.JDeclaredType;
@@ -620,39 +621,27 @@
     }
 
     /**
-     * Weird: we used to have JConstructor (and JConstructorCall) in our AST,
-     * but we got rid of them completely and instead model them as instance
-     * methods whose qualifier is a naked no-argument new operation.
-     * 
-     * There are several reasons we do it this way:
-     * 
-     * 1) When spitting our AST back to Java code (for verification purposes),
-     * we found it was impossible to correctly emulate nested classes as
-     * non-nested classes using traditional constructor syntax. It boiled down
-     * to the fact that you really HAVE to assign your synthetic arguments to
-     * your synthetic fields BEFORE calling your superclass constructor (because
-     * it might call you back polymorphically). And trying to do that in
-     * straight Java is a semantic error, a super call must be the first
-     * statement of your constructor.
-     * 
-     * 2) It's a lot more like how we'll be generating JavaScript eventually.
-     * 
-     * 3) It's a lot easier to optimize; the same optimizations work on our
-     * synthetic fields as work on any user fields. In fact, once we're past AST
-     * generation, we throw away all information about what's synthetic.
+     * This is slightly different from the normal Java language. Specifically,
+     * we explicitly handle synthetic fields that are part of nested/local
+     * classes. It boils down to the fact that we really HAVE to assign
+     * synthetic arguments to synthetic fields BEFORE calling the superclass
+     * constructor (because it might call you back polymorphically). In straight
+     * Java that glue code is a semantic error, because a this/super call must
+     * be the first statement of your constructor. On the upside, optimizations
+     * work the same on our synthetic fields as with any user fields.
      * 
      * The order of emulation is: - assign all synthetic fields from synthetic
      * args - call our super constructor emulation method - call our instance
-     * initializer emulation method - run user code - return this
+     * initializer emulation method - run user code
      */
     void processConstructor(ConstructorDeclaration x) {
-      JMethod ctor = (JMethod) typeMap.get(x.binding);
+      JConstructor ctor = (JConstructor) typeMap.get(x.binding);
       try {
         processHasAnnotations(ctor, x.annotations);
         SourceInfo info = ctor.getSourceInfo();
 
         currentMethod = ctor;
-        currentMethodBody = (JMethodBody) ctor.getBody();
+        currentMethodBody = ctor.getBody();
         currentMethodScope = x.scope;
 
         JMethodCall superOrThisCall = null;
@@ -672,14 +661,8 @@
         boolean hasExplicitThis = (ctorCall != null)
             && !ctorCall.isSuperAccess();
 
-        JClassType enclosingType = (JClassType) ctor.getEnclosingType();
-
-        // Call clinit; $clinit is always in position 0.
-        JMethod clinitMethod = enclosingType.getMethods().get(0);
-        JMethodCall clinitCall = new JMethodCall(info, null, clinitMethod);
-        JMethodBody body = (JMethodBody) ctor.getBody();
-        JBlock block = body.getBlock();
-        block.addStmt(clinitCall.makeStatement());
+        JClassType enclosingType = ctor.getEnclosingType();
+        JBlock block = currentMethodBody.getBlock();
 
         /*
          * All synthetic fields must be assigned, unless we have an explicit
@@ -730,6 +713,7 @@
 
         // optional this or super constructor call
         if (superOrThisCall != null) {
+          superOrThisCall.setStaticDispatchOnly();
           block.addStmt(superOrThisCall.makeStatement());
         }
 
@@ -751,9 +735,6 @@
 
         currentMethodScope = null;
         currentMethod = null;
-
-        // synthesize a return statement to emulate returning the new object
-        block.addStmt(new JReturnStatement(info, thisRef));
       } catch (Throwable e) {
         throw translateException(ctor, e);
       }
@@ -771,7 +752,7 @@
       }
       JClassType newType = (JClassType) typeMap.get(typeBinding);
       MethodBinding b = x.binding;
-      JMethod ctor = (JMethod) typeMap.get(b);
+      JConstructor ctor = (JConstructor) typeMap.get(b);
       JMethodCall call;
       JClassType javaLangString = program.getTypeJavaLangString();
       if (newType == javaLangString) {
@@ -803,9 +784,7 @@
         }
         call = new JMethodCall(makeSourceInfo(x), null, targetMethod);
       } else {
-        JNewInstance newInstance = new JNewInstance(info,
-            (JNonNullType) ctor.getType());
-        call = new JMethodCall(info, newInstance, ctor);
+        call = new JNewInstance(info, ctor, currentClass);
       }
 
       // Enums: hidden arguments for the name and id.
@@ -1226,10 +1205,8 @@
 
       SourceInfo info = makeSourceInfo(x);
       MethodBinding b = x.binding;
-      JMethod ctor = (JMethod) typeMap.get(b);
-      JNewInstance newInstance = new JNewInstance(info,
-          (JNonNullType) ctor.getType());
-      JMethodCall call = new JMethodCall(info, newInstance, ctor);
+      JConstructor ctor = (JConstructor) typeMap.get(b);
+      JNewInstance newInstance = new JNewInstance(info, ctor, currentClass);
       JExpression qualifier = dispProcessExpression(x.enclosingInstance);
       List<JExpression> qualList = new ArrayList<JExpression>();
       qualList.add(qualifier);
@@ -1246,7 +1223,7 @@
       }
 
       // Plain old regular arguments
-      addCallArgs(x.arguments, call, b);
+      addCallArgs(x.arguments, newInstance, b);
 
       // Synthetic args for inner classes
       ReferenceBinding targetBinding = b.declaringClass;
@@ -1257,7 +1234,7 @@
           for (int i = 0; i < nestedBinding.enclosingInstances.length; ++i) {
             SyntheticArgumentBinding arg = nestedBinding.enclosingInstances[i];
             JClassType syntheticThisType = (JClassType) typeMap.get(arg.type);
-            call.addArg(createThisRef(syntheticThisType, qualList));
+            newInstance.addArg(createThisRef(syntheticThisType, qualList));
           }
         }
         // Synthetic locals for local classes
@@ -1265,13 +1242,13 @@
           for (int i = 0; i < nestedBinding.outerLocalVariables.length; ++i) {
             SyntheticArgumentBinding arg = nestedBinding.outerLocalVariables[i];
             JVariable variable = (JVariable) typeMap.get(arg.actualOuterLocalVariable);
-            call.addArg(createVariableRef(info, variable,
+            newInstance.addArg(createVariableRef(info, variable,
                 arg.actualOuterLocalVariable));
           }
         }
       }
 
-      return call;
+      return newInstance;
     }
 
     JExpression processExpression(QualifiedNameReference x) {
@@ -1759,18 +1736,7 @@
 
     JStatement processStatement(ReturnStatement x) {
       SourceInfo info = makeSourceInfo(x);
-      if (currentMethodScope.referenceContext instanceof ConstructorDeclaration) {
-        /*
-         * Special: constructors are implemented as instance methods that return
-         * their this object, so any embedded return statements have to be fixed
-         * up.
-         */
-        JClassType enclosingType = (JClassType) currentMethod.getEnclosingType();
-        assert (x.expression == null);
-        return new JReturnStatement(info, createThisRef(info, enclosingType));
-      } else {
-        return new JReturnStatement(info, dispProcessExpression(x.expression));
-      }
+      return new JReturnStatement(info, dispProcessExpression(x.expression));
     }
 
     JStatement processStatement(SwitchStatement x) {
@@ -3005,6 +2971,11 @@
       currentClass = x;
       return true;
     }
+
+    @Override
+    public boolean visit(JMethodBody x, Context ctx) {
+      return false;
+    }
   }
 
   /**
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 f088dd0..7722d4a 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
@@ -38,6 +38,7 @@
 import com.google.gwt.dev.jjs.ast.JClassLiteral;
 import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JConditional;
+import com.google.gwt.dev.jjs.ast.JConstructor;
 import com.google.gwt.dev.jjs.ast.JContinueStatement;
 import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
 import com.google.gwt.dev.jjs.ast.JDeclaredType;
@@ -315,7 +316,7 @@
     public boolean visit(JMethod x, Context ctx) {
       // my polymorphic name
       String name = x.getName();
-      if (!x.isStatic()) {
+      if (x.needsVtable()) {
         if (polymorphicNames.get(x) == null) {
           String mangleName = mangleNameForPoly(x);
           JsName polyName;
@@ -1052,11 +1053,8 @@
           /*
            * Dispatch statically (odd case). This happens when a call that must
            * be static is targeting an instance method that could not be
-           * transformed into a static. For example, making a super call into a
-           * native method currently causes this, because we cannot currently
-           * staticify native methods.
-           * 
-           * Have to use a "call" construct.
+           * transformed into a static. Super/this constructor calls work this
+           * way. Have to use a "call" construct.
            */
           JsName callName = objectScope.declareName("call");
           callName.setObfuscatable(false);
@@ -1102,8 +1100,9 @@
 
     @Override
     public void endVisit(JNewInstance x, Context ctx) {
-      JsNameRef nameRef = names.get(x.getClassType()).makeRef(x.getSourceInfo());
+      JsNameRef nameRef = names.get(x.getTarget()).makeRef(x.getSourceInfo());
       JsNew newOp = new JsNew(x.getSourceInfo(), nameRef);
+      popList(newOp.getArguments(), x.getArgs().size()); // args
       push(newOp);
     }
 
@@ -1593,20 +1592,31 @@
         globalStmts.add(seedFuncStmt);
         typeForStatMap.put(seedFuncStmt, x);
 
-        // setup prototype, assign to temp
-        // _ = com_example_foo_Foo.prototype = new com_example_foo_FooSuper();
-        JsNameRef lhs = prototype.makeRef(sourceInfo);
-        lhs.setQualifier(seedFuncName.makeRef(sourceInfo));
-        JsExpression rhs;
+        // Setup prototype chain.
+        // _ = Foo__V.prototype = FooSeed.prototype = new FooSuper();
+        JsNameRef seedProtoRef = prototype.makeRef(sourceInfo);
+        seedProtoRef.setQualifier(seedFuncName.makeRef(sourceInfo));
+        JsExpression protoObj;
         if (x.getSuperClass() != null) {
           JsNameRef superPrototypeRef = names.get(x.getSuperClass()).makeRef(
               sourceInfo);
           JsNew newExpr = new JsNew(sourceInfo, superPrototypeRef);
-          rhs = newExpr;
+          protoObj = newExpr;
         } else {
-          rhs = new JsObjectLiteral(sourceInfo);
+          protoObj = new JsObjectLiteral(sourceInfo);
         }
-        JsExpression protoAsg = createAssignment(lhs, rhs);
+        JsExpression protoAsg = createAssignment(seedProtoRef, protoObj);
+
+        // Chain assign the same prototype to every live constructor.
+        for (JMethod method : x.getMethods()) {
+          if (method instanceof JConstructor) {
+            JsNameRef protoRef = prototype.makeRef(sourceInfo);
+            protoRef.setQualifier(names.get(method).makeRef(sourceInfo));
+            protoAsg = createAssignment(protoRef, protoAsg);
+          }
+        }
+
+        // Finally, assign to the temp var for setup code.
         JsExpression tmpAsg = createAssignment(globalTemp.makeRef(sourceInfo),
             protoAsg);
         JsExprStmt tmpAsgStmt = tmpAsg.makeStmt();
@@ -1717,7 +1727,7 @@
       for (JMethod method : x.getMethods()) {
         SourceInfo sourceInfo = method.getSourceInfo().makeChild(
             GenerateJavaScriptVisitor.class, "vtable assignment");
-        if (!method.isStatic() && !method.isAbstract()) {
+        if (method.needsVtable() && !method.isAbstract()) {
           JsNameRef lhs = polymorphicNames.get(method).makeRef(sourceInfo);
           lhs.setQualifier(globalTemp.makeRef(sourceInfo));
           /*
@@ -1769,7 +1779,7 @@
       if (!crossClassTargets.contains(x)) {
         return null;
       }
-      if (!x.isStatic() || program.isStaticImpl(x)) {
+      if (x.canBePolymorphic() || program.isStaticImpl(x)) {
         return null;
       }
       JDeclaredType enclosingType = x.getEnclosingType();
@@ -1866,6 +1876,12 @@
     }
 
     @Override
+    public void endVisit(JProgram x, Context ctx) {
+      // Entry methods can be called externally, so they must run clinit. 
+      crossClassTargets.addAll(x.getAllEntryMethods());
+    }
+
+    @Override
     public void endVisit(JsniMethodRef x, Context ctx) {
       endVisit((JMethodCall) x, ctx);
     }
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
index 2216f98..4649e55 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JsoDevirtualizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JsoDevirtualizer.java
@@ -180,7 +180,7 @@
     }
 
     for (JMethod method : jsoType.getMethods()) {
-      if (!method.isStatic()) {
+      if (method.needsVtable()) {
         virtualJsoMethods.add(method);
       }
     }
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 158ab5f..e9d4e99 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
@@ -15,10 +15,12 @@
  */
 package com.google.gwt.dev.jjs.impl;
 
+import com.google.gwt.dev.jjs.InternalCompilerException;
 import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JAbstractMethodBody;
 import com.google.gwt.dev.jjs.ast.JClassType;
+import com.google.gwt.dev.jjs.ast.JConstructor;
 import com.google.gwt.dev.jjs.ast.JExpression;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodBody;
@@ -229,6 +231,12 @@
       enclosingType.getMethods().add(myIndexInClass + 1, newMethod);
       return false;
     }
+
+    @Override
+    public boolean visit(JConstructor x, Context ctx) {
+      throw new InternalCompilerException(
+          "Should not try to staticify constructors");
+    }
   }
 
   /**
@@ -251,7 +259,7 @@
       if (x.canBePolymorphic()) {
         return;
       }
-      if (method.isStatic()) {
+      if (!method.needsVtable()) {
         return;
       }
       if (method.isAbstract()) {
@@ -281,7 +289,7 @@
     private boolean currentMethodIsInitiallyLive;
     private ControlFlowAnalyzer initiallyLive;
 
-    /*
+    /**
      * In cases where callers are directly referencing (effectively) final
      * instance methods, rewrite the call site to reference the newly-generated
      * static method instead.
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodCallTightener.java b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodCallTightener.java
index d5ec679..ae43d85 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodCallTightener.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodCallTightener.java
@@ -23,6 +23,7 @@
 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.JNewInstance;
 import com.google.gwt.dev.jjs.ast.JNullType;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
@@ -44,6 +45,11 @@
   public class MethodCallTighteningVisitor extends JModVisitor {
 
     @Override
+    public void endVisit(JNewInstance x, Context ctx) {
+      // Do not tighten new operations.
+    }
+
+    @Override
     public void endVisit(JMethodCall x, Context ctx) {
       JMethod method = x.getTarget();
       JExpression instance = x.getInstance();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java
index 535ca89..53074a2 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java
@@ -26,6 +26,7 @@
 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.JNewInstance;
 import com.google.gwt.dev.jjs.ast.JParameter;
 import com.google.gwt.dev.jjs.ast.JParameterRef;
 import com.google.gwt.dev.jjs.ast.JProgram;
@@ -93,6 +94,11 @@
     }
 
     @Override
+    public void endVisit(JNewInstance x, Context ctx) {
+      // Do not inline new operations.
+    }
+    
+    @Override
     public void endVisit(JMethodCall x, Context ctx) {
       JMethod method = x.getTarget();
 
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 74ab3c2..4c6850e 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
@@ -24,6 +24,7 @@
 import com.google.gwt.dev.jjs.ast.JBinaryOperation;
 import com.google.gwt.dev.jjs.ast.JBinaryOperator;
 import com.google.gwt.dev.jjs.ast.JClassType;
+import com.google.gwt.dev.jjs.ast.JConstructor;
 import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
 import com.google.gwt.dev.jjs.ast.JDeclaredType;
 import com.google.gwt.dev.jjs.ast.JExpression;
@@ -35,6 +36,7 @@
 import com.google.gwt.dev.jjs.ast.JMethodCall;
 import com.google.gwt.dev.jjs.ast.JModVisitor;
 import com.google.gwt.dev.jjs.ast.JNameOf;
+import com.google.gwt.dev.jjs.ast.JNewInstance;
 import com.google.gwt.dev.jjs.ast.JNode;
 import com.google.gwt.dev.jjs.ast.JParameter;
 import com.google.gwt.dev.jjs.ast.JPrimitiveType;
@@ -167,43 +169,17 @@
 
       // Did we prune the parameters of the method we're calling?
       if (methodToOriginalParamsMap.containsKey(method)) {
-        // This must be a static method
-        assert method.isStatic();
-
         JMethodCall newCall = new JMethodCall(x, x.getInstance());
-        List<JParameter> originalParams = methodToOriginalParamsMap.get(method);
-        JMultiExpression currentMulti = null;
-        for (int i = 0, c = x.getArgs().size(); i < c; ++i) {
-          JExpression arg = x.getArgs().get(i);
-          JParameter param = null;
-          if (i < originalParams.size()) {
-            param = originalParams.get(i);
-          }
+        replaceForPrunedParameters(x, newCall, ctx);
+      }
+    }
 
-          if (param != null && referencedNonTypes.contains(param)) {
-            // If there is an existing multi, terminate it.
-            if (currentMulti != null) {
-              currentMulti.exprs.add(arg);
-              newCall.addArg(currentMulti);
-              currentMulti = null;
-            } else {
-              newCall.addArg(arg);
-            }
-          } else if (arg.hasSideEffects()) {
-            // The argument is only needed for side effects, add it to a multi.
-            if (currentMulti == null) {
-              currentMulti = new JMultiExpression(x.getSourceInfo());
-            }
-            currentMulti.exprs.add(arg);
-          }
-        }
-
-        // Add any orphaned parameters on the end. Extra params are OK.
-        if (currentMulti != null) {
-          newCall.addArg(currentMulti);
-        }
-
-        ctx.replaceMe(newCall);
+    @Override
+    public void endVisit(JNewInstance x, Context ctx) {
+      // Did we prune the parameters of the method we're calling?
+      if (methodToOriginalParamsMap.containsKey(x.getTarget())) {
+        JMethodCall newCall = new JNewInstance(x);
+        replaceForPrunedParameters(x, newCall, ctx);
       }
     }
 
@@ -305,6 +281,44 @@
         return multi;
       }
     }
+
+    private void replaceForPrunedParameters(JMethodCall x, JMethodCall newCall,
+        Context ctx) {
+      assert !x.getTarget().canBePolymorphic();
+      List<JParameter> originalParams = methodToOriginalParamsMap.get(x.getTarget());
+      JMultiExpression currentMulti = null;
+      for (int i = 0, c = x.getArgs().size(); i < c; ++i) {
+        JExpression arg = x.getArgs().get(i);
+        JParameter param = null;
+        if (i < originalParams.size()) {
+          param = originalParams.get(i);
+        }
+
+        if (param != null && referencedNonTypes.contains(param)) {
+          // If there is an existing multi, terminate it.
+          if (currentMulti != null) {
+            currentMulti.exprs.add(arg);
+            newCall.addArg(currentMulti);
+            currentMulti = null;
+          } else {
+            newCall.addArg(arg);
+          }
+        } else if (arg.hasSideEffects()) {
+          // The argument is only needed for side effects, add it to a multi.
+          if (currentMulti == null) {
+            currentMulti = new JMultiExpression(x.getSourceInfo());
+          }
+          currentMulti.exprs.add(arg);
+        }
+      }
+
+      // Add any orphaned parameters on the end. Extra params are OK.
+      if (currentMulti != null) {
+        newCall.addArg(currentMulti);
+      }
+
+      ctx.replaceMe(newCall);
+    }
   }
 
   /**
@@ -395,7 +409,7 @@
 
     @Override
     public boolean visit(JMethod x, Context ctx) {
-      if (x.isStatic()) {
+      if (!x.canBePolymorphic()) {
         /*
          * Don't prune parameters on unreferenced methods. The methods might not
          * be reachable through the current method traversal routines, but might
@@ -657,6 +671,9 @@
     for (JClassType type : program.codeGenTypes) {
       livenessAnalyzer.traverseFromReferenceTo(type);
       for (JMethod method : type.getMethods()) {
+        if (method instanceof JConstructor) {
+          livenessAnalyzer.traverseFromInstantiationOf(type);
+        }
         livenessAnalyzer.traverseFrom(method);
       }
     }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/RemoveEmptySuperCalls.java b/dev/core/src/com/google/gwt/dev/jjs/impl/RemoveEmptySuperCalls.java
new file mode 100644
index 0000000..1009b1b
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/RemoveEmptySuperCalls.java
@@ -0,0 +1,66 @@
+/*
+ * 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.JConstructor;
+import com.google.gwt.dev.jjs.ast.JExpressionStatement;
+import com.google.gwt.dev.jjs.ast.JMethodCall;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
+import com.google.gwt.dev.jjs.ast.JNewInstance;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.js.JMultiExpression;
+
+/**
+ * Removes calls to no-op super constructors.
+ */
+public class RemoveEmptySuperCalls {
+
+  /**
+   * Removes calls to no-op super constructors.
+   */
+  public static class EmptySuperCallVisitor extends JModVisitor {
+    @Override
+    public void endVisit(JExpressionStatement x, Context ctx) {
+      if (x.getExpr() instanceof JMethodCall
+          && !(x.getExpr() instanceof JNewInstance)) {
+        JMethodCall call = (JMethodCall) x.getExpr();
+        if (call.getTarget() instanceof JConstructor) {
+          JConstructor ctor = (JConstructor) call.getTarget();
+          if (ctor.isEmpty()) {
+            // TODO: move this 3-way into Simplifier.
+            if (call.getArgs().isEmpty()) {
+              ctx.removeMe();
+            } else if (call.getArgs().size() == 1) {
+              ctx.replaceMe(call.getArgs().get(0).makeStatement());
+            } else {
+              JMultiExpression multi = new JMultiExpression(
+                  call.getSourceInfo());
+              multi.exprs.addAll(call.getArgs());
+              ctx.replaceMe(multi.makeStatement());
+            }
+          }
+        }
+      }
+    }
+  }
+
+  public static boolean exec(JProgram program) {
+    EmptySuperCallVisitor v = new EmptySuperCallVisitor();
+    v.accept(program);
+    return v.didChange();
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRebinds.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRebinds.java
index ef8eb03..54ed313 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRebinds.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceRebinds.java
@@ -46,6 +46,7 @@
 
   private class RebindVisitor extends JModVisitor {
 
+    private JDeclaredType currentClass;
     private final JMethod nameOfMethod;
     private final JMethod rebindCreateMethod;
 
@@ -65,6 +66,12 @@
       }
     }
 
+    @Override
+    public boolean visit(JMethod x, Context ctx) {
+      currentClass = x.getEnclosingType();
+      return true;
+    }
+
     private void replaceGwtCreate(JMethodCall x, Context ctx) {
       assert (x.getArgs().size() == 1);
       JExpression arg = x.getArgs().get(0);
@@ -73,7 +80,7 @@
       JReferenceType sourceType = (JReferenceType) classLiteral.getRefType();
       List<JClassType> allRebindResults = getAllPossibleRebindResults(sourceType);
       JGwtCreate gwtCreate = new JGwtCreate(x.getSourceInfo(), sourceType,
-          allRebindResults, program.getTypeJavaLangObject());
+          allRebindResults, program.getTypeJavaLangObject(), currentClass);
       if (allRebindResults.size() == 1) {
         // Just replace with the instantiation expression.
         ctx.replaceMe(gwtCreate.getInstantiationExpressions().get(0));
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/SameParameterValueOptimizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/SameParameterValueOptimizer.java
index bdbf664..4dd95c5 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/SameParameterValueOptimizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/SameParameterValueOptimizer.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JBinaryOperation;
+import com.google.gwt.dev.jjs.ast.JConstructor;
 import com.google.gwt.dev.jjs.ast.JExpression;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodCall;
@@ -112,6 +113,12 @@
     }
 
     @Override
+    public boolean visit(JConstructor x, Context ctx) {
+      // Cannot be overridden or staticified.
+      return true;
+    }
+
+    @Override
     public boolean visit(JMethod x, Context ctx) {
       Set<JMethod> overrides = program.typeOracle.getAllOverrides(x);
       if (!overrides.isEmpty()) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java
index 273e947..434208d 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ToStringGenerationVisitor.java
@@ -37,6 +37,7 @@
 import com.google.gwt.dev.jjs.ast.JClassLiteral;
 import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JConditional;
+import com.google.gwt.dev.jjs.ast.JConstructor;
 import com.google.gwt.dev.jjs.ast.JContinueStatement;
 import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
 import com.google.gwt.dev.jjs.ast.JDoStatement;
@@ -430,7 +431,7 @@
     printStaticFlag(x);
     printType(x);
     space();
-    printUniqueName(x);
+    printName(x);
     return false;
   }
 
@@ -445,7 +446,7 @@
       printTypeName(x.getField().getEnclosingType());
     }
     print('.');
-    printUniqueName(x.getField());
+    printName(x.getField());
     return false;
   }
 
@@ -610,6 +611,29 @@
   }
 
   @Override
+  public boolean visit(JConstructor x, Context ctx) {
+    // Modifiers
+    if (x.isPrivate()) {
+      print(CHARS_PRIVATE);
+    } else {
+      print(CHARS_PUBLIC);
+    }
+    printName(x);
+
+    // Parameters
+    printParameterList(x);
+
+    if (x.isAbstract() || !shouldPrintMethodBody()) {
+      semi();
+      newlineOpt();
+    } else {
+      accept(x.getBody());
+    }
+
+    return false;
+  }
+
+  @Override
   public boolean visit(JMethod x, Context ctx) {
     printMethodHeader(x);
 
@@ -633,17 +657,26 @@
   public boolean visit(JMethodCall x, Context ctx) {
     JExpression instance = x.getInstance();
     JMethod target = x.getTarget();
-    if (instance != null) {
+    if (instance == null) {
+      // Static call.
+      printTypeName(target.getEnclosingType());
+      print('.');
+      printName(target);
+    } else if (x.isStaticDispatchOnly()) {
+      // super() or this() call.
+      JReferenceType thisType = (JReferenceType) x.getInstance().getType();
+      thisType = thisType.getUnderlyingType();
+      if (thisType == target.getEnclosingType()) {
+        print(CHARS_THIS);
+      } else {
+        print(CHARS_SUPER);
+      }
+    } else {
+      // Instance call.
       parenPush(x, instance);
       accept(instance);
       parenPop(x, instance);
-    } else {
-      printTypeName(target.getEnclosingType());
-    }
-    print('.');
-    if (target.isStatic()) {
-      printUniqueName(target);
-    } else {
+      print('.');
       printName(target);
     }
     lparen();
@@ -691,8 +724,10 @@
   @Override
   public boolean visit(JNewInstance x, Context ctx) {
     print(CHARS_NEW);
-    printType(x);
+    JConstructor target = x.getTarget();
+    printName(target);
     lparen();
+    visitCollectionWithCommas(x.getArgs().iterator());
     rparen();
     return false;
   }
@@ -1050,11 +1085,7 @@
     printFinalFlag(x);
     printType(x);
     space();
-    if (x.isStatic()) {
-      printUniqueName(x);
-    } else {
-      printName(x);
-    }
+    printName(x);
 
     // Parameters
     printParameterList(x);
@@ -1128,9 +1159,4 @@
       accept(iter.next());
     }
   }
-
-  private void printUniqueName(HasName x) {
-    print(x.getName());
-  }
-
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
index ab7ea02..764d198 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
@@ -37,6 +37,7 @@
 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.JNewInstance;
 import com.google.gwt.dev.jjs.ast.JNullLiteral;
 import com.google.gwt.dev.jjs.ast.JNullType;
 import com.google.gwt.dev.jjs.ast.JParameter;
@@ -126,6 +127,7 @@
       boolean isStatic = method.isStatic();
       boolean isStaticImpl = program.isStaticImpl(method);
       if (isStatic && !isStaticImpl && instance != null) {
+        // TODO: move to DeadCodeElimination.
         // this doesn't really belong here, but while we're here let's remove
         // non-side-effect qualifiers to statics
         if (!instance.hasSideEffects()) {
@@ -143,6 +145,11 @@
         ctx.replaceMe(Pruner.transformToNullMethodCall(x, program));
       }
     }
+
+    @Override
+    public void endVisit(JNewInstance x, Context ctx) {
+      // Do not visit.
+    }
   }
 
   /**
@@ -272,7 +279,7 @@
     public boolean visit(JMethod x, Context ctx) {
       currentMethod = x;
 
-      if (!x.isStatic()) {
+      if (x.canBePolymorphic()) {
         /*
          * Add an assignment to each parameter from that same parameter in every
          * method this method overrides.
@@ -656,7 +663,7 @@
      * return <code>null</code> no matter what.
      */
     private JMethod getSingleConcreteMethod(JMethod method) {
-      if (method.isStatic()) {
+      if (!method.canBePolymorphic()) {
         return null;
       }
       if (getSingleConcreteType(method.getEnclosingType()) != null) {
diff --git a/dev/core/src/com/google/gwt/dev/js/JsHoister.java b/dev/core/src/com/google/gwt/dev/js/JsHoister.java
index 15113e9..20dae7e 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsHoister.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsHoister.java
@@ -48,7 +48,7 @@
  * {@link JsInliner}. <b>Not all expressions are necessarily implemented</b>,
  * only those that are safe to hoist into outer call sites.
  */
-final class JsHoister {
+public final class JsHoister {
   /**
    * Implements actual cloning logic. We rely on the JsExpressions to provide
    * traversal logic. The {@link #stack} field is used to accumulate
@@ -56,8 +56,8 @@
    * that argument lists are on the stack in reverse order, so lists should be
    * constructed via inserts, rather than appends.
    */
-  private static class Cloner extends JsVisitor {
-    private final Stack<JsExpression> stack = new Stack<JsExpression>();
+  public static class Cloner extends JsVisitor {
+    protected final Stack<JsExpression> stack = new Stack<JsExpression>();
     private boolean successful = true;
 
     @Override
diff --git a/user/src/com/google/gwt/rpc/server/WebModePayloadSink.java b/user/src/com/google/gwt/rpc/server/WebModePayloadSink.java
index a93e308..e8b0d20 100644
--- a/user/src/com/google/gwt/rpc/server/WebModePayloadSink.java
+++ b/user/src/com/google/gwt/rpc/server/WebModePayloadSink.java
@@ -324,37 +324,30 @@
           x.getSerializerClass(), "instantiate",
           SerializationStreamReader.class);
 
-      // x = $Foo(new Foo),
+      // x = new Foo,
       // x = instantiate(reader),
       push(currentBackRef);
       eq();
       if (instantiateIdent == null) {
         // No instantiate method, we'll have to invoke the constructor
 
-        instantiateIdent = clientOracle.getSeedName(x.getTargetClass());
-        assert instantiateIdent != null : "instantiateIdent";
-
-        // $Foo()
+        // new Foo()
         String constructorMethodName;
         if (x.getTargetClass().getEnclosingClass() == null) {
-          constructorMethodName = "$" + x.getTargetClass().getSimpleName();
+          constructorMethodName = x.getTargetClass().getSimpleName();
         } else {
           String name = x.getTargetClass().getName();
-          constructorMethodName = "$"
-              + name.substring(name.lastIndexOf('.') + 1);
+          constructorMethodName = name.substring(name.lastIndexOf('.') + 1);
         }
 
         String constructorIdent = clientOracle.getMethodId(x.getTargetClass(),
-            constructorMethodName, x.getTargetClass());
+            constructorMethodName);
         assert constructorIdent != null : "constructorIdent "
             + constructorMethodName;
 
-        // constructor(new Seed),
-        push(constructorIdent);
-        lparen();
+        // new constructor,
         _new();
-        push(instantiateIdent);
-        rparen();
+        push(constructorIdent);
         comma();
       } else {
         // instantiate(reader),