Roll seed-function optimization forward again.


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10480 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/SymbolData.java b/dev/core/src/com/google/gwt/core/ext/linker/SymbolData.java
index 368acec..97cd1d7 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/SymbolData.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/SymbolData.java
@@ -108,6 +108,11 @@
   int getQueryId();
 
   /**
+   * Returns the seedId for types.
+   */
+  int getSeedId();
+
+  /**
    * Returns the line number on which the symbol was originally declared or
    * <code>-1</code> if the line number is unknown.
    */
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSymbolData.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSymbolData.java
index fc57185..78cfb0c 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSymbolData.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSymbolData.java
@@ -32,15 +32,15 @@
 public class StandardSymbolData implements SymbolData {
 
   public static StandardSymbolData forClass(String className, String uriString,
-      int lineNumber, int queryId, CastableTypeMap castableTypeMap) {
+      int lineNumber, int queryId, CastableTypeMap castableTypeMap, int seedId) {
     return new StandardSymbolData(className, null, null, uriString, lineNumber,
-        queryId, castableTypeMap);
+        queryId, castableTypeMap, seedId);
   }
 
   public static StandardSymbolData forMember(String className,
       String memberName, String methodSig, String uriString, int lineNumber) {
     return new StandardSymbolData(className, memberName, methodSig, uriString,
-        lineNumber, 0, null);
+        lineNumber, 0, null, -1);
   }
 
   public static String toUriString(String fileName) {
@@ -63,11 +63,12 @@
   private String sourceUri;
   private String symbolName;
   private int queryId;
+  private int seedId;
   private CastableTypeMap castableTypeMap;
 
   private StandardSymbolData(String className, String memberName,
       String methodSig, String sourceUri, int sourceLine, int queryId, 
-      CastableTypeMap castableTypeMap) {
+      CastableTypeMap castableTypeMap, int seedId) {
     assert className != null && className.length() > 0 : "className";
     assert memberName != null || methodSig == null : "methodSig without memberName";
     assert sourceLine >= -1 : "sourceLine: " + sourceLine;
@@ -79,6 +80,7 @@
     this.sourceLine = sourceLine;
     this.queryId = queryId;
     this.castableTypeMap = castableTypeMap;
+    this.seedId = seedId;
   }
   
   public CastableTypeMap getCastableTypeMap() {
@@ -107,6 +109,10 @@
     return queryId;
   }
 
+  public int getSeedId() {
+    return seedId;
+  }
+
   public int getSourceLine() {
     return sourceLine;
   }
@@ -161,6 +167,7 @@
     symbolName = in.readUTF();
     queryId = in.readInt();
     castableTypeMap = (CastableTypeMap) in.readObject();
+    seedId = in.readInt();
   }
 
   /**
@@ -189,5 +196,6 @@
     out.writeUTF(symbolName);
     out.writeInt(queryId);
     out.writeObject(castableTypeMap);
+    out.writeInt(seedId);
   }
 }
diff --git a/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java b/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java
index 47906e4..a32d252 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java
@@ -22,9 +22,6 @@
 import com.google.gwt.core.ext.TreeLogger;
 
 import java.io.Serializable;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
 import java.util.TreeSet;
 
 /**
@@ -155,7 +152,7 @@
           possibleValues.add(v);
         }
         return new DefaultSelectionProperty(value, prop.getFallback(), name,
-            possibleValues, (Map<String, List<Set<String>>>) prop.getFallbackValuesMap());
+            possibleValues, prop.getFallbackValuesMap());
       }
     }
 
diff --git a/dev/core/src/com/google/gwt/dev/javac/testing/impl/JavaResourceBase.java b/dev/core/src/com/google/gwt/dev/javac/testing/impl/JavaResourceBase.java
index ee1c256..ea413fb 100644
--- a/dev/core/src/com/google/gwt/dev/javac/testing/impl/JavaResourceBase.java
+++ b/dev/core/src/com/google/gwt/dev/javac/testing/impl/JavaResourceBase.java
@@ -302,11 +302,12 @@
       StringBuilder code = new StringBuilder();
       code.append("package java.lang;\n");
       code.append("public class Object {\n");
+      code.append("  private Class<?> ___clazz;");
       code.append("  public boolean equals(Object that){return this == that;}");
       code.append("  public int hashCode() { return 0; }\n");
       code.append("  public String toString() { return \"Object\"; }\n");
       code.append("  public Object clone() { return this; } ");
-      code.append("  public Class<?> getClass() { return Object.class; } ");
+      code.append("  public Class<?> getClass() { return ___clazz; } ");
       code.append("}\n");
       return code;
     }
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 bac3201..67d86c7 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -99,6 +99,7 @@
 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.ReplaceGetClassOverrides;
 import com.google.gwt.dev.jjs.impl.ReplaceRebinds;
 import com.google.gwt.dev.jjs.impl.ReplaceRunAsyncs;
 import com.google.gwt.dev.jjs.impl.ResolveRebinds;
@@ -307,6 +308,8 @@
       // (6) Perform further post-normalization optimizations
       // Prune everything
       Pruner.exec(jprogram, false);
+      // prune all Object.getClass() overrides and replace with inline field ref
+      ReplaceGetClassOverrides.exec(jprogram);
 
       // (7) Generate a JavaScript code DOM from the Java type declarations
       jprogram.typeOracle.recomputeAfterOptimizations();
@@ -335,7 +338,7 @@
       /*
        * Creates new variables, must run before code splitter and namer.
        */
-      JsStackEmulator.exec(jsProgram, propertyOracles);
+      JsStackEmulator.exec(jprogram, jsProgram, propertyOracles, jjsmap);
 
       /*
        * Work around Safari 5 bug by rewriting a >> b as ~~a >> b.
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 dfb9119..63351a5 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
@@ -64,6 +64,21 @@
       "com.google.gwt.lang.CollapsedPropertyHolder", "com.google.gwt.lang.Exceptions",
       "com.google.gwt.lang.LongLib", "com.google.gwt.lang.Stats", "com.google.gwt.lang.Util"));
 
+  /*
+   * Types which are not referenced by any Java code, but are required to exist
+   * after Java optimizations have run in order to be used by backend
+   * code-generation. These classes and their members, are considered live
+   * by ControlFlowAnalysis, at all times. Immortal types always live in the
+   * initial fragment and their definitions are hoisted to appear before all
+   * other types. Only static methods and fields are allowed, and no clinits
+   * are run. Field initializers must be primitives, literals, or one of
+   * JSO.createObject() or JSO.createArray().
+   *
+   * Classes are inserted into the JsAST in the order they appear in the Set.
+   */
+  public static final Set<String> IMMORTAL_CODEGEN_TYPES_SET = new LinkedHashSet<String>(Arrays.asList(
+      "com.google.gwt.lang.SeedUtil"));
+
   public static final Set<String> INDEX_TYPES_SET = new LinkedHashSet<String>(Arrays.asList(
       "java.io.Serializable", "java.lang.Object", "java.lang.String", "java.lang.Class",
       "java.lang.CharSequence", "java.lang.Cloneable", "java.lang.Comparable", "java.lang.Enum",
@@ -98,6 +113,7 @@
       new HashMap<String, JPrimitiveType>();
 
   static {
+    CODEGEN_TYPES_SET.addAll(IMMORTAL_CODEGEN_TYPES_SET);
     INDEX_TYPES_SET.addAll(CODEGEN_TYPES_SET);
 
     /*
@@ -302,6 +318,7 @@
   }
 
   public final List<JClassType> codeGenTypes = new ArrayList<JClassType>();
+  public final List<JClassType> immortalCodeGenTypes = new ArrayList<JClassType>();
 
   public final JTypeOracle typeOracle = new JTypeOracle(this);
 
@@ -314,6 +331,8 @@
 
   private IdentityHashMap<JReferenceType, JsCastMap> castMaps;
 
+  private Map<JType, JField> classLiteralFields;
+
   /**
    * A factory to create correlations.
    */
@@ -391,6 +410,11 @@
     if (CODEGEN_TYPES_SET.contains(name)) {
       codeGenTypes.add((JClassType) type);
     }
+
+    if (IMMORTAL_CODEGEN_TYPES_SET.contains(name)) {
+      immortalCodeGenTypes.add((JClassType) type);
+    }
+    
     if (INDEX_TYPES_SET.contains(name)) {
       indexedTypes.put(type.getShortName(), type);
       for (JMethod method : type.getMethods()) {
@@ -730,6 +754,10 @@
     return castMaps.get(referenceType);
   }
 
+  public JField getClassLiteralField(JType type) {
+    return classLiteralFields.get(isJavaScriptObject(type) ? getJavaScriptObject() : type);
+  }
+
   public String getClassLiteralName(JType type) {
     return type.getJavahSignatureName() + "_classLit";
   }
@@ -1011,6 +1039,10 @@
     }
   }
 
+  public void recordClassLiteralFields(Map<JType, JField> classLiteralFields) {
+    this.classLiteralFields = classLiteralFields;
+  }
+
   public void recordQueryIds(Map<JReferenceType, Integer> queryIdsByType,
       List<JReferenceType> typesByQueryId) {
     this.queryIdsByType = queryIdsByType;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JSeedIdOf.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JSeedIdOf.java
new file mode 100644
index 0000000..ab1ca95
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JSeedIdOf.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2011 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;
+
+/**
+ * An AST node whose evaluation results in the seedId of its node.
+ */
+public class JSeedIdOf extends JNameOf {
+
+  public JSeedIdOf(SourceInfo info, JClassType stringType, HasName node) {
+    super(info, stringType, node);
+  }
+
+  public void traverse(JVisitor visitor, Context ctx) {
+    if (visitor.visit(this, ctx)) {
+      // Intentionally not visiting referenced node
+    }
+    visitor.endVisit(this, ctx);
+  }
+
+}
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 b9095eb..8850cb6 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
@@ -565,6 +565,10 @@
     return results;
   }
 
+  public Set<JReferenceType> getInstantiatedTypes() {
+    return instantiatedTypes;
+  }
+
   public JMethod getPolyMethod(JClassType type, String signature) {
     return getOrCreatePolyMap(type).get(signature);
   }
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 885d8e7..8e17e97 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
@@ -417,6 +417,10 @@
     endVisit((JExpression) x, ctx);
   }
 
+  public void endVisit(JSeedIdOf x, Context ctx) {
+    endVisit((JNameOf) x, ctx);
+  }
+
   public void endVisit(JsCastMap x, Context ctx) {
     endVisit((JsonArray) x, ctx);
   }
@@ -741,6 +745,10 @@
     return visit((JExpression) x, ctx);
   }
 
