The compiler now generates code that emulates Class objects correctly.  The motivation is in anticipation of support for EnumSet/EnumMap.

Suggested by: mmastrac
Review by: mmendez


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1488 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java
index f2a91a0..25f07a6 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JClassLiteral.java
@@ -15,11 +15,74 @@
  */
 package com.google.gwt.dev.jjs.ast;
 
+import com.google.gwt.dev.jjs.InternalCompilerException;
+
 /**
  * Java class literal expression.
  */
 public class JClassLiteral extends JLiteral {
 
+  // Matches constants in our java.lang.Class
+  private static final int PRIMITIVE = 0x00000001;
+  private static final int INTERFACE = 0x00000002;
+  private static final int ARRAY = 0x00000004;
+  private static final int ENUM = 0x00000008;
+
+  private static String getClassName(String fullName) {
+    int pos = fullName.lastIndexOf(".");
+    return fullName.substring(pos + 1);
+  }
+
+  private static String getPackageName(String fullName) {
+    int pos = fullName.lastIndexOf(".");
+    return fullName.substring(0, pos + 1);
+  }
+
+  private static JIntLiteral getTypeBitsLit(JProgram program, JType type) {
+    int bits;
+    if (type instanceof JArrayType) {
+      bits = ARRAY;
+    } else if (type instanceof JEnumType) {
+      bits = ENUM;
+    } else if (type instanceof JClassType) {
+      bits = 0;
+    } else if (type instanceof JInterfaceType) {
+      bits = INTERFACE;
+    } else if (type instanceof JPrimitiveType) {
+      bits = PRIMITIVE;
+    } else {
+      throw new InternalCompilerException("Unknown kind of type");
+    }
+    return program.getLiteralInt(bits);
+  }
+
+  private static JExpression getTypeNameLit(JProgram program, JType type) {
+    JExpression typeNameLit;
+    String typeName;
+    if (type instanceof JArrayType) {
+      typeName = type.getJsniSignatureName().replace('/', '.');
+    } else {
+      typeName = type.getName();
+    }
+
+    // Split the full class name into package + class so package strings
+    // can be merged.
+    String className = getClassName(typeName);
+    String packageName = getPackageName(typeName);
+    if (packageName.length() > 0) {
+      // use "com.example.foo." + "Foo"
+      typeNameLit = new JBinaryOperation(program, null,
+          program.getTypeJavaLangString(), JBinaryOperator.ADD,
+          program.getLiteralString(packageName),
+          program.getLiteralString(className));
+    } else {
+      // no package name could be split, just use the full name
+      typeNameLit = program.getLiteralString(typeName);
+    }
+    return typeNameLit;
+  }
+
+  private JExpression classObjectAllocation;
   private final JType refType;
 
   /**
@@ -28,6 +91,18 @@
   JClassLiteral(JProgram program, JType type) {
     super(program);
     refType = type;
+
+    JNewInstance classAlloc = new JNewInstance(program, null,
+        program.getTypeJavaLangClass());
+    JMethodCall call = new JMethodCall(program, null, classAlloc,
+        program.getIndexedMethod("Class.Class"));
+
+    call.getArgs().add(getTypeNameLit(program, type));
+    call.getArgs().add(getTypeBitsLit(program, type));
+
+    // TODO: enums
+
+    classObjectAllocation = call;
   }
 
   public JType getRefType() {
@@ -35,11 +110,12 @@
   }
 
   public JType getType() {
-    return program.getTypeJavaLangClass();
+    return classObjectAllocation.getType();
   }
 
   public void traverse(JVisitor visitor, Context ctx) {
     if (visitor.visit(this, ctx)) {
+      classObjectAllocation = visitor.accept(classObjectAllocation);
     }
     visitor.endVisit(this, ctx);
   }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethodCall.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethodCall.java
index dfc89ea..6ae7b88 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JMethodCall.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JMethodCall.java
@@ -33,6 +33,7 @@
   public JMethodCall(JProgram program, SourceInfo info, JExpression instance,
       JMethod method) {
     super(program, info);
+    assert (method != null);
     assert (instance != null || method.isStatic());
     this.instance = instance;
     this.method = method;
@@ -41,7 +42,7 @@
   }
 
   /**
-   * Create a method call whose type is overriden to the specified type,
+   * Create a method call whose type is overridden to the specified type,
    * ignoring the return type of the target method. This constructor is used
    * during normalizing transformations to preserve type semantics when calling
    * externally-defined compiler implementation methods.
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JNewArray.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JNewArray.java
index 7c382fa..f8511ce 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JNewArray.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JNewArray.java
@@ -17,15 +17,15 @@
 
 import com.google.gwt.dev.jjs.SourceInfo;
 
-import java.util.ArrayList;
+import java.util.List;
 
 /**
  * New array expression.
  */
 public class JNewArray extends JExpression implements HasSettableType {
 
-  public ArrayList<JExpression> dims = null;
-  public ArrayList<JExpression> initializers = null;
+  public List<JExpression> dims = null;
+  public List<JExpression> initializers = null;
   private JArrayType arrayType;
 
   public JNewArray(JProgram program, SourceInfo info, JArrayType arrayType) {
@@ -70,10 +70,26 @@
 
       if (dims != null) {
         visitor.accept(dims);
+
+        // Visit all the class literals that will eventually get generated.
+        JArrayType it = arrayType;
+        for (JExpression dim : dims) {
+          if (dim instanceof JAbsentArrayDimension) {
+            break;
+          }
+          visitor.accept(program.getLiteralClass(it));
+          if (it.getElementType() instanceof JArrayType) {
+            it = (JArrayType) it.getElementType();
+          } else {
+            break;
+          }
+        }
       }
 
       if (initializers != null) {
         visitor.accept(initializers);
+        // Visit the class literals that will eventually get generated.
+        visitor.accept(program.getLiteralClass(arrayType));
       }
     }
     visitor.endVisit(this, ctx);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
index c6450a6..3f5134d 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
@@ -117,6 +117,8 @@
 
   private final List<JReferenceType> allTypes = new ArrayList<JReferenceType>();
 
+  private Map<JType, JClassLiteral> classLiterals = new HashMap<JType, JClassLiteral>();
+
   /**
    * Each entry is a HashMap(JType => JArrayType), arranged such that the number
    * of dimensions is that index (plus one) at which the JArrayTypes having that
@@ -447,8 +449,12 @@
   }
 
   public JClassLiteral getLiteralClass(JType type) {
-    // could be interned
-    return new JClassLiteral(this, type);
+    JClassLiteral result = classLiterals.get(type);
+    if (result == null) {
+      result = new JClassLiteral(this, type);
+      classLiterals.put(type, result);
+    }
+    return result;
   }
 
   public JClassSeed getLiteralClassSeed(JClassType type) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsonArray.java b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsonArray.java
index 9f7137c..4bf9d26 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsonArray.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/js/JsonArray.java
@@ -23,13 +23,14 @@
 import com.google.gwt.dev.jjs.ast.JVisitor;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * A JSON-style list of JS expressions.
  */
 public class JsonArray extends JExpression {
 
-  public ArrayList exprs = new ArrayList();
+  public List<JExpression> exprs = new ArrayList<JExpression>();
 
   public JsonArray(JProgram program) {
     super(program, null);
@@ -43,7 +44,7 @@
 
   public boolean hasSideEffects() {
     for (int i = 0, c = exprs.size(); i < c; ++i) {
-      if (((JExpression) exprs.get(i)).hasSideEffects()) {
+      if (exprs.get(i).hasSideEffects()) {
         return true;
       }
     }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java
index 45a9bd0..c540293 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java
@@ -44,7 +44,7 @@
 
   private class ArrayVisitor extends JModVisitor {
 
-    // @Override
+    @Override
     public void endVisit(JBinaryOperation x, Context ctx) {
       if (x.getOp() == JBinaryOperator.ASG && x.getLhs() instanceof JArrayRef) {
         JArrayRef arrayRef = (JArrayRef) x.getLhs();
@@ -74,34 +74,19 @@
       }
     }
 
-    // @Override
+    @Override
     public void endVisit(JNewArray x, Context ctx) {
       JArrayType type = x.getArrayType();
-      JLiteral litTypeName = program.getLiteralString(calcClassName(type));
 
       if (x.initializers != null) {
-        processInitializers(x, ctx, type, litTypeName);
+        processInitializers(x, ctx, type);
       } else if (type.getDims() == 1) {
-        processDim(x, ctx, type, litTypeName);
+        processDim(x, ctx, type);
       } else {
-        processDims(x, ctx, type, litTypeName);
+        processDims(x, ctx, type);
       }
     }
 
-    private char[] calcClassName(JArrayType type) {
-      String leafName = type.getLeafType().getJsniSignatureName();
-      leafName = leafName.replace('/', '.');
-      int leafLength = leafName.length();
-      int nDims = type.getDims();
-      char[] className = new char[leafLength + nDims];
-      for (int i = 0; i < nDims; ++i) {
-        className[i] = '[';
-      }
-
-      leafName.getChars(0, leafLength, className, nDims);
-      return className;
-    }
-
     /**
      * @see com.google.gwt.lang.Array regarding seed types
      */
@@ -119,17 +104,17 @@
       return program.getLiteralInt(0);
     }
 
-    private void processDim(JNewArray x, Context ctx, JArrayType arrayType,
-        JLiteral litTypeName) {
+    private void processDim(JNewArray x, Context ctx, JArrayType arrayType) {
       // override the type of the called method with the array's type
       JMethodCall call = new JMethodCall(program, x.getSourceInfo(), null,
           initDim, arrayType);
+      JLiteral classLit = program.getLiteralClass(arrayType);
       JLiteral typeIdLit = program.getLiteralInt(program.getTypeId(arrayType));
       JLiteral queryIdLit = program.getLiteralInt(tryGetQueryId(arrayType));
       JType leafType = arrayType.getLeafType();
-      JExpression dim = (JExpression) x.dims.get(0);
+      JExpression dim = x.dims.get(0);
 
-      call.getArgs().add(litTypeName);
+      call.getArgs().add(classLit);
       call.getArgs().add(typeIdLit);
       call.getArgs().add(queryIdLit);
       call.getArgs().add(dim);
@@ -137,18 +122,18 @@
       ctx.replaceMe(call);
     }
 
-    private void processDims(JNewArray x, Context ctx, JArrayType arrayType,
-        JLiteral litTypeName) {
+    private void processDims(JNewArray x, Context ctx, JArrayType arrayType) {
       // override the type of the called method with the array's type
       JMethodCall call = new JMethodCall(program, x.getSourceInfo(), null,
           initDims, arrayType);
+      JsonArray classLitList = new JsonArray(program);
       JsonArray typeIdList = new JsonArray(program);
       JsonArray queryIdList = new JsonArray(program);
       JsonArray dimList = new JsonArray(program);
       JType leafType = arrayType.getLeafType();
       int outstandingDims = arrayType.getDims();
       for (int i = 0; i < x.dims.size(); ++i) {
-        JExpression dim = (JExpression) x.dims.get(i);
+        JExpression dim = x.dims.get(i);
         if (dim instanceof JAbsentArrayDimension) {
           break;
         }
@@ -165,10 +150,16 @@
          * 
          */
         JArrayType cur = program.getTypeArray(leafType, outstandingDims--);
+
+        JLiteral classLit = program.getLiteralClass(cur);
+        classLitList.exprs.add(classLit);
+
         JLiteral typeIdLit = program.getLiteralInt(program.getTypeId(cur));
         typeIdList.exprs.add(typeIdLit);
+
         JLiteral queryIdLit = program.getLiteralInt(tryGetQueryId(cur));
         queryIdList.exprs.add(queryIdLit);
+
         dimList.exprs.add(dim);
       }
       JType targetType = leafType;
@@ -176,7 +167,7 @@
         targetType = program.getTypeArray(targetType, outstandingDims);
       }
 
-      call.getArgs().add(litTypeName);
+      call.getArgs().add(classLitList);
       call.getArgs().add(typeIdList);
       call.getArgs().add(queryIdList);
       call.getArgs().add(dimList);
@@ -185,17 +176,18 @@
     }
 
     private void processInitializers(JNewArray x, Context ctx,
-        JArrayType arrayType, JLiteral litTypeName) {
+        JArrayType arrayType) {
       // override the type of the called method with the array's type
       JMethodCall call = new JMethodCall(program, x.getSourceInfo(), null,
           initValues, arrayType);
+      JLiteral classLit = program.getLiteralClass(arrayType);
       JLiteral typeIdLit = program.getLiteralInt(program.getTypeId(arrayType));
       JLiteral queryIdLit = program.getLiteralInt(tryGetQueryId(arrayType));
       JsonArray initList = new JsonArray(program);
       for (int i = 0; i < x.initializers.size(); ++i) {
         initList.exprs.add(x.initializers.get(i));
       }
-      call.getArgs().add(litTypeName);
+      call.getArgs().add(classLit);
       call.getArgs().add(typeIdLit);
       call.getArgs().add(queryIdLit);
       call.getArgs().add(initList);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java b/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java
index 93873ad..24f3df6 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java
@@ -430,6 +430,17 @@
       }
       JReferenceType type = (JReferenceType) typeMap.get(binding);
       try {
+        // Create an override for getClass().
+        if (type instanceof JClassType
+            && type != program.getTypeJavaLangObject()
+            && type != program.getIndexedType("Array")) {
+          JMethod getClassMethod = program.createMethod(null,
+              "getClass".toCharArray(), type, program.getTypeJavaLangClass(),
+              false, false, false, false, false);
+          assert (type.methods.get(2) == getClassMethod);
+          getClassMethod.freezeParamTypes();
+        }
+
         if (binding.isNestedType() && !binding.isStatic()) {
           // add synthetic fields for outer this and locals
           assert (type instanceof JClassType);
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 a6e4313..e56caf1 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
@@ -31,6 +31,7 @@
 import com.google.gwt.dev.jjs.ast.JCaseStatement;
 import com.google.gwt.dev.jjs.ast.JCastOperation;
 import com.google.gwt.dev.jjs.ast.JCharLiteral;
+import com.google.gwt.dev.jjs.ast.JClassLiteral;
 import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JConditional;
 import com.google.gwt.dev.jjs.ast.JContinueStatement;
@@ -47,6 +48,7 @@
 import com.google.gwt.dev.jjs.ast.JIfStatement;
 import com.google.gwt.dev.jjs.ast.JInstanceOf;
 import com.google.gwt.dev.jjs.ast.JIntLiteral;
+import com.google.gwt.dev.jjs.ast.JInterfaceType;
 import com.google.gwt.dev.jjs.ast.JLabel;
 import com.google.gwt.dev.jjs.ast.JLabeledStatement;
 import com.google.gwt.dev.jjs.ast.JLiteral;
@@ -361,6 +363,21 @@
           }
         }
 
+        // Write the body of the getClass() override.
+        if (currentClass instanceof JClassType
+            && currentClass != program.getTypeJavaLangObject()
+            && currentClass != program.getIndexedType("Array")) {
+          JMethod method = currentClass.methods.get(2);
+          assert ("getClass".equals(method.getName()));
+
+          tryFindUpRefs(method);
+
+          JMethodBody body = (JMethodBody) method.getBody();
+          JClassLiteral classLit = program.getLiteralClass(currentClass);
+          body.getStatements().add(
+              new JReturnStatement(program, null, classLit));
+        }
+
         if (x.binding.isEnum()) {
           processEnumType((JEnumType) currentClass);
         }
@@ -2356,6 +2373,18 @@
     }
 
     /**
+     * For a given method, try to find all methods that it overrides/implements.
+     * An experimental version that doesn't use JDT. Right now it's only used to
+     * resolve upRefs for Object.getClass(), which is synthetic on everything
+     * other than object.
+     */
+    private void tryFindUpRefs(JMethod method) {
+      if (method.getEnclosingType() != null) {
+        tryFindUpRefsRecursive(method, method.getEnclosingType());
+      }
+    }
+
+    /**
      * For a given method(and method binding), try to find all methods that it
      * overrides/implements.
      */
@@ -2367,6 +2396,49 @@
      * For a given method(and method binding), recursively try to find all
      * methods that it overrides/implements.
      */
+    private void tryFindUpRefsRecursive(JMethod method,
+        JReferenceType searchThisType) {
+
+      // See if this class has any uprefs, unless this class is myself
+      if (method.getEnclosingType() != searchThisType) {
+        outer : for (JMethod upRef : searchThisType.methods) {
+          if (upRef.isStatic()) {
+            continue;
+          }
+          if (!upRef.getName().equals(method.getName())) {
+            continue;
+          }
+          if (upRef.params.size() != method.params.size()) {
+            continue;
+          }
+          for (int i = 0, c = upRef.params.size(); i < c; ++i) {
+            if (upRef.params.get(i).getType() != method.params.get(i).getType()) {
+              continue outer;
+            }
+          }
+
+          if (!method.overrides.contains(upRef)) {
+            method.overrides.add(upRef);
+            break;
+          }
+        }
+      }
+
+      // recurse super class
+      if (searchThisType.extnds != null) {
+        tryFindUpRefsRecursive(method, searchThisType.extnds);
+      }
+
+      // recurse super interfaces
+      for (JInterfaceType intf : searchThisType.implments) {
+        tryFindUpRefsRecursive(method, intf);
+      }
+    }
+
+    /**
+     * For a given method(and method binding), recursively try to find all
+     * methods that it overrides/implements.
+     */
     private void tryFindUpRefsRecursive(JMethod method, MethodBinding binding,
         ReferenceBinding searchThisType) {
 
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 447af14..8e32999 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
@@ -22,7 +22,6 @@
 import com.google.gwt.dev.jjs.ast.JAbsentArrayDimension;
 import com.google.gwt.dev.jjs.ast.JAbstractMethodBody;
 import com.google.gwt.dev.jjs.ast.JArrayRef;
-import com.google.gwt.dev.jjs.ast.JArrayType;
 import com.google.gwt.dev.jjs.ast.JAssertStatement;
 import com.google.gwt.dev.jjs.ast.JBinaryOperation;
 import com.google.gwt.dev.jjs.ast.JBinaryOperator;
@@ -118,7 +117,6 @@
 import com.google.gwt.dev.js.ast.JsReturn;
 import com.google.gwt.dev.js.ast.JsScope;
 import com.google.gwt.dev.js.ast.JsStatement;
-import com.google.gwt.dev.js.ast.JsStringLiteral;
 import com.google.gwt.dev.js.ast.JsSwitch;
 import com.google.gwt.dev.js.ast.JsSwitchMember;
 import com.google.gwt.dev.js.ast.JsThisRef;
@@ -143,8 +141,6 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.Stack;
-import java.util.TreeMap;
-import java.util.Map.Entry;
 
 /**
  * Creates a JavaScript AST from a <code>JProgram</code> node.
@@ -268,16 +264,6 @@
       }
       classScopes.put(x, myScope);
 
-      // Make a name for my package if it doesn't already exist
-      String packageName = getPackageName(x.getName());
-      if (packageName != null && packageName.length() > 0) {
-        if (packageNames.get(packageName) == null) {
-          String ident = "package_" + packageName.replace('.', '_');
-          JsName jsName = topScope.declareName(ident);
-          packageNames.put(packageName, jsName);
-        }
-      }
-
       push(myScope);
       return true;
     }
@@ -489,10 +475,13 @@
 
     @Override
     public void endVisit(JClassLiteral x, Context ctx) {
+      JsExpression classObjectAllocation = pop(); // classObjectAllocation
+
       // My seed function name
       String nameString = x.getRefType().getJavahSignatureName() + "_classlit";
       JsName classLit = topScope.declareName(nameString);
       classLits.put(x.getRefType(), classLit);
+      classObjects.put(classLit, classObjectAllocation);
       push(classLit.makeRef());
     }
 
@@ -596,6 +585,9 @@
       if (x.constInitializer != null) {
         // setup the constant value
         accept(x.constInitializer);
+      } else if (x == program.getIndexedField("Cast.typeIdArray")) {
+        // magic: setup the type id table
+        push(generateTypeTable());
       } else if (!x.hasInitializer()) {
         // setup a default value
         accept(x.getType().getDefaultValue());
@@ -981,10 +973,17 @@
       }
 
       generateGwtOnLoad(entryFuncs, globalStmts);
+      generateNullFunc(globalStmts);
 
-      // a few more vars on the very end
+      // Add a few things onto the beginning.
+
+      // Reserve the "_" identifier.
       JsVars vars = new JsVars();
-      generateTypeTable(vars);
+      vars.add(new JsVar(globalTemp));
+      globalStmts.add(0, vars);
+
+      // Generate class objects.
+      vars = new JsVars();
       generateClassLiterals(vars);
       if (!vars.isEmpty()) {
         globalStmts.add(vars);
@@ -1147,22 +1146,6 @@
     }
 
     @Override
-    public boolean visit(JProgram x, Context ctx) {
-      List<JsStatement> globalStatements = jsProgram.getGlobalBlock().getStatements();
-
-      // declare some global vars
-      JsVars vars = new JsVars();
-
-      // reserve the "_" identifier
-      vars.add(new JsVar(globalTemp));
-      generatePackageNames(vars);
-      globalStatements.add(vars);
-
-      generateNullFunc(globalStatements);
-      return true;
-    }
-
-    @Override
     public boolean visit(JSwitchStatement x, Context ctx) {
       /*
        * What a pain.. JSwitchStatement and JsSwitch are modeled completely
@@ -1214,26 +1197,12 @@
     }
 
     private void generateClassLiterals(JsVars vars) {
-      /*
-       * Class literals are useless right now, just use String literals and the
-       * Object methods will basically work.
-       */
       for (Object element : classLits.keySet()) {
         JType type = (JType) element;
         JsName jsName = classLits.get(type);
-        String string;
-        if (type instanceof JArrayType) {
-          string = "class " + type.getJsniSignatureName().replace('/', '.');
-        } else if (type instanceof JClassType) {
-          string = "class " + type.getName();
-        } else if (type instanceof JInterfaceType) {
-          string = "interface " + type.getName();
-        } else {
-          string = type.getName();
-        }
-        JsStringLiteral stringLiteral = jsProgram.getStringLiteral(string);
+        JsExpression classObjectAlloc = classObjects.get(jsName);
         JsVar var = new JsVar(jsName);
-        var.setInitExpr(stringLiteral);
+        var.setInitExpr(classObjectAlloc);
         vars.add(var);
       }
     }
@@ -1247,7 +1216,6 @@
         generateToStringAlias(x, globalStmts);
       }
 
-      generateTypeName(x, globalStmts);
       generateTypeId(x, globalStmts);
     }
 
@@ -1325,16 +1293,6 @@
       globalStatements.add(nullFunc.makeStmt());
     }
 
-    private void generatePackageNames(JsVars vars) {
-      for (Entry<String, JsName> entry : packageNames.entrySet()) {
-        String packageName = entry.getKey();
-        JsName name = entry.getValue();
-        JsVar jsVar = new JsVar(name);
-        jsVar.setInitExpr(jsProgram.getStringLiteral(packageName));
-        vars.add(jsVar);
-      }
-    }
-
     private void generateSeedFuncAndPrototype(JClassType x,
         List<JsStatement> globalStmts) {
       if (x != program.getTypeJavaLangString()) {
@@ -1421,50 +1379,14 @@
       }
     }
 
-    private void generateTypeName(JClassType x, List<JsStatement> globalStmts) {
-      JField typeNameField = program.getIndexedField("Object.typeName");
-      JsName typeNameName = names.get(typeNameField);
-      if (typeNameName == null) {
-        // Was pruned; this compilation must not use GWT.getTypeName().
-        return;
-      }
-      JsNameRef lhs = typeNameName.makeRef();
-      lhs.setQualifier(globalTemp.makeRef());
-
-      // Split the full class name into package + class so package strings
-      // can be merged.
-      String className = getClassName(x.getName());
-      String packageName = getPackageName(x.getName());
-      JsExpression rhs;
-      if (packageName.length() > 0) {
-        // use "com.example.foo." + "Foo"
-        JsNameRef packageRef = (packageNames.get(packageName)).makeRef();
-        rhs = new JsBinaryOperation(JsBinaryOperator.ADD, packageRef,
-            jsProgram.getStringLiteral(className));
-      } else {
-        // no package name could be split, just use the full name
-        rhs = jsProgram.getStringLiteral(x.getName());
-      }
-      JsExpression asg = createAssignment(lhs, rhs);
-      globalStmts.add(new JsExprStmt(asg));
-    }
-
-    private void generateTypeTable(JsVars vars) {
-      JField typeIdArray = program.getIndexedField("Cast.typeIdArray");
-      JsName typeIdArrayName = names.get(typeIdArray);
-      if (typeIdArrayName == null) {
-        // Was pruned; this compilation must have no dynamic casts.
-        return;
-      }
+    private JsExpression generateTypeTable() {
       JsArrayLiteral arrayLit = new JsArrayLiteral();
       for (int i = 0; i < program.getJsonTypeTable().size(); ++i) {
         JsonObject jsonObject = program.getJsonTypeTable().get(i);
         accept(jsonObject);
         arrayLit.getExpressions().add((JsExpression) pop());
       }
-      JsVar var = new JsVar(typeIdArrayName);
-      var.setInitExpr(arrayLit);
-      vars.add(var);
+      return arrayLit;
     }
 
     private void generateVTables(JClassType x, List<JsStatement> globalStmts) {
@@ -1534,7 +1456,7 @@
     private <T extends JsVisitable> List<T> popList(int count) {
       List<T> list = new ArrayList<T>();
       while (count > 0) {
-        T item = this.<T>pop();
+        T item = this.<T> pop();
         if (item != null) {
           list.add(item);
         }
@@ -1548,7 +1470,7 @@
         int count) {
       List<T> list = new ArrayList<T>();
       while (count > 0) {
-        T item = this.<T>pop();
+        T item = this.<T> pop();
         if (item != null) {
           list.add(item);
         }
@@ -1658,18 +1580,9 @@
     generateJavaScriptAST.execImpl();
   }
 
-  private static String getClassName(String fullName) {
-    int pos = fullName.lastIndexOf(".");
-    return fullName.substring(pos + 1);
-  }
-
-  private static String getPackageName(String fullName) {
-    int pos = fullName.lastIndexOf(".");
-    return fullName.substring(0, pos + 1);
-  }
-
   private final Map<JBlock, JsCatch> catchMap = new IdentityHashMap<JBlock, JsCatch>();
   private final Map<JType, JsName> classLits = new IdentityHashMap<JType, JsName>();
+  private final Map<JsName, JsExpression> classObjects = new IdentityHashMap<JsName, JsExpression>();
   private final Map<JClassType, JsScope> classScopes = new IdentityHashMap<JClassType, JsScope>();
 
   /**
@@ -1689,7 +1602,6 @@
    * and toString. All other class scopes have this scope as an ultimate parent.
    */
   private final JsScope objectScope;
-  private final Map<String, JsName> packageNames = new TreeMap<String, JsName>();
   private final Map<JMethod, JsName> polymorphicNames = new IdentityHashMap<JMethod, JsName>();
   private final JProgram program;
 
@@ -1739,6 +1651,7 @@
     specialObfuscatedTypes.add(program.getIndexedType("Array"));
 
     // Object polymorphic
+    specialObfuscatedIdents.put("getClass", "gC");
     specialObfuscatedIdents.put("hashCode", "hC");
     specialObfuscatedIdents.put("equals", "eQ");
     specialObfuscatedIdents.put("toString", "tS");
@@ -1755,6 +1668,7 @@
     specialObfuscatedIdents.put("subSequence", "sS");
 
     // Array magic field
+    specialObfuscatedIdents.put("arrayClass", "aC");
     specialObfuscatedIdents.put("queryId", "qI");
   }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectCaster.java b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectCaster.java
index 4ffd2fd..62c87dc 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectCaster.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/JavaScriptObjectCaster.java
@@ -32,7 +32,7 @@
 import com.google.gwt.dev.jjs.ast.JReturnStatement;
 import com.google.gwt.dev.jjs.ast.JType;
 
-import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Synthesize casts for JavaScriptObject types in cases where dynamic type
@@ -47,7 +47,7 @@
 
     private JMethod currentMethod;
 
-    // @Override
+    @Override
     public void endVisit(JBinaryOperation x, Context ctx) {
       if (x.isAssignment()) {
         JType lhsType = x.getLhs().getType();
@@ -67,7 +67,7 @@
       }
     }
 
-    // @Override
+    @Override
     public void endVisit(JConditional x, Context ctx) {
       JExpression newThen = checkAndReplaceJso(x.getThenExpr(), x.getType());
       JExpression newElse = checkAndReplaceJso(x.getElseExpr(), x.getType());
@@ -78,7 +78,7 @@
       }
     }
 
-    // @Override
+    @Override
     public void endVisit(JLocalDeclarationStatement x, Context ctx) {
       JExpression newInst = x.getInitializer();
       if (newInst != null) {
@@ -91,17 +91,17 @@
       }
     }
 
-    // @Override
+    @Override
     public void endVisit(JMethod x, Context ctx) {
       currentMethod = null;
     }
 
-    // @Override
+    @Override
     public void endVisit(JMethodCall x, Context ctx) {
       for (int i = 0; i < x.getTarget().params.size(); ++i) {
-        JParameter param = (JParameter) x.getTarget().params.get(i);
-        JExpression newArg = checkAndReplaceJso(
-            (JExpression) x.getArgs().get(i), param.getType());
+        JParameter param = x.getTarget().params.get(i);
+        JExpression newArg = checkAndReplaceJso(x.getArgs().get(i),
+            param.getType());
         x.getArgs().set(i, newArg);
       }
       if (!x.getTarget().isStatic()) {
@@ -117,13 +117,12 @@
       }
     }
 
-    // @Override
+    @Override
     public void endVisit(JNewArray x, Context ctx) {
-      JType leafType = x.getArrayType().getElementType();
-      ArrayList initializers = x.initializers;
+      List<JExpression> initializers = x.initializers;
       if (initializers != null) {
         for (int i = 0; i < initializers.size(); ++i) {
-          JExpression intializer = (JExpression) initializers.get(i);
+          JExpression intializer = initializers.get(i);
           JExpression newInitializer = checkAndReplaceJsoArrayStore(intializer,
               x.getArrayType().getLeafType());
           if (intializer != newInitializer) {
@@ -134,7 +133,7 @@
       }
     }
 
-    // @Override
+    @Override
     public void endVisit(JReturnStatement x, Context ctx) {
       if (x.getExpr() != null) {
         JExpression newExpr = checkAndReplaceJso(x.getExpr(),
@@ -147,7 +146,7 @@
       }
     }
 
-    // @Override
+    @Override
     public boolean visit(JMethod x, Context ctx) {
       currentMethod = x;
       return true;
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 ad688a5..5832632 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
@@ -21,7 +21,6 @@
 import com.google.gwt.dev.jjs.ast.JArrayType;
 import com.google.gwt.dev.jjs.ast.JBinaryOperation;
 import com.google.gwt.dev.jjs.ast.JBinaryOperator;
-import com.google.gwt.dev.jjs.ast.JClassLiteral;
 import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JExpression;
 import com.google.gwt.dev.jjs.ast.JField;
@@ -323,14 +322,6 @@
     }
 
     @Override
-    public boolean visit(JClassLiteral literal, Context ctx) {
-      // rescue and instantiate java.lang.Class
-      // JLS 12.4.1: do not rescue the target type
-      rescue(program.getTypeJavaLangClass(), true, true);
-      return true;
-    }
-
-    @Override
     public boolean visit(JClassType type, Context ctx) {
       assert (referencedTypes.contains(type));
       boolean isInstantiated = instantiatedTypes.contains(type);
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 2bb0bfc..1581836 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
@@ -43,7 +43,7 @@
   public static <T> T[] cloneSubrange(T[] array, int fromIndex, int toIndex) {
     Array a = asArrayType(array);
     Array result = arraySlice(a, fromIndex, toIndex);
-    initValues(a.typeName, a.typeId, a.queryId, result);
+    initValues(a.getClass(), a.typeId, a.queryId, result);
     return asArray(result);
   }
 
@@ -54,7 +54,7 @@
   public static <T> T[] clonify(T[] array, int length) {
     Array a = asArrayType(array);
     Array result = createFromSeed(NULL_SEED_TYPE, length);
-    initValues(a.typeName, a.typeId, a.queryId, result);
+    initValues(a.getClass(), a.typeId, a.queryId, result);
     return asArray(result);
   }
 
@@ -62,17 +62,17 @@
    * Creates an array like "new T[a][b][c][][]" by passing in a native JSON
    * array, [a, b, c].
    * 
-   * @param typeName the typeName of the array
+   * @param arrayClass the class of the array
    * @param typeId the typeId of the array
    * @param queryId the queryId of the array
    * @param length the length of the array
    * @param seedType the primitive type of the array; 0: null; 1: zero; 2: false
    * @return the new array
    */
-  public static Array initDim(String typeName, int typeId, int queryId,
+  public static Array initDim(Class arrayClass, int typeId, int queryId,
       int length, int seedType) {
     Array result = createFromSeed(seedType, length);
-    initValues(typeName, typeId, queryId, result);
+    initValues(arrayClass, typeId, queryId, result);
     return result;
   }
 
@@ -80,16 +80,16 @@
    * Creates an array like "new T[a][b][c][][]" by passing in a native JSON
    * array, [a, b, c].
    * 
-   * @param typeName the typeName of the array
+   * @param arrayClasses the class of each dimension of the array
    * @param typeIdExprs the typeId at each dimension, from highest to lowest
    * @param queryIdExprs the queryId at each dimension, from highest to lowest
    * @param dimExprs the length at each dimension, from highest to lower
    * @param seedType the primitive type of the array; 0: null; 1: zero; 2: false
    * @return the new array
    */
-  public static Array initDims(String typeName, int[] typeIdExprs,
+  public static Array initDims(Class arrayClasses[], int[] typeIdExprs,
       int[] queryIdExprs, int[] dimExprs, int seedType) {
-    return initDims(typeName, typeIdExprs, queryIdExprs, dimExprs, 0,
+    return initDims(arrayClasses, typeIdExprs, queryIdExprs, dimExprs, 0,
         dimExprs.length, seedType);
   }
 
@@ -97,19 +97,19 @@
    * Creates an array like "new T[][]{a,b,c,d}" by passing in a native JSON
    * array, [a, b, c, d].
    * 
-   * @param typeName the typeName of the array
+   * @param arrayClass the class of the array
    * @param typeId the typeId of the array
    * @param queryId the queryId of the array
    * @param array the JSON array that will be transformed into a GWT array
    * @return values; having wrapped it for GWT
    */
-  public static final Array initValues(String typeName, int typeId,
+  public static final Array initValues(Class arrayClass, int typeId,
       int queryId, Array array) {
     if (protoTypeArray == null) {
       protoTypeArray = new Array();
     }
     wrapArray(array, protoTypeArray);
-    array.typeName = typeName;
+    array.arrayClass = arrayClass;
     array.typeId = typeId;
     array.queryId = queryId;
     return array;
@@ -168,7 +168,7 @@
     return blankArray;
   }-*/;
 
-  private static Array initDims(String typeName, int[] typeIdExprs,
+  private static Array initDims(Class arrayClasses[], int[] typeIdExprs,
       int[] queryIdExprs, int[] dimExprs, int index, int count, int seedType) {
     int length = dimExprs[index];
     if (length < 0) {
@@ -178,14 +178,13 @@
     boolean isLastDim = (index == (count - 1));
 
     Array result = createFromSeed(isLastDim ? seedType : NULL_SEED_TYPE, length);
-    initValues(typeName, typeIdExprs[index], queryIdExprs[index], result);
+    initValues(arrayClasses[index], typeIdExprs[index], queryIdExprs[index], result);
 
     if (!isLastDim) {
       // Recurse to next dimension.
       ++index;
-      typeName = typeName.substring(1);
       for (int i = 0; i < length; ++i) {
-        set(result, i, initDims(typeName, typeIdExprs, queryIdExprs, dimExprs,
+        set(result, i, initDims(arrayClasses, typeIdExprs, queryIdExprs, dimExprs,
             index, count, seedType));
       }
     }
@@ -218,5 +217,11 @@
    * wrapArray(Array, Array).
    */
   public int length = 0;
+  protected Class arrayClass = null;
   protected int queryId = 0;
+
+  @Override
+  public Class getClass() {
+    return arrayClass;
+  }
 }
diff --git a/user/src/com/google/gwt/core/client/GWT.java b/user/src/com/google/gwt/core/client/GWT.java
index 75a1279..a38907c 100644
--- a/user/src/com/google/gwt/core/client/GWT.java
+++ b/user/src/com/google/gwt/core/client/GWT.java
@@ -23,7 +23,7 @@
 public final class GWT {
   /*
    * This is the web mode version of this class. Because it's so special,
-   * there's also a hosted mode version.  See GWT.java-hosted.
+   * there's also a hosted mode version. See GWT.java-hosted.
    */
 
   /**
@@ -107,9 +107,9 @@
    * @return the class name of the specified object, or <code>null</code> if
    *         <code>o</code> is <code>null</code>
    */
-  public static native String getTypeName(Object o) /*-{
-    return (o == null) ? null : o.@java.lang.Object::typeName;
-  }-*/;
+  public static String getTypeName(Object o) {
+    return (o == null) ? null : o.getClass().getName();
+  }
 
   /**
    * Returns the currently active uncaughtExceptionHandler. "Top level" methods
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 dc9b322..1e4f83b 100644
--- a/user/super/com/google/gwt/emul/java/lang/Class.java
+++ b/user/super/com/google/gwt/emul/java/lang/Class.java
@@ -21,18 +21,53 @@
  * 
  * @param <T> the type of the object
  */
-public class Class<T> {
+public final class Class<T> {
+
+  private static final int PRIMITIVE = 0x00000001;
+  private static final int INTERFACE = 0x00000002;
+  private static final int ARRAY = 0x00000004;
+  private static final int ENUM = 0x00000008;
+
+  private final String typeName;
+  private final int modifiers;
 
   /**
-   * Not instantiable.
+   * Not publicly instantiable.
    * 
    * @skip
    */
-  private Class() {
+  Class(String typeName, int modifiers) {
+    this.typeName = typeName;
+    this.modifiers = modifiers;
   }
 
   public T[] getEnumConstants() {
-    throw new UnsupportedOperationException(
-      "Class.getEnumConstants() not yet implemented");
+    // TODO
+    return null;
+  }
+
+  public String getName() {
+    return typeName;
+  }
+
+  public boolean isArray() {
+    return (modifiers & ARRAY) != 0;
+  }
+
+  public boolean isEnum() {
+    return (modifiers & ENUM) != 0;
+  }
+
+  public boolean isInterface() {
+    return (modifiers & INTERFACE) != 0;
+  }
+
+  public boolean isPrimitive() {
+    return (modifiers & PRIMITIVE) != 0;
+  }
+
+  public String toString() {
+    return (isInterface() ? "interface " : (isPrimitive() ? "" : "class "))
+        + getName();
   }
 }
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 0624089..fc9b233 100644
--- a/user/super/com/google/gwt/emul/java/lang/Object.java
+++ b/user/super/com/google/gwt/emul/java/lang/Object.java
@@ -29,23 +29,25 @@
    */
   protected transient int typeId;
 
-  /**
-   * magic magic magic.
-   * 
-   * @skip
-   */
-  protected transient String typeName;
-
   public native boolean equals(Object other) /*-{
     return this === other;
   }-*/;
 
+  /*
+   * Magic; unlike the real JDT, we don't spec this method as final.  The
+   * compiler will generate a polymorphic override on every other class which
+   * will return the correct class object.
+   */
+  public Class<? extends Object> getClass() {
+    return Object.class;
+  }
+
   public int hashCode() {
     return System.identityHashCode(this);
   }
 
   public String toString() {
-    return typeName + "@" + hashCode();
+    return getClass().getName() + "@" + hashCode();
   }
 
   /**
diff --git a/user/super/com/google/gwt/emul/java/lang/Throwable.java b/user/super/com/google/gwt/emul/java/lang/Throwable.java
index 0510635..23649d4 100644
--- a/user/super/com/google/gwt/emul/java/lang/Throwable.java
+++ b/user/super/com/google/gwt/emul/java/lang/Throwable.java
@@ -104,7 +104,7 @@
       if (currentCause != this) {
         msg.append("Caused by: ");
       }
-      msg.append(currentCause.typeName);
+      msg.append(currentCause.getClass().getName());
       msg.append(": ");
       msg.append(causeMessage == null ? "(No exception detail)" : causeMessage);
       msg.append("\n");