This patch substantially reduces the overhead of Java types in the output by minimizing vtable setup and virtual class literal fetches. Improvements are anywhere from 5% to 10% uncompressed obfuscated JS.

The patch includes the following changes:

1) A new Immortal CodeGenType. Immortal CodeGenTypes are CodeGenTypes that cannot even be pruned by the final pruner pass. They exist in order to conveniently define code in Java (as opposed to encoding JS in Java strings) which must be injected into the Javascript AST during
construction. They must consist of only static methods and static fields, and static field initializers are only permitted to be literals and JSO.createObject()/createArray(). In addition, they are guaranteed to be hoisted prior to the first non-Immortal type statement.

2) A new SeedUtil Immortal type which provides utility functions for setting up vtables. It eliminates empty constructor seed functions by using 2 helper functions to setup anonymous closure versions. Something like this before the patch:

function fooSeed() {}
_ = fooSeed.prototype = FooConstructor.prototype = new superType();
_.castableTypeMap = { ... }
_.getClass = function() {
return classLiteral;
}

is replaced with

defineSeed(seedId, superSeedid, { castableTypeMap }, FooConstructor1,
FooConstructor2, ...)

This has two effects. First, it reduces both compressed and uncompressed codesize by a non-trivial amount by eliminating empty globally named seed functions which are only used as prototype placeholders. Secondly, it frees up extra obfuscated identifiers (by using numeric ids to identifer function seeds) in the global scope. Note: the seedId is not necessarily the queryId. Third, prototypes can be
looked up by seedId.

3) Eliminate of All getClass() overrides. Two designs were tried, one which removes the overrides during AST generation and the other which leaves them in, but removes them later in the compilation. The latter turned out to be simpler and produce some side benefits. This works as follows:

First, java.lang.Object is given a new Class<?> clazz field and Object.getClass() returns it.

Second, the ClassLiteralHolder fields, which are evaluated last, install the appropriate Class instance into this field on the appropriate prototype. That is, the Class.createForClass() method for example, creates a new ClassLiteral, looks up the appropriate prototype, and installs it into the Object.clazz field for that type.

Third, a final pass, just prior to generating the Javascript AST, prunes all getClass() overrides. The overrides are left in during the GenerateJavaAST phase, because it is advantageous to allow some of them to be inlined when
method call tightening allows it, and because it minimizes the amount of changes to ControlFlowAnalyzer. CFA did had
to be changed to rescue ClassLiterals for any instantiated type, if the getClass() method is live.

deRPC had to be modified to deal with the new scheme of looking up the prototype/seed function by number instead of name, as well as the new naming scheme when -XdisableClassMetadata is active.

Review at http://gwt-code-reviews.appspot.com/1447821


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10435 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 1363eb8..eb6b85e 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 287f1c1..708261b 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;
@@ -387,7 +389,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.
          */
@@ -404,6 +406,7 @@
             Maps.put(indexedFunctions, x.getEnclosingType().getShortName() + "." + x.getName(),
                 jsFunction);
       }
+
       return true;
     }
 
@@ -498,7 +501,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 ? getSeedId((JClassType) x) : -1);
       assert !symbolTable.containsKey(symbolData);
       symbolTable.put(symbolData, jsName);
     }
@@ -567,6 +571,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 = {};");
@@ -606,12 +611,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.");
@@ -718,6 +727,11 @@
         return;
       }
 
+      if (program.immortalCodeGenTypes.contains(x)) {
+        // Handled in generateImmortalTypes
+        return;
+      }
+
       alreadyRan.add(x);
 
       List<JsFunction> jsFuncs = popList(x.getMethods().size()); // methods
@@ -737,6 +751,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());
@@ -1256,7 +1271,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.
@@ -1294,6 +1309,13 @@
     }
 
     @Override
+    public void endVisit(JSeedIdOf x, Context ctx) {
+      JsName name = names.get(x.getNode());
+      assert name != null : "Missing JsName for " + x.getNode().getName();
+      push(new JsSeedIdOf(x.getSourceInfo(), name));
+    }
+
+    @Override
     public void endVisit(JsCastMap x, Context ctx) {
       super.endVisit(x, ctx);
       JsArrayLiteral arrayLit = (JsArrayLiteral) pop();
@@ -1414,6 +1436,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)) {
@@ -1444,6 +1471,7 @@
       for (int i = 0; i < entryMethods.size(); i++) {
         entryMethodToIndex.put(entryMethods.get(i), i);
       }
+
       return true;
     }
 
@@ -1623,27 +1651,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) {
@@ -1680,8 +1701,6 @@
         // special: setup the identifying typeMarker field
         generateTypeMarker(globalStmts);
       }
-
-      generateCastableTypeMap(x, globalStmts);
     }
 
     private void generateGwtOnLoad(List<JsFunction> entryFuncs, List<JsStatement> globalStmts) {
@@ -1782,6 +1801,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();
@@ -1807,44 +1898,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 {
@@ -1859,6 +1937,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);
       }
     }
 
@@ -2161,6 +2249,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;
 
@@ -2175,6 +2264,11 @@
   private final JProgram program;
 
   /**
+   * Map of class type to allocated seed id.
+   */
+  private final Map<JClassType, Integer> seedIdMap = new HashMap<JClassType, Integer>();
+
+  /**
    * All of the fields in String and Array need special handling for interop.
    */
   private final Map<JField, String> specialObfuscatedFields = new HashMap<JField, String>();
@@ -2256,8 +2350,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());
@@ -2269,6 +2363,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) {
@@ -2276,6 +2375,20 @@
     return s;
   }
 