+  public boolean visit(JSeedIdOf x, Context ctx) {
+    return visit((JNameOf) x, ctx);
+  }
+
   public boolean visit(JsCastMap x, Context ctx) {
     return visit((JsonArray) x, ctx);
   }
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 7730084..4c85a0a 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
@@ -24,6 +24,7 @@
 import com.google.gwt.dev.jjs.ast.Context;
 import com.google.gwt.dev.jjs.ast.JArrayType;
 import com.google.gwt.dev.jjs.ast.JClassLiteral;
+import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JDeclaredType;
 import com.google.gwt.dev.jjs.ast.JExpression;
 import com.google.gwt.dev.jjs.ast.JField;
@@ -454,7 +455,7 @@
     cfa.setDependencyRecorder(dependencyRecorder);
     cfa.traverseEntryMethods();
     traverseClassArray(jprogram, cfa);
-
+    traverseImmortalTypes(jprogram, cfa);
     dependencyRecorder.endDependencyGraph();
     return cfa;
   }
@@ -580,6 +581,21 @@
     }
   }
 
+  /**
+   * Any immortal codegen types must be part of the initial download.
+   */
+  private static void traverseImmortalTypes(JProgram jprogram,
+      ControlFlowAnalyzer cfa) {
+    for (JClassType type : jprogram.immortalCodeGenTypes) {
+      cfa.traverseFromInstantiationOf(type);
+      for (JMethod method : type.getMethods()) {
+        if (!method.needsVtable()) {
+          cfa.traverseFrom(method);
+        }
+      }
+    }
+  }
+
   private static <T> Set<T> union(Set<? extends T> set1, Set<? extends T> set2) {
     Set<T> union = new HashSet<T>();
     union.addAll(set1);
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 36c534f..f9b3aaa 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
@@ -518,6 +518,16 @@
       return false;
     }
 
+    private void maybeRescueClassLiteral(JReferenceType type) {
+      if (liveFieldsAndMethods.contains(getClassMethod) || liveFieldsAndMethods.contains(getClassField)) {
+        // getClass() already live so rescue class literal immediately
+        rescue(program.getClassLiteralField(type));
+      } else {
+        // getClass() not live yet, so mark for later rescue
+        classLiteralsToBeRescuedIfGetClassIsLive.add(type);
+      }
+    }
+
     /**
      * Subclasses of JavaScriptObject are never instantiated directly. They are
      * implicitly created when a JSNI method passes a reference to an existing
@@ -574,6 +584,9 @@
             maybeRescueJavaScriptObjectPassingIntoJava(method.getType());
           }
           rescueOverridingMethods(method);
+          if (method == getClassMethod) {
+            rescueClassLiteralsIfGetClassIsLive();
+          }
           return true;
         }
       }
@@ -594,6 +607,8 @@
       boolean doVisit = false;
       if (isInstantiated && !instantiatedTypes.contains(type)) {
         instantiatedTypes.add(type);
+        maybeRescueClassLiteral(type);
+
         doVisit = true;
       }
 
@@ -623,6 +638,10 @@
       if (var != null) {
         if (liveFieldsAndMethods.add(var)) {
           membersToRescueIfTypeIsInstantiated.remove(var);
+          if (var == getClassField) {
+            rescueClassLiteralsIfGetClassIsLive();
+          }
+
           if (isStaticFieldInitializedToLiteral(var)) {
             /*
              * Rescue literal initializers when the field is rescued, not when
@@ -746,6 +765,18 @@
       }
     }
 
+    private void rescueClassLiteralsIfGetClassIsLive() {
+      if (classLiteralsToBeRescuedIfGetClassIsLive != null) {
+        // guard against re-entrant calls. This only needs to run once.
+        Set<JReferenceType> toRescue = classLiteralsToBeRescuedIfGetClassIsLive;
+        classLiteralsToBeRescuedIfGetClassIsLive = null;
+
+        for (JReferenceType classLit : toRescue) {
+          maybeRescueClassLiteral(classLit);
+        }
+      }
+    }
+
     /**
      * If the type is instantiable, rescue any of its virtual methods that a
      * previously seen method call could call.
@@ -807,6 +838,13 @@
 
   private final JMethod asyncFragmentOnLoad;
   private final JDeclaredType baseArrayType;
+
+  /**
+   * Schrodinger set of classLiterals to be rescued if type is instantiated AND getClass()
+   * is live.
+   */
+  private Set<JReferenceType> classLiteralsToBeRescuedIfGetClassIsLive = new HashSet<JReferenceType>();
+
   private DependencyRecorder dependencyRecorder;
   private Set<JField> fieldsWritten = new HashSet<JField>();
   private Set<JReferenceType> instantiatedTypes = new HashSet<JReferenceType>();
@@ -827,6 +865,8 @@
    */
   private Map<JMethod, List<JMethod>> methodsThatOverrideMe;
 
+  private final JField getClassField;
+  private final JMethod getClassMethod;
   private final JProgram program;
   private Set<JReferenceType> referencedTypes = new HashSet<JReferenceType>();
   private final RescueVisitor rescuer = new RescueVisitor();
@@ -851,6 +891,8 @@
           new HashMap<JParameter, List<JExpression>>(cfa.argsToRescueIfParameterRead);
     }
     methodsThatOverrideMe = cfa.methodsThatOverrideMe;
+    getClassField = program.getIndexedField("Object.___clazz");
+    getClassMethod = program.getIndexedMethod("Object.getClass");
   }
 
   public ControlFlowAnalyzer(JProgram program) {
@@ -858,6 +900,8 @@
     asyncFragmentOnLoad = program.getIndexedMethod("AsyncFragmentLoader.onLoad");
     runAsyncOnsuccess = program.getIndexedMethod("RunAsyncCallback.onSuccess");
     baseArrayType = program.getIndexedType("Array");
+    getClassField = program.getIndexedField("Object.___clazz");
+    getClassMethod = program.getIndexedMethod("Object.getClass");
     buildMethodsOverriding();
   }
 
@@ -964,6 +1008,15 @@
     runAsync.traverseOnSuccess(rescuer);
   }
 
+  /**
+   * Traverse the fragments for all runAsyncs.
+   */
+  public void traverseFromRunAsyncs() {
+    for (JRunAsync runAsync : program.getRunAsyncs()) {
+      traverseFromRunAsync(runAsync);
+    }
+  }
+
   private void buildMethodsOverriding() {
     methodsThatOverrideMe = new HashMap<JMethod, List<JMethod>>();
     for (JDeclaredType type : program.getDeclaredTypes()) {
@@ -979,13 +1032,4 @@
       }
     }
   }
-
-  /**
-   * Traverse the fragments for all runAsyncs.
-   */
-  private void traverseFromRunAsyncs() {
-    for (JRunAsync runAsync : program.getRunAsyncs()) {
-      traverseFromRunAsync(runAsync);
-    }
-  }
 }
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 a522b6e..8d75fc9 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
@@ -31,15 +31,15 @@
 import com.google.gwt.dev.js.ast.JsExpression;
 import com.google.gwt.dev.js.ast.JsFunction;
 import com.google.gwt.dev.js.ast.JsInvocation;
+import com.google.gwt.dev.js.ast.JsModVisitor;
 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.JsNumberLiteral;
-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;
 import com.google.gwt.dev.js.ast.JsVars.JsVar;
+import com.google.gwt.dev.js.ast.JsVisitable;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -238,21 +238,31 @@
      * The type whose vtables can currently be installed.
      */
     JClassType currentVtableType = null;
+    JClassType pendingVtableType = null;
+    JsExprStmt pendingDefineSeed = null;
 
-    // Since we haven't run yet.
+
+      // Since we haven't run yet.
     assert jsprogram.getFragmentCount() == 1;
+
     List<JsStatement> stats = jsprogram.getGlobalBlock().getStatements();
     for (JsStatement stat : stats) {
+
       boolean keepIt;
       JClassType vtableTypeAssigned = vtableTypeAssigned(stat);
-      if (vtableTypeAssigned != null && livenessPredicate.isLive(vtableTypeAssigned)) {
-        JsExprStmt result =
-            extractPrototypeSetup(livenessPredicate, alreadyLoadedPredicate, stat,
-                vtableTypeAssigned);
-        if (result != null) {
+      if (vtableTypeAssigned != null
+          && livenessPredicate.isLive(vtableTypeAssigned)) {
+        boolean[] anyCtorsSetup = new boolean[1];
+        JsExprStmt result = maybeRemoveCtorsFromDefineSeedStmt(livenessPredicate,
+            alreadyLoadedPredicate, stat, anyCtorsSetup);
+        boolean anyWorkDone = anyCtorsSetup[0]
+            || !alreadyLoadedPredicate.isLive(vtableTypeAssigned);
+        if (anyWorkDone) {
           stat = result;
           keepIt = true;
         } else {
+          pendingDefineSeed = result;
+          pendingVtableType = vtableTypeAssigned;
           keepIt = false;
         }
       } else if (containsRemovableVars(stat)) {
@@ -270,8 +280,11 @@
         }
         JClassType vtableType = vtableTypeNeeded(stat);
         if (vtableType != null && vtableType != currentVtableType) {
-          extractedStats.add(vtableStatFor(vtableType));
-          currentVtableType = vtableType;
+          assert pendingVtableType == vtableType;
+          extractedStats.add(pendingDefineSeed);
+          currentVtableType = pendingVtableType;
+          pendingDefineSeed = null;
+          pendingVtableType = null;
         }
         extractedStats.add(stat);
       }
@@ -323,57 +336,6 @@
     return false;
   }
 
-  /**
-   * 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 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;
-  }
-
   private boolean isLive(JsStatement stat, LivenessPredicate livenessPredicate) {
     JClassType type = map.typeForStatement(stat);
     if (type != null) {
@@ -420,6 +382,42 @@
   }
 
   /**
+   * 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.
+   */
+  private JsExprStmt maybeRemoveCtorsFromDefineSeedStmt(
+      final LivenessPredicate livenessPredicate,
+      final LivenessPredicate alreadyLoadedPredicate, JsStatement stat,
+      final boolean[] anyCtorsSetup) {
+    Cloner c = new Cloner();
+    c.accept(((JsExprStmt) stat).getExpression());
+    JsExprStmt result = c.getExpression().makeStmt();
+    new JsModVisitor() {
+      public void endVisit(JsNameRef x, JsContext ctx) {
+        JMethod maybeCtor = map.nameToMethod(x.getName());
+        if (maybeCtor instanceof JConstructor) {
+          JConstructor ctor = (JConstructor) maybeCtor;
+          if (!livenessPredicate.isLive(ctor)
+              || alreadyLoadedPredicate.isLive(ctor)) {
+            ctx.removeMe();
+          } else {
+            anyCtorsSetup[0] = true;
+          }
+        }
+      };
+
+      /**
+       * Overridden to allow insert/remove on the varargs portion.
+       */
+      protected <T extends JsVisitable> void doAcceptList(List<T> collection) {
+        doAcceptWithInsertRemove(collection);
+      };
+    }.accept(result);
+    return result;
+  }
+
+  /**
    * Return the Java method corresponding to <code>stat</code>, or
    * <code>null</code> if there isn't one. It recognizes JavaScript of the form
    * <code>function foo(...) { ...}</code>, where <code>foo</code> is the name
@@ -465,40 +463,30 @@
   }
 
   /**
-   * Compute a statement that can be used to set up for installing instance
-   * methods into a vtable. It will be of the form
-   * <code>_ = foo.prototype</code>, where <code>foo</code> is the constructor
-   * function for <code>vtableType</code>.
-   */
-  private JsStatement vtableStatFor(JClassType vtableType) {
-    JsNameRef prototypeField =
-        new JsNameRef(jsprogram.createSourceInfoSynthetic(FragmentExtractor.class), "prototype");
-    JsExpression constructorRef;
-    SourceInfo sourceInfoVtableSetup = jsprogram.createSourceInfoSynthetic(FragmentExtractor.class);
-    if (vtableType == jprogram.getTypeJavaLangString()) {
-      // The methods of java.lang.String are put onto JavaScript's String
-      // prototype
-      SourceInfo sourceInfoConstructorRef =
-          jsprogram.createSourceInfoSynthetic(FragmentExtractor.class);
-      constructorRef = new JsNameRef(sourceInfoConstructorRef, "String");
-    } else {
-      constructorRef = map.nameForType(vtableType).makeRef(sourceInfoVtableSetup);
-    }
-    prototypeField.setQualifier(constructorRef);
-    SourceInfo underlineSourceInfo = jsprogram.createSourceInfoSynthetic(FragmentExtractor.class);
-    return (new JsBinaryOperation(sourceInfoVtableSetup, JsBinaryOperator.ASG, jsprogram.getScope()
-        .declareName("_").makeRef(underlineSourceInfo), prototypeField)).makeStmt();
-  }
-
-  /**
-   * If <code>state</code> is of the form <code>_ = Foo.prototype = exp</code>,
-   * then return <code>Foo</code>. Otherwise return <code>null</code>.
+   * If <code>state</code> is of the form <code>_ = String.prototype</code>,
+   * then return <code>String</code>. If the form is
+   * <code>defineSeed(id, superId, cTM, ctor1, ctor2, ...)</code> return the type
+   * corresponding to that id. Otherwise return <code>null</code>.
    */
   private JClassType vtableTypeAssigned(JsStatement stat) {
     if (!(stat instanceof JsExprStmt)) {
       return null;
     }
     JsExprStmt expr = (JsExprStmt) stat;
+    if (expr.getExpression() instanceof JsInvocation) {
+      // Handle a defineSeed call.
+      JsInvocation call = (JsInvocation) expr.getExpression();
+      if (!(call.getQualifier() instanceof JsNameRef)) {
+        return null;
+      }
+      JsNameRef func = (JsNameRef) call.getQualifier();
+      if (func.getName() != jsprogram.getIndexedFunction("SeedUtil.defineSeed").getName()) {
+        return null;
+      }
+      return map.typeForStatement(stat);
+    }
+
+    // Handle String.
     if (!(expr.getExpression() instanceof JsBinaryOperation)) {
       return null;
     }
@@ -510,30 +498,26 @@
       return null;
     }
     JsNameRef lhs = (JsNameRef) binExpr.getArg1();
-    if (lhs.getQualifier() != null) {
+    JsName underBar = jsprogram.getScope().findExistingName("_");
+    assert underBar != null;
+    if (lhs.getName() != underBar) {
       return null;
     }
-    if (lhs.getName() == null) {
-      return null;
-    }
-    if (!lhs.getName().getShortIdent().equals("_")) {
-      return null;
-    }
-    if (!(binExpr.getArg2() instanceof JsBinaryOperation)) {
-      return null;
-    }
-    JsBinaryOperation binExprRhs = (JsBinaryOperation) binExpr.getArg2();
-    if (binExprRhs.getOperator() != JsBinaryOperator.ASG) {
-      return null;
-    }
-    if (!(binExprRhs.getArg1() instanceof JsNameRef)) {
-      return null;
-    }
-    JsNameRef middleNameRef = (JsNameRef) binExprRhs.getArg1();
-    if (!middleNameRef.getName().getShortIdent().equals("prototype")) {
+    if (!(binExpr.getArg2() instanceof JsNameRef)) {
       return null;
     }
 
+    JsNameRef rhsRef = (JsNameRef) binExpr.getArg2();
+    if (!(rhsRef.getQualifier() instanceof JsNameRef)) {
+      return null;
+    }
+    if (!((JsNameRef) rhsRef.getQualifier()).getShortIdent().equals("String")) {
+      return null;
+    }
+
+    if (!rhsRef.getName().getShortIdent().equals("prototype")) {
+      return null;
+    }
     return map.typeForStatement(stat);
   }
 
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 87a6aa7..3c4883f 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
@@ -514,14 +514,17 @@
             // Just use JavaScriptObject's implementation for all subclasses.
             currentClass.getMethods().remove(2);
           } else {
-            tryFindUpRefs(method);
-            SourceInfo info = method.getSourceInfo();
-            if (isScript(program) && currentClass == program.getIndexedType("Array")) {
-              // Special implementation: return this.arrayClass
-              implementMethod(method, new JFieldRef(info, new JThisRef(info,
-                  (JClassType) currentClass), program.getIndexedField("Array.arrayClass"),
-                  currentClass));
+            if (currentClass == program.getIndexedType("Array")) {
+              /*
+              * Don't implement, fall through to Object.getClass(). Array emulation code
+              * in com.google.gwt.lang.Array invokes Array.getClass() and expects to get the
+              * class literal for the actual runtime type of the array (e.g. Foo[].class) and
+              * not Array.class.
+              */
+              currentClass.getMethods().remove(2);
             } else {
+              tryFindUpRefs(method);
+              SourceInfo info = method.getSourceInfo();
               implementMethod(method, new JClassLiteral(info.makeChild(), currentClass));
             }
           }
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 8cfa63e..c599720 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
@@ -76,6 +76,7 @@
 import com.google.gwt.dev.jjs.ast.JReboundEntryPoint;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
 import com.google.gwt.dev.jjs.ast.JReturnStatement;
+import com.google.gwt.dev.jjs.ast.JSeedIdOf;
 import com.google.gwt.dev.jjs.ast.JStatement;
 import com.google.gwt.dev.jjs.ast.JSwitchStatement;
 import com.google.gwt.dev.jjs.ast.JThisRef;
@@ -138,6 +139,7 @@
 import com.google.gwt.dev.js.ast.JsReturn;
 import com.google.gwt.dev.js.ast.JsRootScope;
 import com.google.gwt.dev.js.ast.JsScope;
+import com.google.gwt.dev.js.ast.JsSeedIdOf;
 import com.google.gwt.dev.js.ast.JsStatement;
 import com.google.gwt.dev.js.ast.JsSwitch;
 import com.google.gwt.dev.js.ast.JsSwitchMember;
@@ -388,7 +390,7 @@
          * as var foo = function blah() {} and introduce a separate scope for
          * the function's name according to EcmaScript-262, but this would mess
          * up stack traces by allowing two inner scope function names to
-         * onfuscate to the same identifier, making function names no longer a
+         * obfuscate to the same identifier, making function names no longer a
          * 1:1 mapping to obfuscated symbols. Leaving them in global scope
          * causes no harm.
          */
@@ -405,6 +407,7 @@
             Maps.put(indexedFunctions, x.getEnclosingType().getShortName() + "." + x.getName(),
                 jsFunction);
       }
+
       return true;
     }
 
@@ -499,7 +502,8 @@
 
       StandardSymbolData symbolData =
           StandardSymbolData.forClass(x.getName(), x.getSourceInfo().getFileName(), x
-              .getSourceInfo().getStartLine(), program.getQueryId(x), castableTypeMap);
+              .getSourceInfo().getStartLine(), program.getQueryId(x), castableTypeMap,
+              x instanceof JClassType || x instanceof JArrayType ? getSeedId(x) : -1);
       assert !symbolTable.containsKey(symbolData);
       symbolTable.put(symbolData, jsName);
     }
@@ -568,6 +572,7 @@
         JsName name = topScope.declareName("Q$" + longName, "Q$" + shortName);
         namesByQueryId.add(name);
       }
+      // TODO(cromwellian): see about moving this into an immortal type
       StringBuilder sb = new StringBuilder();
       sb.append("function makeCastMap(a) {");
       sb.append("  var result = {};");
@@ -607,12 +612,16 @@
 
     private final JsName prototype = objectScope.declareName("prototype");
 
+
     {
       globalTemp.setObfuscatable(false);
       prototype.setObfuscatable(false);
       arrayLength.setObfuscatable(false);
     }
 
+    public GenerateJavaScriptVisitor() {
+    }
+    
     @Override
     public void endVisit(JAbsentArrayDimension x, Context ctx) {
       throw new InternalCompilerException("Should not get here.");
@@ -719,6 +728,11 @@
         return;
       }
 
+      if (program.immortalCodeGenTypes.contains(x)) {
+        // Handled in generateImmortalTypes
+        return;
+      }
+
       alreadyRan.add(x);
 
       List<JsFunction> jsFuncs = popList(x.getMethods().size()); // methods