+  /**
+   * Looks up or assigns a seed id for a type..
+   */
+  int getSeedId(JClassType 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;
@@ -2409,6 +2522,11 @@
         return constructorNameToTypeMap.get(name);
       }
 
+      public int seedIdForType(JClassType type) {
+        Integer seedId = seedIdMap.get(type);
+        return seedId != null ? seedId : -1;
+      }
+
       public JClassType typeForStatement(JsStatement stat) {
         return typeForStatMap.get(stat);
       }
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..7d31cae 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
@@ -34,10 +34,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 +58,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 +125,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 +144,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>
    */
@@ -185,12 +190,10 @@
     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));
-
+      call.addArg(new JSeedIdOf(info, program.getTypeJavaLangString(), arrayType));
     } else if (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 +259,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 +267,7 @@
   private void execImpl() {
     NormalizeVisitor visitor = new NormalizeVisitor();
     visitor.accept(program);
+    program.recordClassLiteralFields(classLiteralFields);
   }
 
   private String getTypeName(JType type) {
@@ -309,8 +313,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/JavaToJavaScriptMap.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaToJavaScriptMap.java
index 89aace3..de9e7fa 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaToJavaScriptMap.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaToJavaScriptMap.java
@@ -55,6 +55,11 @@
   JClassType nameToType(JsName name);
 
   /**
+   * Return the seed id corresponding to the Java type.
+   */
+  int seedIdForType(JClassType type);
+
+  /**
    * If <code>stat</code> is used to set up the definition of some class, return
    * that class. Otherwise, return 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..12b72c2 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
@@ -599,13 +599,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 +631,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..834bce7 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,17 @@
   }
 
   @Override
+  public boolean visit(JsSeedIdOf x, JsContext ctx) {
+    Integer seedId = map.seedIdForType(map.nameToType(x.getName()));
+    if (seedId == null) {
+      seedId = -1;
+    }
+    out.print(Integer.toString(seedId));
+
+    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..51d0ed5
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsSeedIdOf.java
@@ -0,0 +1,34 @@
+/*
+ * 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 {
+
+  public JsSeedIdOf(SourceInfo info, JsName name) {
+    super(info, name);
+  }
+
+  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..602fabb 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,8 @@
         data.typeName = className;
         data.seedName = jsIdent;
         oracle.seedNamesToClassData.put(jsIdent, data);
+        oracle.seedIdsToClassData.put(seedId, data);
+        data.seedId = seedId;
       } else {
         if (jsniIdent.contains("(")) {
           jsniIdent = jsniIdent.substring(jsniIdent.indexOf("::") + 2,
@@ -131,6 +134,7 @@
     public String seedName;
     public List<String> serializableFields = Collections.emptyList();
     public String typeName;
+    public int seedId;
   }
 
   /**
@@ -139,7 +143,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 +226,7 @@
   private final Set<String> idents = new HashSet<String>();
 
   private final Map<String, ClassData> seedNamesToClassData = new HashMap<String, ClassData>();
+  private final Map<Integer, ClassData> seedIdsToClassData = new HashMap<Integer, ClassData>();
 
   private transient Map<Class<?>, Field[]> operableFieldMap = new IdentityHashMap<Class<?>, Field[]>();
 
@@ -381,6 +386,15 @@
       seedName = seedName.substring(6);
     }
     ClassData data = seedNamesToClassData.get(seedName);
+    if (data == null) {
+      int seedId = 0;
+      try {
+        seedId = Integer.parseInt(seedName);
+      } catch (NumberFormatException e) {
+        return null;
+      }
+      data = seedIdsToClassData.get(seedId);
+    }
     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..c41f7d3 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,20 @@
   private static final int ARRAY = 0x00000004;
   private static final int ENUM = 0x00000008;
 
+  static native String asString(int number) /*-{
+    return 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, -1);
     clazz.modifiers = ARRAY;
     clazz.superclass = Object.class;
     clazz.componentType = componentType;
@@ -52,10 +56,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 +70,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 +90,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, -1);
     clazz.modifiers = INTERFACE;
     return clazz;
   }
@@ -97,21 +101,73 @@
    * @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 -1 implies lack of seed function / non-instantiable type
+   */
+  static native boolean isInstantiable(int seedId) /*-{
+    return seedId != null && seedId > -1;
+  }-*/;
+
+  /**
+   * 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 > -1) {
+        // 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;
+  }-*/;
+
   static void setName(Class<?> clazz, String packageName, String className,
-      String seedName) {
+      int seedId) {
     if (clazz.isClassMetadataEnabled()) {
       clazz.typeName = packageName + className;
     } else {
@@ -121,7 +177,10 @@
        * during application start up, before class Integer has been initialized.
        */
       clazz.typeName = "Class$"
-          + (seedName != null ? seedName : "" + clazz.hashCode());
+          + (seedId != -1 ? asString(seedId) : "" + clazz.hashCode());
+    }
+    if (isInstantiable(seedId)) {
+      setClassLiteral(seedId, clazz);
     }
   }
 
@@ -140,6 +199,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() {
diff --git a/user/test/com/google/gwt/emultest/java/util/ArraysTest.java b/user/test/com/google/gwt/emultest/java/util/ArraysTest.java
index 6b17574..8bc3344 100644
--- a/user/test/com/google/gwt/emultest/java/util/ArraysTest.java
+++ b/user/test/com/google/gwt/emultest/java/util/ArraysTest.java
@@ -402,7 +402,7 @@
   /**
    * Test Arrays.binarySearch(Object[], Object, Comparator).
    * 
-   * <pre>
+   * <pre>                                                  -draftCompile
    * Verify the following cases:
    *   empty array
    *   odd numbers of elements