@@ -738,6 +752,7 @@
       // declare all methods into the global scope
       for (int i = 0; i < jsFuncs.size(); ++i) {
         JsFunction func = jsFuncs.get(i);
+
         // don't add polymorphic JsFuncs, inline decl into vtable assignment
         if (func != null && !polymorphicJsFunctions.contains(func)) {
           globalStmts.add(func.makeStmt());
@@ -1260,7 +1275,7 @@
 
       // Long lits must go at the top, they can be constant field initializers.
       generateLongLiterals(vars);
-
+      generateImmortalTypes(vars);
       generateQueryIdConstants(vars);
 
       // Class objects, but only if there are any.
@@ -1298,6 +1313,12 @@
     }
 
     @Override
+    public void endVisit(JSeedIdOf x, Context ctx) {
+      JsName name = names.get(x.getNode());
+      push(new JsSeedIdOf(x.getSourceInfo(), name, getSeedId((JReferenceType) x.getNode())));
+    }
+
+    @Override
     public void endVisit(JsCastMap x, Context ctx) {
       super.endVisit(x, ctx);
       JsArrayLiteral arrayLit = (JsArrayLiteral) pop();
@@ -1418,6 +1439,11 @@
         return false;
       }
 
+      if (program.immortalCodeGenTypes.contains(x)) {
+        // Handled in generateImmortalTypes
+        return false;
+      }
+
       // force super type to generate code first, this is required for prototype
       // chaining to work properly
       if (x.getSuperClass() != null && !alreadyRan.contains(x)) {
@@ -1454,6 +1480,7 @@
       for (int i = 0; i < entryMethods.size(); i++) {
         entryMethodToIndex.put(entryMethods.get(i), i);
       }
+
       return true;
     }
 
@@ -1647,27 +1674,20 @@
       return new JsBinaryOperation(lhs.getSourceInfo(), JsBinaryOperator.COMMA, lhs, rhs);
     }
 
-    private void generateCastableTypeMap(JClassType x, List<JsStatement> globalStmts) {
+    private JsExpression generateCastableTypeMap(JClassType x) {
       JsCastMap castMap = program.getCastMap(x);
       if (castMap != null) {
         JField castableTypeMapField = program.getIndexedField("Object.castableTypeMap");
         JsName castableTypeMapName = names.get(castableTypeMapField);
         if (castableTypeMapName == null) {
           // Was pruned; this compilation must have no dynamic casts.
-          return;
+          return new JsObjectLiteral(SourceOrigin.UNKNOWN);
         }
 
-        // Generate castableTypeMap for each type prototype
-        // _.castableTypeMap$ = ...
-        SourceInfo sourceInfo = jsProgram.createSourceInfoSynthetic(GenerateJavaScriptAST.class);
-        JsNameRef fieldRef = castableTypeMapName.makeRef(sourceInfo);
-        fieldRef.setQualifier(globalTemp.makeRef(sourceInfo));
         accept(castMap);
-        JsExpression asg = createAssignment(fieldRef, (JsExpression) pop());
-        JsExprStmt asgStmt = asg.makeStmt();
-        globalStmts.add(asgStmt);
-        typeForStatMap.put(asgStmt, x);
+        return (JsExpression) pop();
       }
+      return new JsObjectLiteral(SourceOrigin.UNKNOWN);
     }
 
     private void generateClassLiteral(JDeclarationStatement decl, JsVars vars) {
@@ -1704,8 +1724,6 @@
         // special: setup the identifying typeMarker field
         generateTypeMarker(globalStmts);
       }
-
-      generateCastableTypeMap(x, globalStmts);
     }
 
     private void generateGwtOnLoad(List<JsFunction> entryFuncs, List<JsStatement> globalStmts) {
@@ -1806,6 +1824,78 @@
       errCall.getArguments().add(modName.makeRef(sourceInfo));
     }
 
+    private void generateImmortalTypes(JsVars globals) {
+      List<JsStatement> globalStmts = jsProgram.getGlobalBlock().getStatements();
+      List<JClassType> immortalTypes = new ArrayList<JClassType>(
+          program.immortalCodeGenTypes);
+      // visit in reverse order since insertions start at head
+      Collections.reverse(immortalTypes);
+      JMethod createObjMethod = program.getIndexedMethod("JavaScriptObject.createObject");
+      JMethod createArrMethod = program.getIndexedMethod("JavaScriptObject.createArray");
+
+      for (JClassType x : immortalTypes) {
+        // should not be pruned
+        assert x.getMethods().size() > 0;
+        // insert all static methods
+        for (JMethod method : x.getMethods()) {
+          /*
+           * Skip virtual methods and constructors. Even in cases where there is no constructor
+           * defined, the compiler will synthesize a default constructor which invokes
+           * a synthensized $init() method. We must skip both of these inserted methods.
+           */
+          if (method.needsVtable() || method instanceof JConstructor) {
+            continue;
+          }
+          if (JProgram.isClinit(method)) {
+            /**
+             * Emit empty clinits that will be pruned. If a type B extends A, then even if
+             * B and A have no fields to initialize, there will be a call inserted in B's clinit
+             * to invoke A's clinit. Likewise, if you have a static field initialized to
+             * JavaScriptObject.createObject(), the clinit() will include this initializer code,
+             * which we don't want.
+             */
+            JsFunction func = new JsFunction(x.getSourceInfo(), topScope,
+                topScope.declareName(mangleNameForGlobal(method)), true);
+            func.setBody(new JsBlock(method.getBody().getSourceInfo()));
+            push(func);
+          } else {
+            accept(method);
+          }
+          // add after var declaration, but before everything else
+          JsFunction func = (JsFunction) pop();
+          assert func.getName() != null;
+          globalStmts.add(1, func.makeStmt());
+        }
+
+        // insert fields into global var declaration
+        for (JField field : x.getFields()) {
+          assert field.isStatic() : "All fields on immortal types must be static.";
+          accept(field);
+          JsNode node = pop();
+          assert node instanceof JsVar;
+          JsVar fieldVar = (JsVar) node;
+          JExpression init = field.getInitializer();
+          if (init != null
+              && field.getLiteralInitializer() == null) {
+            // no literal, but it could be a JavaScriptObject
+            if (init.getType() == program.getJavaScriptObject()) {
+              assert init instanceof JMethodCall;
+              JMethod meth = ((JMethodCall) init).getTarget();
+              // immortal types can only have non-primitive literal initializers of createArray,createObject
+              if (meth == createObjMethod) {
+                fieldVar.setInitExpr(new JsObjectLiteral(init.getSourceInfo()));
+              } else if (meth == createArrMethod) {
+                fieldVar.setInitExpr(new JsArrayLiteral(init.getSourceInfo()));
+              } else {
+                assert false : "Illegal initializer expression for immortal field " + field;
+              }
+            }
+          }
+          globals.add(fieldVar);
+        }
+      }
+    }
+
     private void generateLongLiterals(JsVars vars) {
       for (Entry<Long, JsName> entry : longLits.entrySet()) {
         JsName jsName = entry.getValue();
@@ -1831,44 +1921,31 @@
     private void generateSeedFuncAndPrototype(JClassType x, List<JsStatement> globalStmts) {
       SourceInfo sourceInfo = x.getSourceInfo();
       if (x != program.getTypeJavaLangString()) {
-        JsName seedFuncName = names.get(x);
-
-        // seed function
-        // function com_example_foo_Foo() { }
-        JsFunction seedFunc = new JsFunction(sourceInfo, topScope, seedFuncName, true);
-        seedFuncName.setStaticRef(seedFunc);
-        JsBlock body = new JsBlock(sourceInfo);
-        seedFunc.setBody(body);
-        JsExprStmt seedFuncStmt = seedFunc.makeStmt();
-        globalStmts.add(seedFuncStmt);
-        typeForStatMap.put(seedFuncStmt, x);
-
-        // 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);
-          protoObj = newExpr;
-        } else {
-          protoObj = new JsObjectLiteral(sourceInfo);
-        }
-        JsExpression protoAsg = createAssignment(seedProtoRef, protoObj);
-
+        JsInvocation defineSeed = new JsInvocation(x.getSourceInfo());
+        JsName seedNameRef = indexedFunctions.get(
+            "SeedUtil.defineSeed").getName();
+        defineSeed.setQualifier(seedNameRef.makeRef(x.getSourceInfo()));
+        int newSeed = getSeedId(x);
+        assert newSeed > 0;
+        JClassType superClass = x.getSuperClass();
+        int superSeed = (superClass == null) ? -1 : getSeedId(x.getSuperClass());
+        // SeedUtil.defineSeed(queryId, superId, castableMap, constructors)
+        defineSeed.getArguments().add(new JsNumberLiteral(x.getSourceInfo(),
+            newSeed));
+        defineSeed.getArguments().add(new JsNumberLiteral(x.getSourceInfo(),
+            superSeed));
+        JsExpression castMap = generateCastableTypeMap(x);
+        defineSeed.getArguments().add(castMap);
+       
         // Chain assign the same prototype to every live constructor.
         for (JMethod method : x.getMethods()) {
           if (liveCtors.contains(method)) {
-            JsNameRef protoRef = prototype.makeRef(sourceInfo);
-            protoRef.setQualifier(names.get(method).makeRef(sourceInfo));
-            protoAsg = createAssignment(protoRef, protoAsg);
+            defineSeed.getArguments().add(names.get(method).makeRef(
+                sourceInfo));
           }
         }
 
-        // Finally, assign to the temp var for setup code.
-        JsExpression tmpAsg = createAssignment(globalTemp.makeRef(sourceInfo), protoAsg);
-        JsExprStmt tmpAsgStmt = tmpAsg.makeStmt();
+        JsStatement tmpAsgStmt = defineSeed.makeStmt();
         globalStmts.add(tmpAsgStmt);
         typeForStatMap.put(tmpAsgStmt, x);
       } else {
@@ -1883,6 +1960,16 @@
         JsExprStmt tmpAsgStmt = tmpAsg.makeStmt();
         globalStmts.add(tmpAsgStmt);
         typeForStatMap.put(tmpAsgStmt, x);
+        JField castableTypeMapField = program.getIndexedField("Object.castableTypeMap");
+        JsName castableTypeMapName = names.get(castableTypeMapField);
+        JsNameRef ctmRef = castableTypeMapName.makeRef(sourceInfo);
+        ctmRef.setQualifier(globalTemp.makeRef(sourceInfo));
+        JsExpression castMapLit = generateCastableTypeMap(x);
+        JsExpression ctmAsg = createAssignment(ctmRef,
+            castMapLit);
+        JsExprStmt ctmAsgStmt = ctmAsg.makeStmt();
+        globalStmts.add(ctmAsgStmt);
+        typeForStatMap.put(ctmAsgStmt, x);
       }
     }
 
@@ -2185,6 +2272,7 @@
   private final Map<JAbstractMethodBody, JsFunction> methodBodyMap =
       new IdentityHashMap<JAbstractMethodBody, JsFunction>();
   private final Map<HasName, JsName> names = new IdentityHashMap<HasName, JsName>();
+  private int nextSeedId = 1;
   private List<JsName> namesByQueryId;
   private JsFunction nullFunc;
 
@@ -2199,6 +2287,11 @@
   private final JProgram program;
 
   /**
+   * Map of class type to allocated seed id.
+   */
+  private final Map<JReferenceType, Integer> seedIdMap = new HashMap<JReferenceType, Integer>();
+
+  /**
    * All of the fields in String and Array need special handling for interop.
    */
   private final Map<JField, String> specialObfuscatedFields = new HashMap<JField, String>();
@@ -2280,8 +2373,8 @@
     namesToIdents.put("expando", "eX");
     namesToIdents.put("typeMarker", "tM");
     namesToIdents.put("castableTypeMap", "cM");
+    namesToIdents.put("___clazz", "cZ");
     // Array magic field
-    namesToIdents.put("arrayClass", "aC");
     namesToIdents.put("queryId", "qI");
 
     List<JField> fields = new ArrayList<JField>(program.getTypeJavaLangObject().getFields());
@@ -2293,6 +2386,11 @@
         specialObfuscatedFields.put(field, ident);
       }
     }
+
+    // force java.lang.Object,java.lang.String
+    // to have seed ids 1,2
+    getSeedId(program.getTypeJavaLangObject());
+    getSeedId(program.getTypeJavaLangString());
   }
 
   String getNameString(HasName hasName) {
@@ -2300,6 +2398,20 @@
     return s;
   }
 
+  /**
+   * Looks up or assigns a seed id for a type..
+   */
+  int getSeedId(JReferenceType type) {
+    Integer val = seedIdMap.get(type);
+    int seedId = val == null ? 0 : val;
+
+    if (seedId == 0) {
+      seedId = nextSeedId++;
+      seedIdMap.put(type, seedId);
+    }
+    return seedId;
+  }
+
   String mangleName(JField x) {
     String s = getNameString(x.getEnclosingType()) + '_' + getNameString(x);
     return s;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
index ae2e645..831aa25 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
@@ -2158,15 +2158,13 @@
       assert ("getClass".equals(method.getName()));
       SourceInfo info = method.getSourceInfo();
       if ("com.google.gwt.lang.Array".equals(type.getName())) {
-        // Special implementation: return this.arrayClass
-        JField arrayClassField = null;
-        for (JField field : type.getFields()) {
-          if ("arrayClass".equals(field.getName())) {
-            arrayClassField = field;
-          }
-        }
-        assert arrayClassField != null;
-        implementMethod(method, new JFieldRef(info, makeThisRef(info), arrayClassField, type));
+        /*
+         * Don't implement, fall through to Object.getClass(). Array emulation code
+         * in com.google.gwt.lang.Array invokes Array.getClass() and expects to get the
+         * class literal for the actual runtime type of the array (e.g. Foo[].class) and
+         * not Array.class.
+         */
+        type.getMethods().remove(2);
       } else {
         implementMethod(method, new JClassLiteral(info, type));
       }
@@ -2767,8 +2765,7 @@
    * 
    * TODO(zundel): something much more awesome?
    */
-  private static final long AST_VERSION = 2;
-
+  private static final long AST_VERSION = 3;
   private static final char[] _STRING = "_String".toCharArray();
   private static final String ARRAY_LENGTH_FIELD = "length";
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementClassLiteralsAsFields.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementClassLiteralsAsFields.java
index f525c3a..d13c0fc 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementClassLiteralsAsFields.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementClassLiteralsAsFields.java
@@ -23,7 +23,6 @@
 import com.google.gwt.dev.jjs.ast.JClassLiteral;
 import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
-import com.google.gwt.dev.jjs.ast.JDeclaredType;
 import com.google.gwt.dev.jjs.ast.JEnumType;
 import com.google.gwt.dev.jjs.ast.JField;
 import com.google.gwt.dev.jjs.ast.JField.Disposition;
@@ -34,10 +33,10 @@
 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.JNameOf;
 import com.google.gwt.dev.jjs.ast.JNullLiteral;
 import com.google.gwt.dev.jjs.ast.JPrimitiveType;
 import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JSeedIdOf;
 import com.google.gwt.dev.jjs.ast.JStringLiteral;
 import com.google.gwt.dev.jjs.ast.JType;
 import com.google.gwt.dev.jjs.ast.js.JsniMethodRef;
@@ -58,15 +57,20 @@
  * Ordinarily, accessing one of these fields would trigger a clinit to run, but
  * we've special-cased class literal fields to evaluate as top-level code before
  * the application starts running to avoid the clinit.
+ *
+ * Class literal factory methods are responsible for installing references
+ * to themselves on the Object.clazz field of their JS runtime prototype
+ * since getClass() is no longer an overridden method.  Prototypes can be
+ * looked up via 'seedId' from the global seedTable object, and so each
+ * class literal factory method is passed the seedId of its type.
  * <p>
- * TODO(cromwellian): consider lazy-initialization to improve startup time.
  */
 public class ImplementClassLiteralsAsFields {
 
   private class NormalizeVisitor extends JModVisitor {
     @Override
     public void endVisit(JClassLiteral x, Context ctx) {
-      JField field = resolveClassLiteralField(x);
+      JField field = resolveClassLiteralField(x.getRefType());
       x.setField(field);
     }
   }
@@ -120,8 +124,8 @@
    * Class:
    * 
    * <pre>
-   * Class.createForClass("java.lang.", "Object", /JNameOf/"java.lang.Object", null)
-   * Class.createForClass("java.lang.", "Exception", /JNameOf/"java.lang.Exception", Throwable.class)
+   * Class.createForClass("java.lang.", "Object", /JSeedIdOf/"java.lang.Object", null)
+   * Class.createForClass("java.lang.", "Exception", /JSeedIdOf/"java.lang.Exception", Throwable.class)
    * </pre>
    * 
    * Interface:
@@ -139,21 +143,21 @@
    * Array:
    * 
    * <pre>
-   * Class.createForArray("", "[I", /JNameOf/"com.google.gwt.lang.Array", int.class)
-   * Class.createForArray("[Lcom.example.", "Foo;", /JNameOf/"com.google.gwt.lang.Array", Foo.class)
+   * Class.createForArray("", "[I", /JSeedIdOf/"com.google.gwt.lang.Array", int.class)
+   * Class.createForArray("[Lcom.example.", "Foo;", /JSeedIdOf/"com.google.gwt.lang.Array", Foo.class)
    * </pre>
    * 
    * Enum:
    * 
    * <pre>
-   * Class.createForEnum("com.example.", "MyEnum", /JNameOf/"com.example.MyEnum", Enum.class,
+   * Class.createForEnum("com.example.", "MyEnum", /JSeedIdOf/"com.example.MyEnum", Enum.class,
    *     public static MyEnum[] values(), public static MyEnum valueOf(String name))
    * </pre>
    * 
    * Enum subclass:
    * 
    * <pre>
-   * Class.createForEnum("com.example.", "MyEnum$1", /JNameOf/"com.example.MyEnum$1", MyEnum.class,
+   * Class.createForEnum("com.example.", "MyEnum$1", /JSeedIdOf/"com.example.MyEnum$1", MyEnum.class,
    *     null, null))
    * </pre>
    */
@@ -182,15 +186,9 @@
     JStringLiteral className = program.getLiteralString(info, getClassName(typeName));
     call.addArgs(packageName, className);
 
-    if (type instanceof JArrayType) {
-      // There's only one seed function for all arrays
-      JDeclaredType arrayType = program.getIndexedType("Array");
-      call.addArg(new JNameOf(info, program.getTypeJavaLangString(), arrayType));
-
-    } else if (type instanceof JClassType) {
+    if (type instanceof JArrayType || type instanceof JClassType) {
       // Add the name of the seed function for concrete types
-      call.addArg(new JNameOf(info, program.getTypeJavaLangString(), type));
-
+      call.addArg(new JSeedIdOf(info, program.getTypeJavaLangString(), type));
     } else if (type instanceof JPrimitiveType) {
       // And give primitive types an illegal, though meaningful, value
       call.addArg(program.getLiteralString(info, " " + type.getJavahSignatureName()));
@@ -256,7 +254,7 @@
 
   private JClassLiteral createDependentClassLiteral(SourceInfo info, JType type) {
     JClassLiteral classLiteral = new JClassLiteral(info.makeChild(), type);
-    JField field = resolveClassLiteralField(classLiteral);
+    JField field = resolveClassLiteralField(classLiteral.getRefType());
     classLiteral.setField(field);
     return classLiteral;
   }
@@ -264,6 +262,7 @@
   private void execImpl() {
     NormalizeVisitor visitor = new NormalizeVisitor();
     visitor.accept(program);
+    program.recordClassLiteralFields(classLiteralFields);
   }
 
   private String getTypeName(JType type) {
@@ -309,8 +308,7 @@
    * }
    * </pre>
    */
-  private JField resolveClassLiteralField(JClassLiteral classLiteral) {
-    JType type = classLiteral.getRefType();
+  private JField resolveClassLiteralField(JType type) {
     type = normalizeJsoType(type);
     JField field = classLiteralFields.get(type);
     if (field == null) {
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 4375220..136f558 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
@@ -42,6 +42,7 @@
 import com.google.gwt.dev.jjs.ast.JPrimitiveType;
 import com.google.gwt.dev.jjs.ast.JProgram;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
+import com.google.gwt.dev.jjs.ast.JSeedIdOf;
 import com.google.gwt.dev.jjs.ast.JType;
 import com.google.gwt.dev.jjs.ast.JVariable;
 import com.google.gwt.dev.jjs.ast.JVariableRef;
@@ -205,6 +206,13 @@
     }
 
     @Override
+    public void endVisit(JSeedIdOf x, Context ctx) {
+      if (x.getNode() instanceof JClassType) {
+        endVisit((JNameOf) x, ctx);  
+      }
+    }
+
+    @Override
     public void endVisit(JsniFieldRef x, Context ctx) {
       if (isPruned(x.getField())) {
         String ident = x.getIdent();
@@ -599,13 +607,17 @@
 
     ControlFlowAnalyzer livenessAnalyzer = new ControlFlowAnalyzer(program);
     livenessAnalyzer.setForPruning();
+
+    // SPECIAL: Immortal codegen types are never pruned
+    traverseTypes(livenessAnalyzer, program.immortalCodeGenTypes);
+
     if (saveCodeGenTypes) {
       /*
        * SPECIAL: Some classes contain methods used by code generation later.
        * Unless those transforms have already been performed, we must rescue all
        * contained methods for later user.
        */
-      traverseFromCodeGenTypes(livenessAnalyzer);
+      traverseTypes(livenessAnalyzer, program.codeGenTypes);
     }
     livenessAnalyzer.traverseEverything();
 
@@ -627,11 +639,11 @@
   }
 
   /**
-   * Traverse from all methods in the program's code-gen types. See
-   * {@link JProgram#CODEGEN_TYPES_SET}.
+   * Traverse from all methods starting from a set of types.
    */
-  private void traverseFromCodeGenTypes(ControlFlowAnalyzer livenessAnalyzer) {
-    for (JClassType type : program.codeGenTypes) {
+  private void traverseTypes(ControlFlowAnalyzer livenessAnalyzer,
+      List<JClassType> types) {
+    for (JClassType type : types) {
       livenessAnalyzer.traverseFromReferenceTo(type);
       for (JMethod method : type.getMethods()) {
         if (method instanceof JConstructor) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceGetClassOverrides.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceGetClassOverrides.java
new file mode 100644
index 0000000..18152d0
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ReplaceGetClassOverrides.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2011 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.JField;
+import com.google.gwt.dev.jjs.ast.JFieldRef;
+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.JProgram;
+
+/**
+ * Prune all overrides of Object.getClass() except when the enclosing class is JavaScriptObject
+ * (to permit getClass() devirtualization in JsoDevirtualizer to continue to work).
+ * Also Inline all method calls to Object.getClass() as Object.clazz.
+ */
+public class ReplaceGetClassOverrides {
+  public static void exec(JProgram program) {
+    new GetClassInlinerRemover(program).accept(program);
+  }
+
+  private static class GetClassInlinerRemover extends JModVisitor {
+
+    private JProgram program;
+    private JMethod getClassMethod;
+    private JField clazzField;
+
+    public GetClassInlinerRemover(JProgram program) {
+      this.program = program;
+      getClassMethod = program.getIndexedMethod("Object.getClass");
+      clazzField = program.getIndexedField("Object.___clazz");
+    }
+
+    public void endVisit(JMethod x, Context ctx) {
+      // don't prune JSO.getClass()
+      if (x.getEnclosingType() == program.getJavaScriptObject()) {
+        return;
+      }
+      if (x.getOverrides().contains(getClassMethod)) {
+        ctx.removeMe();
+      }
+    }
+
+    public void endVisit(JMethodCall x, Context ctx) {
+      // don't inline JSO.getClass()
+      if (x.getTarget().getEnclosingType() == program.getJavaScriptObject()) {
+        return;
+      }
+      // replace overridden getClass() with reference to Object.clazz field
+      if (x.getTarget() == getClassMethod ||
+          x.getTarget().getOverrides().contains(getClassMethod)) {
+        ctx.replaceMe(new JFieldRef(x.getSourceInfo(), x.getInstance(),
+            clazzField, clazzField.getEnclosingType()));
+      }
+    }
+  }
+}
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 829886f..691752a 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
@@ -76,6 +76,7 @@
 import com.google.gwt.dev.jjs.ast.JReboundEntryPoint;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
 import com.google.gwt.dev.jjs.ast.JReturnStatement;
+import com.google.gwt.dev.jjs.ast.JSeedIdOf;
 import com.google.gwt.dev.jjs.ast.JStatement;
 import com.google.gwt.dev.jjs.ast.JStringLiteral;
 import com.google.gwt.dev.jjs.ast.JSwitchStatement;
@@ -135,6 +136,7 @@
   protected static final char[] CHARS_PROTECTED = "protected ".toCharArray();
   protected static final char[] CHARS_PUBLIC = "public ".toCharArray();
   protected static final char[] CHARS_RETURN = "return".toCharArray();
+  protected static final char[] CHARS_SEEDIDOF = " JSeedIdOf ".toCharArray();
   protected static final char[] CHARS_SLASHSTAR = "/*".toCharArray();
   protected static final char[] CHARS_STARSLASH = "*/".toCharArray();
   protected static final char[] CHARS_STATIC = "static ".toCharArray();
@@ -677,7 +679,7 @@
   @Override
   public boolean visit(JNameOf x, Context ctx) {
     print(CHARS_SLASHSTAR);
-    print(CHARS_NAMEOF);
+    print(x instanceof JSeedIdOf ? CHARS_SEEDIDOF : CHARS_NAMEOF);
     print(CHARS_STARSLASH);
     printStringLiteral(x.getNode().getName());
     return false;
diff --git a/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java b/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java
index 2a430a5..f123b0f 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java
@@ -26,6 +26,7 @@
 import com.google.gwt.dev.js.ast.JsName;
 import com.google.gwt.dev.js.ast.JsProgram;
 import com.google.gwt.dev.js.ast.JsProgramFragment;
+import com.google.gwt.dev.js.ast.JsSeedIdOf;
 import com.google.gwt.dev.js.ast.JsStatement;
 import com.google.gwt.dev.js.ast.JsVisitable;
 import com.google.gwt.dev.js.ast.JsVars.JsVar;
@@ -82,6 +83,12 @@
   }
 
   @Override
+  public boolean visit(JsSeedIdOf x, JsContext ctx) {
+    out.print(String.valueOf(x.getSeedId()));
+    return false;
+  }
+
+  @Override
   protected <T extends JsVisitable> T doAccept(T node) {
     JsName newName = nameToBillTo(node);
     if (newName == null) {
diff --git a/dev/core/src/com/google/gwt/dev/js/JsStackEmulator.java b/dev/core/src/com/google/gwt/dev/js/JsStackEmulator.java
index 82a6379..5df8898 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsStackEmulator.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsStackEmulator.java
@@ -22,6 +22,9 @@
 import com.google.gwt.dev.jjs.HasSourceInfo;
 import com.google.gwt.dev.jjs.InternalCompilerException;
 import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.impl.JavaToJavaScriptMap;
 import com.google.gwt.dev.js.ast.JsArrayAccess;
 import com.google.gwt.dev.js.ast.JsArrayLiteral;
 import com.google.gwt.dev.js.ast.JsBinaryOperation;
@@ -559,12 +562,22 @@
   }
 
   /**
-   * Creates a visitor to instrument each JsFunction in the program.
+   * Creates a visitor to instrument each JsFunction in the jsProgram.
    */
   private class InstrumentAllFunctions extends JsVisitor {
+
     @Override
     public void endVisit(JsFunction x, JsContext ctx) {
       if (!x.getBody().getStatements().isEmpty()) {
+        JsName fnName = x.getName();
+        JMethod method = jjsmap.nameToMethod(fnName);
+        /**
+         * Do not instrumental immortal types because they are potentially
+         * evaluated before anything else has been defined.
+         */
+        if (method != null && jprogram.immortalCodeGenTypes.contains(method.getEnclosingType())) {
+          return;
+        }
         if (recordLineNumbers) {
           (new LocationVisitor(x)).accept(x.getBody());
         } else {
@@ -802,9 +815,11 @@
     STRIP, NATIVE, EMULATED;
   }
 
-  public static void exec(JsProgram program, PropertyOracle[] propertyOracles) {
+  public static void exec(JProgram jprogram, JsProgram jsProgram,
+      PropertyOracle[] propertyOracles,
+      JavaToJavaScriptMap jjsmap) {
     if (getStackMode(propertyOracles) == StackMode.EMULATED) {
-      (new JsStackEmulator(program, propertyOracles)).execImpl();
+      (new JsStackEmulator(jprogram, jsProgram, propertyOracles, jjsmap)).execImpl();
     }
   }
 
@@ -839,14 +854,20 @@
 
   private JsFunction caughtFunction;
   private JsName lineNumbers;
-  private final JsProgram program;
+  private JProgram jprogram;
+  private final JsProgram jsProgram;
+  private JavaToJavaScriptMap jjsmap;
   private boolean recordFileNames;
   private boolean recordLineNumbers;
   private JsName stack;
   private JsName stackDepth;
 
-  private JsStackEmulator(JsProgram program, PropertyOracle[] propertyOracles) {
-    this.program = program;
+  private JsStackEmulator(JProgram jprogram, JsProgram jsProgram,
+      PropertyOracle[] propertyOracles,
+      JavaToJavaScriptMap jjsmap) {
+    this.jprogram = jprogram;
+    this.jsProgram = jsProgram;
+    this.jjsmap = jjsmap;
 
     assert propertyOracles.length > 0;
     PropertyOracle oracle = propertyOracles[0];
@@ -865,27 +886,27 @@
   }
 
   private void execImpl() {
-    caughtFunction = program.getIndexedFunction("Exceptions.caught");
+    caughtFunction = jsProgram.getIndexedFunction("Exceptions.caught");
     if (caughtFunction == null) {
       // No exceptions caught? Weird, but possible.
       return;
     }
     initNames();
     makeVars();
-    (new ReplaceUnobfuscatableNames()).accept(program);
-    (new InstrumentAllFunctions()).accept(program);
+    (new ReplaceUnobfuscatableNames()).accept(jsProgram);
+    (new InstrumentAllFunctions()).accept(jsProgram);
   }
 
   private void initNames() {
-    stack = program.getScope().declareName("$JsStackEmulator_stack", "$stack");
-    stackDepth = program.getScope().declareName("$JsStackEmulator_stackDepth",
+    stack = jsProgram.getScope().declareName("$JsStackEmulator_stack", "$stack");
+    stackDepth = jsProgram.getScope().declareName("$JsStackEmulator_stackDepth",
         "$stackDepth");
-    lineNumbers = program.getScope().declareName("$JsStackEmulator_location",
+    lineNumbers = jsProgram.getScope().declareName("$JsStackEmulator_location",
         "$location");
   }
 
   private void makeVars() {
-    SourceInfo info = program.createSourceInfoSynthetic(getClass());
+    SourceInfo info = jsProgram.createSourceInfoSynthetic(getClass());
     JsVar stackVar = new JsVar(info, stack);
     stackVar.setInitExpr(new JsArrayLiteral(info));
     JsVar stackDepthVar = new JsVar(info, stackDepth);
@@ -894,12 +915,12 @@
     lineNumbersVar.setInitExpr(new JsArrayLiteral(info));
 
     JsVars vars;
-    JsStatement first = program.getGlobalBlock().getStatements().get(0);
+    JsStatement first = jsProgram.getGlobalBlock().getStatements().get(0);
     if (first instanceof JsVars) {
       vars = (JsVars) first;
     } else {
       vars = new JsVars(info);
-      program.getGlobalBlock().getStatements().add(0, vars);
+      jsProgram.getGlobalBlock().getStatements().add(0, vars);
     }
     vars.add(stackVar);
     vars.add(stackDepthVar);
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsSeedIdOf.java b/dev/core/src/com/google/gwt/dev/js/ast/JsSeedIdOf.java
new file mode 100644
index 0000000..e7c1778
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsSeedIdOf.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2011 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.js.ast;
+
+import com.google.gwt.dev.jjs.SourceInfo;
+
+/**
+ * An AST node whose evaluation results in the numeric seed id of its type.
+ */
+public class JsSeedIdOf extends JsNameOf {
+
+  private final int seedId;
+
+  public JsSeedIdOf(SourceInfo info, JsName name, int seedId) {
+    super(info, name);
+    this.seedId = seedId;
+  }
+
+  public int getSeedId() {
+    return seedId;
+  }
+
+  public void traverse(JsVisitor visitor, JsContext ctx) {
+    if (visitor.visit(this, ctx)) {
+    }
+    visitor.endVisit(this, ctx);
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java b/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java
index e64799f..142e462 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsVisitor.java
@@ -219,6 +219,10 @@
   public void endVisit(JsReturn x, JsContext ctx) {
   }
 
+  public void endVisit(JsSeedIdOf x, JsContext ctx) {
+    endVisit((JsNameOf) x, ctx);
+  }
+
   public void endVisit(JsStringLiteral x, JsContext ctx) {
   }
 
@@ -383,6 +387,10 @@
     return true;
   }
 
+  public boolean visit(JsSeedIdOf x, JsContext ctx) {
+    return visit((JsNameOf) x, ctx);
+  }
+
   public boolean visit(JsStringLiteral x, JsContext ctx) {
     return true;
   }
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 60c809f..3c36c55 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
@@ -173,7 +173,7 @@
   public static Array initValues(Class<?> arrayClass,
       JavaScriptObject castableTypeMap, int queryId, Array array) {
     ExpandoWrapper.wrapArray(array);
-    array.arrayClass = arrayClass;
+    setClass(array, arrayClass);
     Util.setCastableTypeMap(array, castableTypeMap);
     array.queryId = queryId;
     return array;
@@ -302,17 +302,15 @@
     return array[index] = value;
   }-*/;
 
+  // violator pattern so that the field remains private
+  private static native void setClass(Object o, Class<?> clazz) /*-{
+    o.@java.lang.Object::___clazz = clazz;
+  }-*/;
+
   /*
    * Explicitly initialize all fields to JS false values; see comment in
    * ExpandoWrapper.initExpandos().
    */
-  
-  /**
-   * Holds the real type-specific Class object for a given array instance. The
-   * compiler produces a magic implementation of getClass() which returns this
-   * field directly.
-   */
-  protected Class<?> arrayClass = null;
 
   /**
    * A representation of the necessary cast target for objects stored into this
diff --git a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/SeedUtil.java b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/SeedUtil.java
new file mode 100644
index 0000000..bd80104
--- /dev/null
+++ b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/SeedUtil.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2011 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.lang;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+/**
+ * Utility class for fetching prototype-seed functions for injection into JsAST.
+ */
+public class SeedUtil {
+
+  /*
+  * Holds a map of seedId to anonymous Javascript functions (prototypes for class vtables).
+  */
+  private static JavaScriptObject seedTable = JavaScriptObject.createObject();
+
+  /**
+   * If not already created, generates an anonymous function and assigns it a slot in the global
+   * seedTable. If superSeed is > -1, it creates an instance of the superSeed by invoking
+   * newSeed() and then assigns it as the prototype of the seed being defined. It also sets up the
+   * castableTypeMap, as well as any ctors which are passed in via Javascript varargs. Finally, if
+   * the class literal for this seed id was setup first, which can happen if they are in separate
+   * code-split fragments, the Class.createFor* methods will have created a placeholder seedTable
+   * entry containing the Class literal, and this will be copied from the placeholder location
+   * onto the current prototype.
+   */
+  public static native JavaScriptObject defineSeed(int id, int superSeed,
+      JavaScriptObject castableTypeMap) /*-{
+    var seed = @com.google.gwt.lang.SeedUtil::seedTable[id];
+    if (seed && !seed.@java.lang.Object::___clazz) {
+      // not a placeholder entry setup by Class.setClassLiteral
+      _ = seed.prototype;
+    } else {
+      if (!seed) {
+        seed = @com.google.gwt.lang.SeedUtil::seedTable[id] = function() {
+        };
+      }
+      _ = seed.prototype = (superSeed < 0) ? {}
+          : @com.google.gwt.lang.SeedUtil::newSeed(I)(superSeed);
+      _.@java.lang.Object::castableTypeMap = castableTypeMap;
+    }
+    for (var i = 3; i < arguments.length; ++i) {
+      arguments[i].prototype = _;
+    }
+    if (seed.@java.lang.Object::___clazz) {
+      _.@java.lang.Object::___clazz = seed.@java.lang.Object::___clazz;
+      seed.@java.lang.Object::___clazz = null;
+    }
+  }-*/;
+
+  /**
+   * Lookup seed function by id and instantiate an object with it.
+   */
+  public static native JavaScriptObject newSeed(int id) /*-{
+    return new (@com.google.gwt.lang.SeedUtil::seedTable[id]);
+  }-*/;
+}
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/CodeSplitterTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/CodeSplitterTest.java
index 187b660..d9cdc10 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/CodeSplitterTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/CodeSplitterTest.java
@@ -31,7 +31,5 @@
     ControlFlowAnalyzer cfa = CodeSplitter.computeInitiallyLive(program);
 
     assertTrue(cfa.getInstantiatedTypes().contains(findType(program, "com.google.gwt.lang.Array")));
-    assertTrue(cfa.getLiveFieldsAndMethods().contains(
-        findMethod(findType(program, "com.google.gwt.lang.Array"), "getClass")));
   }
 }
diff --git a/user/src/com/google/gwt/rpc/linker/ClientOracleLinker.java b/user/src/com/google/gwt/rpc/linker/ClientOracleLinker.java
index eb3c555..9bd5e36 100644
--- a/user/src/com/google/gwt/rpc/linker/ClientOracleLinker.java
+++ b/user/src/com/google/gwt/rpc/linker/ClientOracleLinker.java
@@ -77,7 +77,8 @@
           builder.add(symbolData.getSymbolName(), symbolData.getJsniIdent(),
               symbolData.getClassName(), symbolData.getMemberName(),
               symbolData.getQueryId(), 
-              new CastableTypeDataImpl(castableTypeMapString));
+              new CastableTypeDataImpl(castableTypeMapString),
+              symbolData.getSeedId());
         }
 
         ByteArrayOutputStream out = new ByteArrayOutputStream();
diff --git a/user/src/com/google/gwt/rpc/server/WebModeClientOracle.java b/user/src/com/google/gwt/rpc/server/WebModeClientOracle.java
index d91c0e3..4c8976b 100644
--- a/user/src/com/google/gwt/rpc/server/WebModeClientOracle.java
+++ b/user/src/com/google/gwt/rpc/server/WebModeClientOracle.java
@@ -52,7 +52,8 @@
     private WebModeClientOracle oracle = new WebModeClientOracle();
 
     public void add(String jsIdent, String jsniIdent, String className,
-        String memberName, int queryId, CastableTypeData castableTypeData) {
+        String memberName, int queryId, CastableTypeData castableTypeData,
+        int seedId) {
       
       oracle.idents.add(jsIdent);
       ClassData data = oracle.getClassData(className);
@@ -73,6 +74,9 @@
         data.typeName = className;
         data.seedName = jsIdent;
         oracle.seedNamesToClassData.put(jsIdent, data);
+        // Class.getName() with metadata disabled is "Class$S<seedId>"
+        oracle.seedIdsToClassData.put("S" + seedId, data);
+        data.seedId = seedId;
       } else {
         if (jsniIdent.contains("(")) {
           jsniIdent = jsniIdent.substring(jsniIdent.indexOf("::") + 2,
@@ -131,6 +135,7 @@
     public String seedName;
     public List<String> serializableFields = Collections.emptyList();
     public String typeName;
+    public int seedId;
   }
 
   /**
@@ -139,7 +144,7 @@
    * TODO: Use something other than Java serialization to store this type's
    * data.
    */
-  private static final long serialVersionUID = 1L;
+  private static final long serialVersionUID = 2L;
 
   /**
    * Recreate a WebModeClientOracle based on the contents previously emitted by
@@ -222,6 +227,7 @@
   private final Set<String> idents = new HashSet<String>();
 
   private final Map<String, ClassData> seedNamesToClassData = new HashMap<String, ClassData>();
+  private final Map<String, ClassData> seedIdsToClassData = new HashMap<String, ClassData>();
 
   private transient Map<Class<?>, Field[]> operableFieldMap = new IdentityHashMap<Class<?>, Field[]>();
 
@@ -381,6 +387,9 @@
       seedName = seedName.substring(6);
     }
     ClassData data = seedNamesToClassData.get(seedName);
+    if (data == null) {
+      data = seedIdsToClassData.get(seedName);
+    }
     return data == null ? null : data.typeName;
   }
 
diff --git a/user/src/com/google/gwt/rpc/server/WebModePayloadSink.java b/user/src/com/google/gwt/rpc/server/WebModePayloadSink.java
index b5716fd..7e9f431 100644
--- a/user/src/com/google/gwt/rpc/server/WebModePayloadSink.java
+++ b/user/src/com/google/gwt/rpc/server/WebModePayloadSink.java
@@ -257,12 +257,14 @@
 
       byte[] currentBackRef = begin(x);
       byte[] constructorFunction = constructorFunction(x);
-      String seedName = clientOracle.getSeedName(x.getTargetClass());
 
-      if (seedName == null) {
-        throw new IncompatibleRemoteServiceException(
-            "The client cannot create type " + x.getTargetClass());
-      }
+      String getSeedFunc = clientOracle.getMethodId("java.lang.Class",
+          "getSeedFunction", "Ljava/lang/Class;");
+      String classLitId = clientOracle.getFieldId(
+               "com.google.gwt.lang.ClassLiteralHolder",
+               getJavahSignatureName(x.getTargetClass()) + "_classLit");
+           assert classLitId != null : "No class literal for "
+               + x.getTargetClass().getName();
 
       /*
        * If we need to maintain a backreference to the object, it's established
@@ -270,7 +272,7 @@
        * constructorFunction. This is done in case one of the fields should
        * require a reference to the object that is currently being constructed.
        */
-      // constructorFunctionFoo(x = new Foo, field1, field2)
+      // constructorFunctionFoo(x = new (classLit.getSeedFunction()), field1, field2)
       push(constructorFunction);
       lparen();
       if (hasBackRef) {
@@ -278,7 +280,12 @@
         eq();
       }
       _new();
-      push(seedName);
+      lparen();
+      push(getSeedFunc);
+      lparen();
+      push(classLitId);
+      rparen();
+      rparen();
       for (SetCommand setter : x.getSetters()) {
         comma();
         accept(setter.getValue());
@@ -562,7 +569,7 @@
 
       byte[] ident = getBytes("_0");
 
-      // function foo(_0) {return initValues(classLid, castableTypeData, queryId, _0)}
+      // function foo(_0) {return initValues(classLit, castableTypeData, queryId, _0)}
       function();
       push(functionName);
       lparen();
diff --git a/user/super/com/google/gwt/emul/java/lang/Class.java b/user/super/com/google/gwt/emul/java/lang/Class.java
index d000d2d..8db8ead 100644
--- a/user/super/com/google/gwt/emul/java/lang/Class.java
+++ b/user/super/com/google/gwt/emul/java/lang/Class.java
@@ -30,16 +30,21 @@
   private static final int ARRAY = 0x00000004;
   private static final int ENUM = 0x00000008;
 
+  static native String asString(int number) /*-{
+    // for primitives, the seedId isn't a number, but a string like ' Z'
+    return typeof(number) == 'number' ?  "S" + (number < 0 ? -number : number) : number;
+  }-*/;
+
   /**
    * Create a Class object for an array.
    * 
    * @skip
    */
   static <T> Class<T> createForArray(String packageName, String className,
-      String seedName, Class<?> componentType) {
+      int seedId, Class<?> componentType) {
     // Initialize here to avoid method inliner
     Class<T> clazz = new Class<T>();
-    setName(clazz, packageName, className, seedName);
+    setName(clazz, packageName, className, seedId != 0 ? -seedId : 0);
     clazz.modifiers = ARRAY;
     clazz.superclass = Object.class;
     clazz.componentType = componentType;
@@ -52,10 +57,10 @@
    * @skip
    */
   static <T> Class<T> createForClass(String packageName, String className,
-      String seedName, Class<? super T> superclass) {
+      int seedId, Class<? super T> superclass) {
     // Initialize here to avoid method inliner
     Class<T> clazz = new Class<T>();
-    setName(clazz, packageName, className, seedName);
+    setName(clazz, packageName, className, seedId);
     clazz.superclass = superclass;
     return clazz;
   }
@@ -66,11 +71,11 @@
    * @skip
    */
   static <T> Class<T> createForEnum(String packageName, String className,
-      String seedName, Class<? super T> superclass,
+      int seedId, Class<? super T> superclass,
       JavaScriptObject enumConstantsFunc, JavaScriptObject enumValueOfFunc) {
     // Initialize here to avoid method inliner
     Class<T> clazz = new Class<T>();
-    setName(clazz, packageName, className, seedName);
+    setName(clazz, packageName, className, seedId);
     clazz.modifiers = (enumConstantsFunc != null) ? ENUM : 0;
     clazz.superclass = clazz.enumSuperclass = superclass;
     clazz.enumConstantsFunc = enumConstantsFunc;
@@ -86,7 +91,7 @@
   static <T> Class<T> createForInterface(String packageName, String className) {
     // Initialize here to avoid method inliner
     Class<T> clazz = new Class<T>();
-    setName(clazz, packageName, className, null);
+    setName(clazz, packageName, className, 0);
     clazz.modifiers = INTERFACE;
     return clazz;
   }
@@ -97,21 +102,87 @@
    * @skip
    */
   static Class<?> createForPrimitive(String packageName, String className,
-      String seedName) {
+      int seedId) {
     // Initialize here to avoid method inliner
     Class<?> clazz = new Class<Object>();
-    setName(clazz, packageName, className, seedName);
+    setName(clazz, packageName, className, seedId);
     clazz.modifiers = PRIMITIVE;
     return clazz;
   }
 
+  /**
+    * Used by {@link WebModePayloadSink} to create uninitialized instances.
+    */
+   static native JavaScriptObject getSeedFunction(Class<?> clazz) /*-{
+     var func = @com.google.gwt.lang.SeedUtil::seedTable[clazz.@java.lang.Class::seedId];
+     clazz = null; // HACK: prevent pruning via inlining by using param as lvalue
+     return func;
+   }-*/;
+
   static boolean isClassMetadataEnabled() {
     // This body may be replaced by the compiler
     return true;
   }
 
+  /**
+   * null or 0 implies lack of seed function / non-instantiable type
+   */
+  static native boolean isInstantiable(int seedId) /*-{
+    return typeof (seedId) == 'number' && seedId > 0;
+  }-*/;
+
+  /**
+   * null implies pruned.
+   */
+  static native boolean isInstantiableOrPrimitive(int seedId) /*-{
+    return seedId != null && seedId != 0;
+  }-*/;
+
+  /**
+   * Install class literal into seed.prototype.clazz field such that
+   * Object.getClass() returning this.clazz returns the literal. Also stores
+   * seedId on class literal for looking up prototypes given a literal. This
+   * is used for deRPC at the moment, but may be used to implement
+   * Class.newInstance() in the future.
+   */
+  static native void setClassLiteral(int seedId, Class<?> clazz) /*-{
+    var proto;
+    clazz.@java.lang.Class::seedId = seedId;
+    // String is the exception to the usual vtable setup logic
+    if (seedId == 2) {
+      proto = String.prototype
+    } else {
+      if (seedId > 0) {
+        // Guarantees virtual method won't be pruned by using a JSNI ref
+        // This is required because deRPC needs to call it.
+        var seed = @java.lang.Class::getSeedFunction(Ljava/lang/Class;)(clazz);
+        // A class literal may be referenced prior to an async-loaded vtable setup
+        // For example, class literal lives in inital fragment,
+        // but type is instantiated in another fragment
+        if (seed) {
+          proto = seed.prototype;
+        } else {
+          // Leave a place holder for now to be filled in by __defineSeed__ later
+          seed = @com.google.gwt.lang.SeedUtil::seedTable[seedId] = function(){};
+          seed.@java.lang.Object::___clazz = clazz;
+          return;
+        }
+      } else {
+        return;
+      }
+    }
+    proto.@java.lang.Object::___clazz = clazz;
+  }-*/;
+
+  /**
+   * The seedId parameter can take on the following values:
+   * > 0 =>  type is instantiable class
+   * < 0 => type is instantiable array
+   * null => type is not instantiable
+   * string => type is primitive
+   */
   static void setName(Class<?> clazz, String packageName, String className,
-      String seedName) {
+      int seedId) {
     if (clazz.isClassMetadataEnabled()) {
       clazz.typeName = packageName + className;
     } else {
@@ -121,7 +192,11 @@
        * during application start up, before class Integer has been initialized.
        */
       clazz.typeName = "Class$"
-          + (seedName != null ? seedName : "" + clazz.hashCode());
+          + (isInstantiableOrPrimitive(seedId) ? asString(seedId) : "" + clazz.hashCode());
+    }
+
+    if (isInstantiable(seedId)) {
+      setClassLiteral(seedId, clazz);
     }
   }
 
@@ -140,6 +215,8 @@
 
   private String typeName;
 
+  private int seedId;
+
   /**
    * Not publicly instantiable.
    * 
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 3b8efff..63717d2 100644
--- a/user/super/com/google/gwt/emul/java/lang/Object.java
+++ b/user/super/com/google/gwt/emul/java/lang/Object.java
@@ -26,6 +26,14 @@
 public class Object {
 
   /**
+   * Holds class literal for subtypes of Object.
+   */
+  // BUG: If this field name conflicts with a method param name, JDT will complain
+  // CHECKSTYLE_OFF
+  private transient Class<?> ___clazz;
+  // CHECKSTYLE_ON
+
+  /**
    * Used by {@link com.google.gwt.core.client.impl.WeakMapping} in web mode
    * to store an expando containing a String -> Object mapping.
    * 
@@ -60,14 +68,11 @@
   }
 
   /*
-   * Note: Unlike the real JRE, we don't spec this method as final because the
-   * compiler generates a polymorphic override on every other class which will
-   * return the correct class object.
-   * 
-   * TODO(scottb, compiler magician): declare this final, but have the compiler fix it up.
+   * magic; Actual assignment to this field is done by Class.createFor() methods by injecting it
+   * into the prototype.
    */
   public Class<? extends Object> getClass() {
-    return Object.class;
+    return ___clazz;
   }
 
   public int hashCode() {