Double and Boolean implemented as unboxed raw types.

Change-Id: I3a11e91be6ceb0d72b727a43c28581ebef6d70e3
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 e250f9d..b507c64 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -113,6 +113,7 @@
 import com.google.gwt.dev.jjs.impl.ResolveRuntimeTypeReferences.StringTypeMapper;
 import com.google.gwt.dev.jjs.impl.ResolveRuntimeTypeReferences.TypeMapper;
 import com.google.gwt.dev.jjs.impl.ResolveRuntimeTypeReferences.TypeOrder;
+import com.google.gwt.dev.jjs.impl.RewriteConstructorCallsForUnboxedTypes;
 import com.google.gwt.dev.jjs.impl.SameParameterValueOptimizer;
 import com.google.gwt.dev.jjs.impl.SourceInfoCorrelator;
 import com.google.gwt.dev.jjs.impl.TypeCoercionNormalizer;
@@ -325,6 +326,9 @@
       // TODO(stalcup): hide metrics gathering in a callback or subclass
       logger.log(TreeLogger.INFO, "Compiling permutation " + permutationId + "...");
 
+      // Rewrite calls to from boxed constructor types to specialized unboxed methods
+      RewriteConstructorCallsForUnboxedTypes.exec(jprogram);
+
       // (2) Transform unresolved Java AST to resolved Java AST
       ResolvePermutationDependentValues
           .exec(jprogram, properties, permutation.getPropertyAndBindingInfos());
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 c5d5a17..2135760 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
@@ -22,6 +22,7 @@
 import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.jjs.SourceOrigin;
 import com.google.gwt.dev.jjs.impl.GwtAstBuilder;
+import com.google.gwt.dev.jjs.impl.TypeCategory;
 import com.google.gwt.dev.jjs.impl.codesplitter.FragmentPartitioningResult;
 import com.google.gwt.dev.util.StringInterner;
 import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
@@ -32,6 +33,7 @@
 import com.google.gwt.thirdparty.guava.common.collect.Collections2;
 import com.google.gwt.thirdparty.guava.common.collect.HashBiMap;
 import com.google.gwt.thirdparty.guava.common.collect.ImmutableList;
+import com.google.gwt.thirdparty.guava.common.collect.ImmutableMap;
 import com.google.gwt.thirdparty.guava.common.collect.Lists;
 import com.google.gwt.thirdparty.guava.common.collect.Maps;
 import com.google.gwt.thirdparty.guava.common.collect.Sets;
@@ -43,6 +45,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.EnumSet;
 import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -55,6 +58,68 @@
 public class JProgram extends JNode implements ArrayTypeCreator {
 
   /**
+   * Encapsulates all information necessary to deal with native represented types in an
+   * generic fashion used throughout GWT. This can be extended later to deal with say, unboxed
+   * Integer if desired.
+   */
+  public enum DispatchType {
+    // These this list can be extended by creating the appropriate fields/methods on Cast,
+    // as well as extending the TypeCategory enum and updating EqualityNormalizer.
+    STRING(true),
+    DOUBLE(true),
+    BOOLEAN(true),
+
+    // non-native represented type values.
+    HAS_JAVA_VIRTUAL_DISPATCH(false), JAVA_ARRAY(false), JSO(false);
+
+    private final String instanceOfMethod;
+    private final String castMapField;
+    private final TypeCategory typeCategory;
+    private final String dynamicCastMethod;
+
+    DispatchType(boolean nativeType) {
+      if (nativeType) {
+        // These field are initialized to methods that are by-convention
+        // The conventions are:
+        // Cast.isJava[BoxedTypeName] for instanceof checks
+        // Cast.[boxedTypeName]CastMap for cast map fields
+        // Cast.dynamicCastTo[BoxedTypeName] for cast checks
+        // TypedCategory.TYPE_JAVA_LANG_[BoxedTypeName]
+        // If Cast or TypeCategory is edited, update this constructor
+        this.instanceOfMethod = "Cast.isJava" + camelCase(name());
+        this.castMapField = "Cast." + name().toLowerCase() + "CastMap";
+        this.dynamicCastMethod = "Cast.dynamicCastTo" + camelCase(name());
+        this.typeCategory = TypeCategory.valueOf("TYPE_JAVA_LANG_" + name());
+      } else {
+        instanceOfMethod = null;
+        castMapField = null;
+        typeCategory = null;
+        dynamicCastMethod = null;
+      }
+    }
+
+    private static String camelCase(String name) {
+      return Character.toUpperCase(name.charAt(0)) + name.toLowerCase().substring(1);
+    }
+
+    public String getInstanceOfMethod() {
+      return instanceOfMethod;
+    }
+
+    public String getCastMapField() {
+      return castMapField;
+    }
+
+    public TypeCategory getTypeCategory() {
+      return typeCategory;
+    }
+
+    public String getDynamicCastMethod() {
+      return dynamicCastMethod;
+    }
+  }
+
+  /**
    * Returns whether a class is a synthetic Prototype class generated by APT or user.
    */
   public boolean isJsTypePrototype(JDeclaredType classType) {
@@ -288,8 +353,14 @@
 
   private JClassType typeString;
 
+  private JClassType typeDouble;
+
+  private JClassType typeBoolean;
+
   private FragmentPartitioningResult fragmentPartitioningResult;
 
+  private Map<JClassType, DispatchType> nativeType2DispatchType;
+
   /**
    * Add a pinned method.
    */
@@ -353,6 +424,12 @@
       case "java.lang.String":
         typeString = (JClassType) type;
         break;
+      case "java.lang.Double":
+        typeDouble = (JClassType) type;
+        break;
+      case "java.lang.Boolean":
+        typeBoolean = (JClassType) type;
+        break;
       case "java.lang.Class":
         typeClass = (JClassType) type;
         break;
@@ -365,6 +442,59 @@
     }
   }
 
+  public boolean isRepresentedAsNativeJsPrimitive(JType type) {
+    return getRepresentedAsNativeTypes().contains(type);
+  }
+
+  public Set<JClassType> getRepresentedAsNativeTypes() {
+    return getRepresentedAsNativeTypesDispatchMap().keySet();
+  }
+
+  public Map<JClassType, DispatchType> getRepresentedAsNativeTypesDispatchMap() {
+     if (nativeType2DispatchType == null) {
+       // these are returned in the reverse order in which they are checked
+       // because devirtualizer constructs the nested expressions from the inside out.
+       nativeType2DispatchType = ImmutableMap.of(
+           typeBoolean, DispatchType.BOOLEAN,
+           typeDouble, DispatchType.DOUBLE,
+           typeString, DispatchType.STRING);
+     }
+    return nativeType2DispatchType;
+  }
+
+  public EnumSet<DispatchType> getDispatchType(JReferenceType type) {
+    if (!typeOracle.isInstantiatedType(type)) {
+      return EnumSet.noneOf(DispatchType.class);
+    }
+
+    // Object methods can be dispatched to all four possible classes.
+    if (type == getTypeJavaLangObject()) {
+      return EnumSet.allOf(DispatchType.class);
+    }
+
+    EnumSet<DispatchType> dispatchSet = EnumSet.noneOf(DispatchType.class);
+    DispatchType dispatchType = getRepresentedAsNativeTypesDispatchMap().get(type);
+    if (dispatchType != null) {
+      dispatchSet = EnumSet.of(dispatchType);
+    } else if (typeOracle.isDualJsoInterface(type)) {
+      // If it is an interface implemented both by JSOs and regular Java Objects;
+      dispatchSet = EnumSet.of(DispatchType.HAS_JAVA_VIRTUAL_DISPATCH, DispatchType.JSO);
+    } else if (typeOracle.isSingleJsoImpl(type) || type.isJsoType()) {
+      // If it is either an interface implemented by JSOs or JavaScriptObject or one of its
+      // subclasses.
+      dispatchSet = EnumSet.of(DispatchType.JSO);
+    }
+
+    for (JDeclaredType potentialNativeDispatchType : getRepresentedAsNativeTypes()) {
+      if (typeOracle.isInstantiatedType(potentialNativeDispatchType) &&
+          typeOracle.castSucceedsTrivially(potentialNativeDispatchType, type)) {
+        dispatchSet.add(getRepresentedAsNativeTypesDispatchMap().get(potentialNativeDispatchType));
+        dispatchSet.add(DispatchType.HAS_JAVA_VIRTUAL_DISPATCH);
+      }
+    }
+    return dispatchSet;
+  }
+
   /**
    * Return the greatest lower bound of two types. That is, return the largest
    * type that is a subtype of both inputs. If none exists return {@code thisType}.
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 4dc76f8..b263b4c 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
@@ -413,7 +413,6 @@
       }
 
       rescueArgumentsIfParametersCanBeRead(call);
-
       return false;
     }
 
@@ -514,7 +513,8 @@
     private boolean canBeInstantiatedInJavaScript(JType type) {
       // Technically, JsType/JsFunction are also instantiatable in JavaScript but we don't track
       // them using similar to JSO as if we do that then after cast normalization, they got pruned.
-      if (program.typeOracle.canBeJavaScriptObject(type) || program.isJavaLangString(type)) {
+      if (program.typeOracle.canBeJavaScriptObject(type)
+          || program.isRepresentedAsNativeJsPrimitive(type)) {
         return true;
       }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/Devirtualizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/Devirtualizer.java
index 3da8a7e..acf104c 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/Devirtualizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/Devirtualizer.java
@@ -31,6 +31,7 @@
 import com.google.gwt.dev.jjs.ast.JParameter;
 import com.google.gwt.dev.jjs.ast.JParameterRef;
 import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JProgram.DispatchType;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
 import com.google.gwt.dev.jjs.ast.JReturnStatement;
 import com.google.gwt.dev.jjs.ast.JType;
@@ -41,6 +42,8 @@
 import com.google.gwt.thirdparty.guava.common.collect.Lists;
 import com.google.gwt.thirdparty.guava.common.collect.Maps;
 
+import java.util.EnumMap;
+import java.util.EnumSet;
 import java.util.List;
 import java.util.Map;
 
@@ -109,16 +112,16 @@
       }
       JType instanceType = x.getInstance().getType().getUnderlyingType();
 
-      // If the instance can't possibly be a JSO, String or an interface implemented by String, do
-      // not devirtualize.
+      // If the instance can't possibly be a JSO, String, Number, or an interface implemented by
+      // either, do not devirtualize.
       if (instanceType != program.getTypeJavaLangObject()
           && !program.typeOracle.canBeJavaScriptObject(instanceType)
           // not a string
-          && instanceType != program.getTypeJavaLangString()
+          && !(program.isRepresentedAsNativeJsPrimitive(instanceType))
           // not an array
           && !(instanceType instanceof JArrayType)
           // not an interface of String, e.g. CharSequence or Comparable
-          && !program.getTypeJavaLangString().getImplements().contains(instanceType)
+          && !isSuperOfRepresentedAsNativeType(instanceType)
           // it is a super.m() call and the superclass is not a JSO. (this case is NOT reached if
           // MakeCallsStatic was called).
           || x.isStaticDispatchOnly()
@@ -203,12 +206,21 @@
           || program.typeOracle.isSingleJsoImpl(targetType)
           || program.typeOracle.isDualJsoInterface(targetType)
           || targetType == program.getTypeJavaLangObject()
-          || targetType == program.getTypeJavaLangString()
-          || program.getTypeJavaLangString().getImplements().contains(targetType)) {
+          || isSuperOfRepresentedAsNativeType(targetType)) {
         return true;
       }
       return false;
     }
+
+    private boolean isSuperOfRepresentedAsNativeType(JType targetType) {
+      for (JClassType type : program.getRepresentedAsNativeTypes()) {
+        if (program.typeOracle.isInstantiatedType(type) &&
+            program.typeOracle.castSucceedsTrivially(type, targetType)) {
+          return true;
+        }
+      }
+      return false;
+    }
   }
 
   public static void exec(JProgram program) {
@@ -236,11 +248,6 @@
   private final JMethod hasJavaObjectVirtualDispatch;
 
   /**
-   * Contains the Cast.isJavaString method.
-   */
-  private final JMethod isJavaStringMethod;
-
-  /**
    * Contains the Cast.instanceofArray method.
    */
   private final JMethod isJavaArray;
@@ -297,7 +304,7 @@
 
   private Devirtualizer(JProgram program) {
     this.program = program;
-    this.isJavaStringMethod = program.getIndexedMethod("Cast.isJavaString");
+
     this.hasJavaObjectVirtualDispatch =
         program.getIndexedMethod("Cast.hasJavaObjectVirtualDispatch");
     this.isJavaArray = program.getIndexedMethod("Cast.isJavaArray");
@@ -325,6 +332,7 @@
     if (target == null) {
       return null;
     }
+
     for (JMethod overridingMethod : target.getMethods()) {
       if (JTypeOracle.methodsDoMatch(method, overridingMethod)) {
         return overridingMethod;
@@ -385,13 +393,6 @@
     return dispatchCall;
   }
 
-  // Byte mask used by {@link ::getÓrCreateDevirtualMethod} to determine possible dispatches of a
-  // method.
-  private static final byte STRING = 0x01;
-  private static final byte HAS_JAVA_VIRTUAL_DISPATCH = 0x02;
-  private static final byte JAVA_ARRAY = 0x04;
-  private static final byte JSO = 0x08;
-
   /**
    * Create a conditional method to discriminate between static and virtual
    * dispatch.
@@ -413,42 +414,27 @@
     /////////////////////////////////////////////////////////////////
     // 1. Determine which types of object are target of this dispatch
     /////////////////////////////////////////////////////////////////
-    byte possibleTargetTypes = 0x0;
     JReferenceType enclosingType = method.getEnclosingType();
-    if (enclosingType == program.getTypeJavaLangObject()) {
-      // Object methods can be dispatched to all four possible classes.
-      possibleTargetTypes = STRING | HAS_JAVA_VIRTUAL_DISPATCH | JAVA_ARRAY | JSO;
-    } else if (enclosingType == program.getTypeJavaLangString()) {
-      // String is final and can not be extended.
-      possibleTargetTypes |= STRING;
-    }
-
-    if (program.typeOracle.isDualJsoInterface(enclosingType)) {
-      // If it is an interface implemented both by JSOs and regular Java Objects;
-      possibleTargetTypes = HAS_JAVA_VIRTUAL_DISPATCH | JSO;
-    } else if (program.typeOracle.isSingleJsoImpl(enclosingType) || enclosingType.isJsoType()) {
-      // If it is either an interface implemented by JSOs or JavaScriptObject or one of its
-      // subclasses.
-      possibleTargetTypes = JSO;
-    }
-
-    if (program.getTypeJavaLangString().getImplements().contains(enclosingType)) {
-      // If it is an interface implemented by String.
-      possibleTargetTypes |= (byte) (STRING | HAS_JAVA_VIRTUAL_DISPATCH);
-    }
+    EnumSet<DispatchType> possibleTargetTypes = program.getDispatchType(
+        enclosingType.getUnderlyingType());
 
     /////////////////////////////////////////////////////////////////
     // 2. Compute the dispatch to method for each relevant case.
     /////////////////////////////////////////////////////////////////
-    Map<Byte, JMethod> dispatchToMethodByTargetType = Maps.newTreeMap();
-    if ((possibleTargetTypes & STRING) != 0) {
-      JMethod overridingMethod = findOverridingMethod(method, program.getTypeJavaLangString());
-      assert overridingMethod != null : method.getEnclosingType().getName() + "::" +
-          method.getName() + " not overridden by String";
-      dispatchToMethodByTargetType.put(STRING,
-          staticImplCreator.getOrCreateStaticImpl(program, overridingMethod));
+    EnumMap<DispatchType, JMethod> dispatchToMethodByTargetType = new EnumMap<>(DispatchType.class);
+    for (Map.Entry<JClassType, DispatchType> nativeRepresentedType :
+        program.getRepresentedAsNativeTypesDispatchMap().entrySet()) {
+      // skip non-instantiated boxed types, which have been pruned from the AST.
+      if (program.typeOracle.isInstantiatedType(nativeRepresentedType.getKey())) {
+        maybeCreateDispatchFor(method, nativeRepresentedType.getValue(), possibleTargetTypes,
+            dispatchToMethodByTargetType, nativeRepresentedType.getKey());
+      }
     }
-    if ((possibleTargetTypes & JSO) != 0) {
+
+    maybeCreateDispatchFor(method, DispatchType.JAVA_ARRAY, possibleTargetTypes,
+        dispatchToMethodByTargetType, program.getTypeJavaLangObject());
+
+    if (possibleTargetTypes.contains(DispatchType.JSO)) {
       JMethod overridingMethod = findOverridingMethod(method,
           program.typeOracle.getSingleJsoImpl(enclosingType));
       if (overridingMethod == null && enclosingType == program.getTypeJavaLangObject()) {
@@ -456,19 +442,12 @@
       }
       assert overridingMethod != null : method.getEnclosingType().getName() + "::" +
           method.getName() + " not overridden by JavaScriptObject";
-      dispatchToMethodByTargetType.put(JSO,
+      dispatchToMethodByTargetType.put(DispatchType.JSO,
           staticImplCreator.getOrCreateStaticImpl(program, overridingMethod));
     }
-    if ((possibleTargetTypes & JAVA_ARRAY) != 0) {
-      // Arrays only implement Object methods as the Clonable interface is not supported in GWT.
-      JMethod overridingMethod = findOverridingMethod(method, program.getTypeJavaLangObject());
-      assert overridingMethod != null : method.getEnclosingType().getName() + "::" +
-          method.getName() + " not overridden by Object";
-      dispatchToMethodByTargetType.put(JAVA_ARRAY,
-          staticImplCreator.getOrCreateStaticImpl(program, overridingMethod));
-    }
-    if ((possibleTargetTypes & HAS_JAVA_VIRTUAL_DISPATCH) != 0) {
-      dispatchToMethodByTargetType.put(HAS_JAVA_VIRTUAL_DISPATCH, method);
+
+    if (possibleTargetTypes.contains(DispatchType.HAS_JAVA_VIRTUAL_DISPATCH)) {
+      dispatchToMethodByTargetType.put(DispatchType.HAS_JAVA_VIRTUAL_DISPATCH, method);
     }
 
     /////////////////////////////////////////////////////////////////
@@ -483,17 +462,27 @@
     JClassType devirtualMethodEnclosingClass  = null;
     if (method.getEnclosingType() instanceof JClassType) {
       devirtualMethodEnclosingClass = (JClassType) method.getEnclosingType();
-    } else if (dispatchToMethodByTargetType.get(STRING) != null) {
-      // Methods from interfaces implemented by String end up in String.
-      devirtualMethodEnclosingClass = program.getTypeJavaLangString();
-    } else if (dispatchToMethodByTargetType.get(JSO) != null) {
-      // This is an interface method implemented by a JSO, place in the JSO class.
-      devirtualMethodEnclosingClass = (JClassType)
-          dispatchToMethodByTargetType.get(JSO).getEnclosingType();
-    } else {
-      // It is an interface implemented by String or arrays, place it in Object.
-      devirtualMethodEnclosingClass = program.getTypeJavaLangObject();
+    }  else {
+      for (Map.Entry<JClassType, DispatchType> nativeRepresentedType :
+          program.getRepresentedAsNativeTypesDispatchMap().entrySet()) {
+        if (dispatchToMethodByTargetType.containsKey(nativeRepresentedType.getValue())) {
+          devirtualMethodEnclosingClass = nativeRepresentedType.getKey();
+          break;
+        }
+      }
     }
+
+    if (devirtualMethodEnclosingClass == null) {
+      if (dispatchToMethodByTargetType.get(DispatchType.JSO) != null) {
+        // This is an interface method implemented by a JSO, place in the JSO class.
+        devirtualMethodEnclosingClass = (JClassType)
+            dispatchToMethodByTargetType.get(DispatchType.JSO).getEnclosingType();
+      } else {
+        // It is an interface implemented by String or arrays, place it in Object.
+        devirtualMethodEnclosingClass = program.getTypeJavaLangObject();
+      }
+    }
+
     // Devirtualization of external methods stays external and devirtualization of internal methods
     // stays internal.
     assert program.isReferenceOnly(devirtualMethodEnclosingClass)
@@ -521,13 +510,13 @@
 
     // Construct back to fort. Last is JSO.
     JExpression dispatchExpression =
-        maybeCreateDispatch(dispatchToMethodByTargetType.get(JSO), devirtualMethod);
+        maybeCreateDispatch(dispatchToMethodByTargetType.get(DispatchType.JSO), devirtualMethod);
 
     // Dispatch to array
     dispatchExpression = constructMinimalCondition(
         isJavaArray,
         new JParameterRef(thisParam.getSourceInfo(), thisParam),
-        maybeCreateDispatch(dispatchToMethodByTargetType.get(JAVA_ARRAY), devirtualMethod),
+        maybeCreateDispatch(dispatchToMethodByTargetType.get(DispatchType.JAVA_ARRAY), devirtualMethod),
         dispatchExpression);
 
     // Dispatch to regular object
@@ -535,15 +524,19 @@
         hasJavaObjectVirtualDispatch,
         new JParameterRef(thisParam.getSourceInfo(), thisParam),
         maybeCreateDispatch(
-            dispatchToMethodByTargetType.get(HAS_JAVA_VIRTUAL_DISPATCH), devirtualMethod),
+            dispatchToMethodByTargetType.get(DispatchType.HAS_JAVA_VIRTUAL_DISPATCH), devirtualMethod),
         dispatchExpression);
 
-    // Dispatch to regular string
-    dispatchExpression = constructMinimalCondition(
-        isJavaStringMethod,
-        new JParameterRef(thisParam.getSourceInfo(), thisParam),
-        maybeCreateDispatch(dispatchToMethodByTargetType.get(STRING), devirtualMethod),
-        dispatchExpression);
+    // Dispatch to regular string, double, boolean
+    for (Map.Entry<JClassType, DispatchType> nativeRepresentedType
+        : program.getRepresentedAsNativeTypesDispatchMap().entrySet()) {
+      DispatchType dispatchType = nativeRepresentedType.getValue();
+      dispatchExpression = constructMinimalCondition(
+          program.getIndexedMethod(dispatchType.getInstanceOfMethod()),
+          new JParameterRef(thisParam.getSourceInfo(), thisParam),
+          maybeCreateDispatch(dispatchToMethodByTargetType.get(dispatchType), devirtualMethod),
+          dispatchExpression);
+    }
 
     // return dispatchConditional;
     JReturnStatement returnStatement = new JReturnStatement(sourceInfo, dispatchExpression);
@@ -553,4 +546,16 @@
 
     return devirtualMethod;
   }
+
+  private void maybeCreateDispatchFor(JMethod method, DispatchType target,
+      EnumSet<DispatchType> possibleTargetTypes,
+      EnumMap<DispatchType, JMethod> dispatchToMethodByTargetType, JClassType targetDevirtualType) {
+    if (possibleTargetTypes.contains(target)) {
+      JMethod overridingMethod = findOverridingMethod(method, targetDevirtualType);
+      assert overridingMethod != null : method.getEnclosingType().getName() + "::" +
+          method.getName() + " not overridden by " + targetDevirtualType.getSimpleName();
+      dispatchToMethodByTargetType.put(target,
+          staticImplCreator.getOrCreateStaticImpl(program, overridingMethod));
+    }
+  }
 }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/EqualityNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/EqualityNormalizer.java
index d9714e3..30e0bcb 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/EqualityNormalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/EqualityNormalizer.java
@@ -24,9 +24,12 @@
 import com.google.gwt.dev.jjs.ast.JMethodCall;
 import com.google.gwt.dev.jjs.ast.JModVisitor;
 import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JProgram.DispatchType;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
 import com.google.gwt.dev.jjs.ast.JType;
 
+import java.util.Map;
+
 /**
  * <p>
  * Rewrite Java <code>==</code> so that it will execute correctly in JavaScript.
@@ -40,16 +43,16 @@
  * <code>undefined</code> as a valid translation of a Java <code>null</code>.
  * </p>
  * <p>
- * However, whenever something that may be a String is compared to something
- * that may not be a <code>String</code>, use <code>===</code>. A Java object
+ * However, whenever something that may be an unboxed type is compared to something
+ * that may not be a unboxed type, use <code>===</code>. A Java object
  * compared to a string should always yield false, but that's not true when
  * comparing in JavaScript using <code>==</code>. The cases where
  * <code>===</code> must be used are:
  * </p>
  * <ul>
- * <li>One or both sides have unknown <code>String</code> status.</li>
- * <li>One side is definitely <code>String</code> and one side is definitely !
- * <code>String</code>. <br/>
+ * <li>One or both sides have unknown <i>unboxed type</i> status.</li>
+ * <li>One side is definitely an <i>unboxed type</i> and one side is definitely !
+ * <i>unboxed type</i>. <br/>
  * TODO: This case could be optimized as
  * <code>(a == null) &amp; (b == null)</code>.</li>
  * </ul>
@@ -58,6 +61,8 @@
  * <code>null</code> vs. <code>undefined</code> if it's possible for one side to
  * be <code>null</code> and the other to be <code>undefined</code>.
  * </p>
+ * <p> An "unboxed type" is a String, Boolean, or Double that is represented as a naked raw
+ * JS type.
  */
 public class EqualityNormalizer {
 
@@ -81,12 +86,12 @@
         return;
       }
 
-      StringStatus lhsStatus = getStringStatus((JReferenceType) lhsType);
-      StringStatus rhsStatus = getStringStatus((JReferenceType) rhsType);
-      int strat = COMPARISON_STRAT[lhsStatus.getIndex()][rhsStatus.getIndex()];
+      UnboxedTypeStatus lhsStatus = getUnboxedTypeStatus((JReferenceType) lhsType);
+      UnboxedTypeStatus rhsStatus = getUnboxedTypeStatus((JReferenceType) rhsType);
+      int comparisonStrategy = COMPARISON_TABLE[lhsStatus.getIndex()][rhsStatus.getIndex()];
 
-      switch (strat) {
-        case STRAT_TRIPLE: {
+      switch (comparisonStrategy) {
+        case T: {
           if (canBeNull(lhs) && canBeNull(rhs)) {
             /*
              * If it's possible for one side to be null and the other side
@@ -102,11 +107,11 @@
           break;
         }
 
-        case STRAT_DOUBLE: {
+        case D: {
           boolean lhsNullLit = lhs == program.getLiteralNull();
           boolean rhsNullLit = rhs == program.getLiteralNull();
-          if ((lhsNullLit && rhsStatus == StringStatus.NOTSTRING)
-              || (rhsNullLit && lhsStatus == StringStatus.NOTSTRING)) {
+          if ((lhsNullLit && rhsStatus == UnboxedTypeStatus.NOT_UNBOXEDTYPE)
+              || (rhsNullLit && lhsStatus == UnboxedTypeStatus.NOT_UNBOXEDTYPE)) {
             /*
              * If either side is a null literal and the other is non-String,
              * replace with a null-check.
@@ -139,21 +144,33 @@
       }
     }
 
-    private StringStatus getStringStatus(JReferenceType type) {
-      JClassType stringType = program.getTypeJavaLangString();
+    private UnboxedTypeStatus getUnboxedTypeStatus(JReferenceType type) {
       if (type.isNullType()) {
-        return StringStatus.NULL;
-      } else if (program.typeOracle.castSucceedsTrivially(type, stringType)) {
-        return StringStatus.STRING;
-      } else if (program.typeOracle.castFailsTrivially(type, stringType)) {
-        return StringStatus.NOTSTRING;
+        return UnboxedTypeStatus.NULL;
       } else {
-        return StringStatus.UNKNOWN;
+        for (Map.Entry<JClassType, DispatchType> nativeDispatchType :
+            program.getRepresentedAsNativeTypesDispatchMap().entrySet()) {
+          if (program.typeOracle.castSucceedsTrivially(type, nativeDispatchType.getKey())) {
+            switch (nativeDispatchType.getValue()) {
+              case DOUBLE:
+                return UnboxedTypeStatus.DOUBLE;
+              case BOOLEAN:
+                return UnboxedTypeStatus.BOOLEAN;
+              case STRING:
+                return UnboxedTypeStatus.STRING;
+              default:
+                throw new IllegalStateException("Shouldn't happen");
+            }
+          } else if (!program.typeOracle.castFailsTrivially(type, nativeDispatchType.getKey())) {
+            return UnboxedTypeStatus.UNKNOWN;
+          }
+        }
+        return UnboxedTypeStatus.NOT_UNBOXEDTYPE;
       }
     }
 
     private JExpression maskUndefined(JExpression lhs) {
-      assert ((JReferenceType) lhs.getType()).canBeNull();
+      assert lhs.getType().canBeNull();
 
       JMethod maskMethod = program.getIndexedMethod("Cast.maskUndefined");
       JMethodCall lhsCall = new JMethodCall(lhs.getSourceInfo(), null, maskMethod, lhs.getType());
@@ -166,12 +183,12 @@
    * Represents what we know about an operand type in terms of its type and
    * <code>null</code> status.
    */
-  private enum StringStatus {
-    NOTSTRING(2), NULL(3), STRING(1), UNKNOWN(0);
+  private enum UnboxedTypeStatus {
+    NOT_UNBOXEDTYPE(4), NULL(5), DOUBLE(3), BOOLEAN(2) ,STRING(1), UNKNOWN(0);
 
     private int index;
 
-    StringStatus(int index) {
+    UnboxedTypeStatus(int index) {
       this.index = index;
     }
 
@@ -181,32 +198,41 @@
   }
 
   /**
-   * A map of the combinations where each comparison strategy should be used.
-   */
-  private static int[][] COMPARISON_STRAT = {
-      // ..U..S.!S..N
-      {1, 1, 1, 0,}, // UNKNOWN
-      {1, 0, 1, 0,}, // STRING
-      {1, 1, 0, 0,}, // NOTSTRING
-      {0, 0, 0, 0,}, // NULL
-  };
-
-  /**
    * The comparison strategy of using ==.
+   * Mnemonic: D = double eq
    */
-  private static final int STRAT_DOUBLE = 0;
+  private static final int D = 0;
 
   /**
    * The comparison strategy of using ===.
+   * Mnemonic: T = triple eq
    */
-  private static final int STRAT_TRIPLE = 1;
+  private static final int T = 1;
+
+  /**
+   * A map of the combinations where each comparison strategy should be used.
+   */
+  private static int[][] COMPARISON_TABLE = {
+      // any type compared to unknown uses triple eq
+      {T, T, T, T, T, D,}, // UNKNOWN
+      // string type uses D only against String and null
+      {T, D, T, T, T, D,}, // STRING
+      // double type uses D only against Double and null
+      {T, T, D, T, T, D,}, // DOUBLE
+      // boolean type uses D only against Boolean and null
+      {T, T, T, D, T, D,}, // BOOLEAN
+      // non-unboxed type uses D only against other non-unboxed types and null
+      {T, T, T, T, D, D,}, // NOT_UNBOXEDTYPE
+      // null vs null is safe everywhere
+      {D, D, D, D, D, D,}, // NULL
+  };
 
   public static void exec(JProgram program) {
     new EqualityNormalizer(program).execImpl();
   }
 
   private static boolean canBeNull(JExpression x) {
-    return ((JReferenceType) x.getType()).canBeNull();
+    return x.getType().canBeNull();
   }
 
   private final JProgram program;
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 f208011..ac591c3 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
@@ -81,6 +81,7 @@
 import com.google.gwt.dev.jjs.ast.JPrefixOperation;
 import com.google.gwt.dev.jjs.ast.JPrimitiveType;
 import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JProgram.DispatchType;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
 import com.google.gwt.dev.jjs.ast.JReturnStatement;
 import com.google.gwt.dev.jjs.ast.JStatement;
@@ -2348,8 +2349,12 @@
         // special: setup a "toString" alias for java.lang.Object.toString()
         generateToStringAlias(x, globalStmts);
 
-        // Set up the artificial castmap for string.
-        setupStringCastMap(program.getTypeJavaLangString(), globalStmts);
+        // Set up the artificial castmap for string, double, and boolean
+        for (Map.Entry<JClassType, DispatchType> nativeRepresentedType :
+            program.getRepresentedAsNativeTypesDispatchMap().entrySet()) {
+          setupCastMapForUnboxedType(nativeRepresentedType.getKey(), globalStmts,
+              nativeRepresentedType.getValue().getCastMapField());
+        }
 
         //  Perform necessary polyfills.
         globalStmts.add(constructInvocation(x.getSourceInfo(),
@@ -2661,11 +2666,12 @@
     }
 
     /*
-     * Sets up the catmap for String.
+     * Sets up the castmap for type X
      */
-    private void setupStringCastMap(JClassType x, List<JsStatement> globalStmts) {
-      //  Cast.stringCastMap = /* String cast map */ { ..:1, ..:1}
-      JField castableTypeMapField = program.getIndexedField("Cast.stringCastMap");
+    private void setupCastMapForUnboxedType(JClassType x, List<JsStatement> globalStmts,
+        String castMapField) {
+      //  Cast.[castMapName] = /* cast map */ { ..:1, ..:1}
+      JField castableTypeMapField = program.getIndexedField(castMapField);
       JsName castableTypeMapName = names.get(castableTypeMapField);
       JsNameRef ctmRef = castableTypeMapName.makeRef(x.getSourceInfo());
 
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 91c89df..1768df2 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
@@ -3283,24 +3283,6 @@
         }
       }
 
-      if (ctor.getEnclosingType() == javaLangString) {
-        /*
-         * MAGIC: java.lang.String is implemented as a JavaScript String
-         * primitive with a modified prototype. This requires funky handling of
-         * constructor calls. We find a method named _String() whose signature
-         * matches the requested constructor
-         *
-         * TODO(scottb): consider moving this to a later pass.
-         */
-        MethodBinding staticBinding =
-            targetBinding.getExactMethod(_STRING, b.parameters, curCud.scope);
-        assert staticBinding.isStatic();
-        JMethod staticMethod = typeMap.get(staticBinding);
-        JMethodCall newCall = new JMethodCall(info, null, staticMethod);
-        newCall.addArgs(call.getArgs());
-        call = newCall;
-      }
-
       push(call);
     }
 
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementCastsAndTypeChecks.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementCastsAndTypeChecks.java
index 8e38b09..71cb6c9 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementCastsAndTypeChecks.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementCastsAndTypeChecks.java
@@ -29,6 +29,7 @@
 import com.google.gwt.dev.jjs.ast.JModVisitor;
 import com.google.gwt.dev.jjs.ast.JPrimitiveType;
 import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.JProgram.DispatchType;
 import com.google.gwt.dev.jjs.ast.JReferenceType;
 import com.google.gwt.dev.jjs.ast.JRuntimeTypeReference;
 import com.google.gwt.dev.jjs.ast.JType;
@@ -226,6 +227,8 @@
 
     assert EnumSet.of(TypeCategory.TYPE_JSO, TypeCategory.TYPE_JAVA_OBJECT_OR_JSO,
         TypeCategory.TYPE_JAVA_LANG_OBJECT, TypeCategory.TYPE_JAVA_LANG_STRING,
+        TypeCategory.TYPE_JAVA_LANG_DOUBLE,
+        TypeCategory.TYPE_JAVA_LANG_BOOLEAN,
         TypeCategory.TYPE_JAVA_OBJECT, TypeCategory.TYPE_JS_PROTOTYPE,
         TypeCategory.TYPE_JS_FUNCTION).contains(typeCategory);
 
@@ -300,12 +303,17 @@
     this.instanceOfMethodsByTargetTypeCategory.put(
         TypeCategory.TYPE_JSO, program.getIndexedMethod("Cast.instanceOfJso"));
     this.instanceOfMethodsByTargetTypeCategory.put(
-        TypeCategory.TYPE_JAVA_LANG_STRING, program.getIndexedMethod("Cast.isJavaString"));
-    this.instanceOfMethodsByTargetTypeCategory.put(
         TypeCategory.TYPE_JS_PROTOTYPE, program.getIndexedMethod("Cast.instanceOfJsPrototype"));
     this.instanceOfMethodsByTargetTypeCategory.put(
         TypeCategory.TYPE_JS_FUNCTION, program.getIndexedMethod("Cast.instanceOfJsFunction"));
 
+    for (DispatchType nativeDispatchType : program.getRepresentedAsNativeTypesDispatchMap().values()) {
+      this.instanceOfMethodsByTargetTypeCategory.put(
+          nativeDispatchType.getTypeCategory(),
+          program.getIndexedMethod(nativeDispatchType.getInstanceOfMethod())
+      );
+    }
+
     // Populate the necessary dynamicCast methods.
     this.dynamicCastMethodsByTargetTypeCategory.put(
         TypeCategory.TYPE_JAVA_OBJECT, program.getIndexedMethod("Cast.dynamicCast"));
@@ -315,12 +323,19 @@
         TypeCategory.TYPE_JAVA_OBJECT_OR_JSO, program.getIndexedMethod("Cast.dynamicCastAllowJso"));
     this.dynamicCastMethodsByTargetTypeCategory.put(
         TypeCategory.TYPE_JSO, program.getIndexedMethod("Cast.dynamicCastJso"));
-    this.dynamicCastMethodsByTargetTypeCategory.put(
-        TypeCategory.TYPE_JAVA_LANG_STRING, program.getIndexedMethod("Cast.dynamicCastToString"));
+
     this.dynamicCastMethodsByTargetTypeCategory.put(
         TypeCategory.TYPE_JS_PROTOTYPE, program.getIndexedMethod("Cast.dynamicCastWithPrototype"));
     this.dynamicCastMethodsByTargetTypeCategory.put(
         TypeCategory.TYPE_JS_FUNCTION, program.getIndexedMethod("Cast.dynamicCastToJsFunction"));
+
+    for (DispatchType nativeDispatchType :
+        program.getRepresentedAsNativeTypesDispatchMap().values()) {
+      this.dynamicCastMethodsByTargetTypeCategory.put(
+          nativeDispatchType.getTypeCategory(),
+          program.getIndexedMethod(nativeDispatchType.getDynamicCastMethod())
+      );
+    }
   }
 
   private void execImpl() {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/RewriteConstructorCallsForUnboxedTypes.java b/dev/core/src/com/google/gwt/dev/jjs/impl/RewriteConstructorCallsForUnboxedTypes.java
new file mode 100644
index 0000000..4717a41
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/RewriteConstructorCallsForUnboxedTypes.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2014 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.jjs.impl;
+
+import com.google.gwt.dev.jjs.ast.Context;
+import com.google.gwt.dev.jjs.ast.JConstructor;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JMethodCall;
+import com.google.gwt.dev.jjs.ast.JModVisitor;
+import com.google.gwt.dev.jjs.ast.JNewInstance;
+import com.google.gwt.dev.jjs.ast.JProgram;
+import com.google.gwt.dev.jjs.ast.js.JsniMethodRef;
+import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
+import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
+import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Rewrite instantiations of Boolean, Double, and String to use static helper methods which return
+ * unboxed versions.
+ *
+ */
+public class RewriteConstructorCallsForUnboxedTypes extends JModVisitor {
+
+  public static final String NATIVE_TYPE_CREATEMETHOD_PREFIX = "$create";
+  private JProgram program;
+  private Map<String, JMethod> jsniSig2Method = new HashMap<>();
+
+  public RewriteConstructorCallsForUnboxedTypes(JProgram program) {
+    this.program = program;
+    for (JDeclaredType unboxedType : program.getRepresentedAsNativeTypes()) {
+      for (JMethod method : unboxedType.getMethods()) {
+        if (method.getName().startsWith(NATIVE_TYPE_CREATEMETHOD_PREFIX)) {
+          jsniSig2Method.put(method.getJsniSignature(false, false), method);
+        }
+      }
+    }
+  }
+
+  @Override
+  public void endVisit(JNewInstance x, Context ctx) {
+    JMethod createMethod;
+    JConstructor ctor = x.getTarget();
+
+    if (!program.isRepresentedAsNativeJsPrimitive(ctor.getEnclosingType())) {
+      return;
+    }
+
+    // map BoxedType(args) -> BoxedType.$createBoxedType(args)
+    createMethod = jsniSig2Method.get(NATIVE_TYPE_CREATEMETHOD_PREFIX
+        + ctor.getJsniSignature(false, false));
+    assert createMethod != null;
+
+    JMethodCall createCall = new JMethodCall(x.getSourceInfo(), null, createMethod);
+    createCall.addArgs(x.getArgs());
+    ctx.replaceMe(createCall);
+  }
+
+  @Override
+  public void endVisit(JsniMethodRef x, Context ctx) {
+    if (x.getTarget().isConstructor()
+        && program.isRepresentedAsNativeJsPrimitive(x.getTarget().getEnclosingType())) {
+      JConstructor ctor = (JConstructor) x.getTarget();
+      // map BoxedType(args) -> BoxedType.$createBoxedType(args)
+      JMethod createMethod = jsniSig2Method.get(NATIVE_TYPE_CREATEMETHOD_PREFIX
+          + ctor.getJsniSignature(false, false));
+      assert createMethod != null;
+
+      JsniMethodRef newJsniMethodRef = new JsniMethodRef(x.getSourceInfo(),
+          x.getIdent(), createMethod, program.getJavaScriptObject());
+      ctx.replaceMe(newJsniMethodRef);
+    }
+  }
+
+  private static final String NAME = RewriteConstructorCallsForUnboxedTypes.class
+      .getSimpleName();
+
+  private OptimizerStats execImpl() {
+    OptimizerStats stats = new OptimizerStats(NAME);
+    accept(program);
+    return stats;
+  }
+
+  public static OptimizerStats exec(JProgram program) {
+    Event optimizeEvent = SpeedTracerLogger
+        .start(CompilerEventType.OPTIMIZE, "optimizer", NAME);
+    OptimizerStats stats = new RewriteConstructorCallsForUnboxedTypes(program).execImpl();
+    optimizeEvent.end("didChange", "" + stats.didChange());
+    return stats;
+  }
+}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeCategory.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeCategory.java
index 5d25699..b7f73f0 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeCategory.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeCategory.java
@@ -25,14 +25,17 @@
  *
  * These are used in Cast checking and array implementation.
  */
-public  enum TypeCategory {
+public enum TypeCategory {
   /* Make sure this list is kept in sync with the one in Array.java */
 
   TYPE_JAVA_OBJECT,
   TYPE_JAVA_OBJECT_OR_JSO,
   TYPE_JSO,
   TYPE_JAVA_LANG_OBJECT,
+  // If the next three are renamed, please update JProgram.DispatchType's constructor
   TYPE_JAVA_LANG_STRING,
+  TYPE_JAVA_LANG_DOUBLE,
+  TYPE_JAVA_LANG_BOOLEAN,
   TYPE_JS_PROTOTYPE,
   TYPE_JS_FUNCTION,
   TYPE_PRIMITIVE_LONG,
@@ -57,8 +60,8 @@
     type = type.getUnderlyingType();
     if (type == program.getTypeJavaLangObject()) {
       return TypeCategory.TYPE_JAVA_LANG_OBJECT;
-    } else if (type == program.getTypeJavaLangString()) {
-      return TypeCategory.TYPE_JAVA_LANG_STRING;
+    } else if (program.getRepresentedAsNativeTypesDispatchMap().containsKey(type)) {
+      return program.getRepresentedAsNativeTypesDispatchMap().get(type).getTypeCategory();
     } else if (program.typeOracle.isEffectivelyJavaScriptObject(type)) {
       return TypeCategory.TYPE_JSO;
     } else if (program.typeOracle.isDualJsoInterface(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 5ddff12..989da65 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
@@ -34,11 +34,13 @@
   private static final int TYPE_JSO = 2;
   private static final int TYPE_JAVA_LANG_OBJECT = 3;
   private static final int TYPE_JAVA_LANG_STRING = 4;
-  private static final int TYPE_JS_PROTOTYPE = 5;
-  private static final int TYPE_JS_FUNCTION = 6;
-  private static final int TYPE_PRIMITIVE_LONG = 7;
-  private static final int TYPE_PRIMITIVE_NUMBER = 8;
-  private static final int TYPE_PRIMITIVE_BOOLEAN = 9;
+  private static final int TYPE_JAVA_LANG_DOUBLE = 5;
+  private static final int TYPE_JAVA_LANG_BOOLEAN = 6;
+  private static final int TYPE_JS_PROTOTYPE = 7;
+  private static final int TYPE_JS_FUNCTION = 8;
+  private static final int TYPE_PRIMITIVE_LONG = 9;
+  private static final int TYPE_PRIMITIVE_NUMBER = 10;
+  private static final int TYPE_PRIMITIVE_BOOLEAN = 11;
 
   public static <T> T[] stampJavaTypeInfo(Object array, T[] referenceType) {
     initValues(referenceType.getClass(), Util.getCastableTypeMap(referenceType),
@@ -157,6 +159,10 @@
     switch (Array.getElementTypeCategory(array)) {
       case TYPE_JAVA_LANG_STRING:
         return Cast.isJavaString(value);
+      case TYPE_JAVA_LANG_DOUBLE:
+        return Cast.isJavaDouble(value);
+      case TYPE_JAVA_LANG_BOOLEAN:
+        return Cast.isJavaBoolean(value);
       case TYPE_JAVA_OBJECT:
         return Cast.canCast(value, Array.getElementTypeId(array));
       case TYPE_JSO:
diff --git a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java
index b41383e..122fa90 100644
--- a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java
+++ b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java
@@ -39,13 +39,23 @@
    * NOTE: it is important that the field is left uninitialized so that Cast does not
    * require a clinit.
    */
+  // NOTE: if any of these three are edited, update JProgram.DispatchType's constructor
   private static JavaScriptObject stringCastMap;
 
+  // the next two are implemented exactly as the former
+  private static JavaScriptObject doubleCastMap;
+  private static JavaScriptObject booleanCastMap;
+
   @HasNoSideEffects
   static native boolean canCast(Object src, JavaScriptObject dstId) /*-{
     return @com.google.gwt.lang.Cast::isJavaString(*)(src) &&
         !!@com.google.gwt.lang.Cast::stringCastMap[dstId] ||
-        src.@java.lang.Object::castableTypeMap && !!src.@java.lang.Object::castableTypeMap[dstId];
+        src.@java.lang.Object::castableTypeMap && !!src.@java.lang.Object::castableTypeMap[dstId] ||
+        @com.google.gwt.lang.Cast::isJavaDouble(*)(src) &&
+        !!@com.google.gwt.lang.Cast::doubleCastMap[dstId] ||
+        // this occurs last because it is much rarer and less likely to be in hot code
+        @com.google.gwt.lang.Cast::isJavaBoolean(*)(src) &&
+        !!@com.google.gwt.lang.Cast::booleanCastMap[dstId];
   }-*/;
 
   @HasNoSideEffects
@@ -65,11 +75,22 @@
     return src;
   }
 
+  // NOTE: if any of these three are edited, update JProgram.DispatchType's constructor
   static Object dynamicCastToString(Object src) {
     checkType(src == null || isJavaString(src));
     return src;
   }
 
+  static Object dynamicCastToDouble(Object src) {
+    checkType(src == null || isJavaDouble(src));
+    return src;
+  }
+
+  static Object dynamicCastToBoolean(Object src) {
+    checkType(src == null || isJavaBoolean(src));
+    return src;
+  }
+
   /**
    * Allow a dynamic cast to an object, always succeeding if it's a JSO.
    */
@@ -145,7 +166,7 @@
 
   /**
    * Uses the not operator to perform a null-check; do NOT use on anything that
-   * could be a String.
+   * could be a String, 'unboxed' Double, or 'unboxed' Boolean.
    */
   static native boolean isNotNull(Object src) /*-{
     // Coerce to boolean.
@@ -154,7 +175,7 @@
 
   /**
    * Uses the not operator to perform a null-check; do NOT use on anything that
-   * could be a String.
+   * could be a String, 'unboxed' Double, or 'unboxed' Boolean.
    */
   static native boolean isNull(Object src) /*-{
     return !src;
@@ -257,6 +278,7 @@
     return o;
   }
 
+  // NOTE: if any of these three are edited, update JProgram.DispatchType's constructor
   /**
    * Returns whether the Object is a Java String.
    *
@@ -268,6 +290,26 @@
   }-*/;
 
   /**
+   * Returns whether the Object is a Java Double.
+   *
+   * Java Numbers are translated to JavaScript numbers.
+   */
+  @HasNoSideEffects
+  static native boolean isJavaDouble(Object src) /*-{
+    return typeof(src) === "number";
+  }-*/;
+
+  /**
+   * Returns whether the Object is a Java Boolean. (*)
+   *
+   * Java Booleans are translated to JavaScript booleans.
+   */
+  @HasNoSideEffects
+  static native boolean isJavaBoolean(Object src) /*-{
+    return typeof(src) === "boolean";
+  }-*/;
+
+  /**
    * Returns true if Object can dispatch instance methods and does not need a compiler
    * provided trampoline.
    *
diff --git a/dev/core/test/com/google/gwt/core/ext/linker/SourceMapTest.java b/dev/core/test/com/google/gwt/core/ext/linker/SourceMapTest.java
index c820112..5fd62f3 100644
--- a/dev/core/test/com/google/gwt/core/ext/linker/SourceMapTest.java
+++ b/dev/core/test/com/google/gwt/core/ext/linker/SourceMapTest.java
@@ -385,7 +385,7 @@
           return true;
         }
         // only $init and $clinit will match the below if
-        if (relaxName.startsWith("$")) {
+        if (relaxName.startsWith("$") && !relaxName.startsWith("$create")) {
           return strictName.equals(relaxName.substring(1));
         }
         return false;
diff --git a/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java b/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
index 613af57..9e5713a 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
@@ -82,17 +82,23 @@
           "import com.google.gwt.core.client.JavaScriptObject;",
           "public final class Cast {",
           "  private static JavaScriptObject stringCastMap;",
+          "  private static JavaScriptObject doubleCastMap;",
+          "  private static JavaScriptObject booleanCastMap;",
           "  public static native String charToString(char x) /*-{ }-*/;",
           "  public static Object dynamicCast(Object src, int dstId) { return src;}",
           "  public static Object dynamicCastAllowJso(Object src, int dstId) { return src;}",
           "  public static Object dynamicCastJso(Object src) { return src;}",
           "  public static Object dynamicCastToString(Object src) { return src;}",
+          "  public static Object dynamicCastToDouble(Object src) { return src;}",
+          "  public static Object dynamicCastToBoolean(Object src) { return src;}",
           "  public static Object dynamicCastWithPrototype(Object src, int dstId, String jsType) { return src;}",
           "  public static Object dynamicCastToJsFunction(Object src) { return src; }",
           "  public static boolean instanceOf(Object src, int dstId) { return false;}",
           "  public static boolean hasJavaObjectVirtualDispatch(Object o) { return true; }",
           "  public static boolean isJavaArray(Object o) { return false; }",
           "  public static boolean isJavaString(Object o) { return true; }",
+          "  public static boolean isJavaDouble(Object o) { return true; }",
+          "  public static boolean isJavaBoolean(Object o) { return true; }",
           "  public static boolean isJavaScriptObject(Object o) { return true; }",
           "  public static boolean instanceOfOrJso(Object src, int dst) { return false;}",
           "  public static boolean instanceOfJso(Object src) { return false;}",
diff --git a/user/super/com/google/gwt/emul/java/lang/Boolean.java b/user/super/com/google/gwt/emul/java/lang/Boolean.java
index 0a57b30..9d4a1a4 100644
--- a/user/super/com/google/gwt/emul/java/lang/Boolean.java
+++ b/user/super/com/google/gwt/emul/java/lang/Boolean.java
@@ -70,37 +70,55 @@
     return valueOf(parseBoolean(s));
   }
 
-  private final transient boolean value;
-
   public Boolean(boolean value) {
-    this.value = value;
+    /*
+     * Call to $createBoolean(value) must be here so that the method is referenced and not pruned
+     * before new Boolean(value) is replaced by $createBoolean(value) by
+     * RewriteConstructorCallsForUnboxedTypes.
+     */
+    $createBoolean(value);
   }
 
   public Boolean(String s) {
-    this(parseBoolean(s));
+     /*
+     * Call to $createBoolean(value) must be here so that the method is referenced and not pruned
+     * before new Boolean(value) is replaced by $createBoolean(value) by
+     * RewriteConstructorCallsForUnboxedTypes.
+     */
+    $createBoolean(s);
   }
 
-  public boolean booleanValue() {
-    return value;
-  }
+  public native boolean booleanValue() /*-{
+    return @javaemul.internal.InternalPreconditions::checkNotNull(Ljava/lang/Object;)(this);
+  }-*/;
 
   @Override
   public int compareTo(Boolean b) {
-    return compare(value, b.value);
-  }
+    return compare(booleanValue(), b.booleanValue());
+  };
 
   @Override
-  public boolean equals(Object o) {
-    return (o instanceof Boolean) && (((Boolean) o).value == value);
-  }
+  public native boolean equals(Object o) /*-{
+    return this === o;
+  }-*/;
 
   @Override
   public int hashCode() {
-    return hashCode(value);
+    return hashCode(booleanValue());
   }
 
   @Override
   public String toString() {
-    return toString(value);
+    return toString(booleanValue());
   }
+
+  // CHECKSTYLE_OFF: Utility Methods for unboxed Boolean.
+  static native Boolean $createBoolean(boolean x) /*-{
+    return x;
+  }-*/;
+
+  static Boolean $createBoolean(String x) {
+    return $createBoolean(Boolean.parseBoolean(x));
+  }
+  // CHECKSTYLE_ON: End utility methods
 }
diff --git a/user/super/com/google/gwt/emul/java/lang/Double.java b/user/super/com/google/gwt/emul/java/lang/Double.java
index f75e195..2151657 100644
--- a/user/super/com/google/gwt/emul/java/lang/Double.java
+++ b/user/super/com/google/gwt/emul/java/lang/Double.java
@@ -76,16 +76,20 @@
   // 2^-1022 (smallest double non-denorm)
   private static final double POWER_MINUS_1022 = 2.2250738585072014E-308;
 
-  private static final double[] powers = {
-    POWER_512, POWER_256, POWER_128, POWER_64, POWER_32, POWER_16, POWER_8,
-    POWER_4, POWER_2, POWER_1
-  };
 
-  private static final double[] invPowers = {
-    POWER_MINUS_512, POWER_MINUS_256, POWER_MINUS_128, POWER_MINUS_64,
-    POWER_MINUS_32, POWER_MINUS_16, POWER_MINUS_8, POWER_MINUS_4, POWER_MINUS_2,
-    POWER_MINUS_1
-  };
+
+  private static class PowersTable {
+    private static final double[] powers = {
+        POWER_512, POWER_256, POWER_128, POWER_64, POWER_32, POWER_16, POWER_8,
+        POWER_4, POWER_2, POWER_1
+    };
+
+    private static final double[] invPowers = {
+        POWER_MINUS_512, POWER_MINUS_256, POWER_MINUS_128, POWER_MINUS_64,
+        POWER_MINUS_32, POWER_MINUS_16, POWER_MINUS_8, POWER_MINUS_4, POWER_MINUS_2,
+        POWER_MINUS_1
+    };
+  }
 
   public static int compare(double x, double y) {
     if (x < y) {
@@ -141,8 +145,8 @@
     if (value < 1.0) {
       int bit = 512;
       for (int i = 0; i < 10; i++, bit >>= 1) {
-        if (value < invPowers[i] && exp - bit >= -1023) {
-          value *= powers[i];
+        if (value < PowersTable.invPowers[i] && exp - bit >= -1023) {
+          value *= PowersTable.powers[i];
           exp -= bit;
         }
       }
@@ -154,8 +158,8 @@
     } else if (value >= 2.0) {
       int bit = 512;
       for (int i = 0; i < 10; i++, bit >>= 1) {
-        if (value >= powers[i]) {
-          value *= invPowers[i];
+        if (value >= PowersTable.powers[i]) {
+          value *= PowersTable.invPowers[i];
           exp += bit;
         }
       }
@@ -237,7 +241,7 @@
       int bit = 512;
       for (int i = 0; i < 10; i++, bit >>= 1) {
         if (exp >= bit) {
-          d *= powers[i];
+          d *= PowersTable.powers[i];
           exp -= bit;
         }
       }
@@ -246,7 +250,7 @@
         int bit = 512;
         for (int i = 0; i < 10; i++, bit >>= 1) {
           if (exp <= -bit) {
-            d *= invPowers[i];
+            d *= PowersTable.invPowers[i];
             exp += bit;
           }
         }
@@ -271,39 +275,47 @@
     return new Double(s);
   }
 
-  private final transient double value;
-
   public Double(double value) {
-    this.value = value;
+    /*
+     * Call to $createDouble(value) must be here so that the method is referenced and not
+     * pruned before new Double(value) is replaced by $createDouble(value) by
+     * RewriteConstructorCallsForUnboxedTypes.
+     */
+    $createDouble(value);
   }
 
   public Double(String s) {
-    value = parseDouble(s);
+    /*
+     * Call to $createDouble(value) must be here so that the method is referenced and not
+     * pruned before new Double(value) is replaced by $createDouble(value) by
+     * RewriteConstructorCallsForUnboxedTypes.
+     */
+    $createDouble(s);
   }
 
   @Override
   public byte byteValue() {
-    return (byte) value;
+    return (byte) doubleValue();
   }
 
   @Override
   public int compareTo(Double b) {
-    return compare(this.value, b.value);
+    return compare(doubleValue(), b.doubleValue());
   }
 
   @Override
-  public double doubleValue() {
-    return value;
-  }
+  public native double doubleValue() /*-{
+    return @javaemul.internal.InternalPreconditions::checkNotNull(Ljava/lang/Object;)(this);
+  }-*/;
 
   @Override
-  public boolean equals(Object o) {
-    return (o instanceof Double) && (((Double) o).value == value);
-  }
+  public native boolean equals(Object o) /*-{
+    return this === o;
+  }-*/;
 
   @Override
   public float floatValue() {
-    return (float) value;
+    return (float) doubleValue();
   }
 
   /**
@@ -317,34 +329,44 @@
    */
   @Override
   public int hashCode() {
-    return hashCode(value);
+    return hashCode(doubleValue());
   }
 
   @Override
   public int intValue() {
-    return (int) value;
+    return (int) doubleValue();
   }
 
   public boolean isInfinite() {
-    return isInfinite(value);
+    return isInfinite(doubleValue());
   }
 
   public boolean isNaN() {
-    return isNaN(value);
+    return isNaN(doubleValue());
   }
 
   @Override
   public long longValue() {
-    return (long) value;
+    return (long) doubleValue();
   }
 
   @Override
   public short shortValue() {
-    return (short) value;
+    return (short) doubleValue();
   }
 
   @Override
   public String toString() {
-    return toString(value);
+    return toString(doubleValue());
   }
+
+  // CHECKSTYLE_OFF: Utility Methods for unboxed Double.
+  static native Double $createDouble(double x) /*-{
+    return x;
+  }-*/;
+
+  static Double $createDouble(String s) {
+    return $createDouble(Double.parseDouble(s));
+  }
+  // CHECKSTYLE_ON: End utility methods
 }
diff --git a/user/super/com/google/gwt/emul/java/lang/String.java b/user/super/com/google/gwt/emul/java/lang/String.java
index cbbe5b9..75cf4ab 100644
--- a/user/super/com/google/gwt/emul/java/lang/String.java
+++ b/user/super/com/google/gwt/emul/java/lang/String.java
@@ -188,104 +188,7 @@
     return replaceStr;
   }
 
-  /**
-   * @skip
-   */
-  static String _String() {
-    return "";
-  }
-
-  /**
-   * @skip
-   */
-  static String _String(byte[] bytes) {
-    return _String(bytes, 0, bytes.length);
-  }
-
-  /**
-   * @skip
-   */
-  static String _String(byte[] bytes, int ofs, int len) {
-    return _String(bytes, ofs, len, EmulatedCharset.UTF_8);
-  }
-
-  /**
-   * @skip
-   */
-  static String _String(byte[] bytes, int ofs, int len, String charsetName)
-      throws UnsupportedEncodingException {
-    return _String(bytes, ofs, len, getCharset(charsetName));
-  }
-
-  /**
-   * @skip
-   */
-  static String _String(byte[] bytes, int ofs, int len, Charset charset) {
-    return valueOf(((EmulatedCharset) charset).decodeString(bytes, ofs, len));
-  }
-
-  /**
-   * @skip
-   */
-  static String _String(byte[] bytes, String charsetName)
-      throws UnsupportedEncodingException {
-    return _String(bytes, 0, bytes.length, charsetName);
-  }
-
-  /**
-   * @skip
-   */
-  static String _String(byte[] bytes, Charset charset)
-      throws UnsupportedEncodingException {
-    return _String(bytes, 0, bytes.length, charset.name());
-  }
-
-  /**
-   * @skip
-   */
-  static String _String(char value[]) {
-    return valueOf(value);
-  }
-
-  /**
-   * @skip
-   */
-  static String _String(char value[], int offset, int count) {
-    return valueOf(value, offset, count);
-  }
-
-  /**
-   * @skip
-   */
-  static String _String(int[] codePoints, int offset, int count) {
-    char[] chars = new char[count * 2];
-    int charIdx = 0;
-    while (count-- > 0) {
-      charIdx += Character.toChars(codePoints[offset++], chars, charIdx);
-    }
-    return valueOf(chars, 0, charIdx);
-  }
-
-  /**
-   * @skip
-   */
-  static String _String(String other) {
-    return other;
-  }
-
-  /**
-   * @skip
-   */
-  static String _String(StringBuffer sb) {
-    return valueOf(sb);
-  }
-
-  /**
-   * @skip
-   */
-  static String _String(StringBuilder sb) {
-    return valueOf(sb);
-  }
+ 
 
   // CHECKSTYLE_ON
 
@@ -316,71 +219,123 @@
   }
 
   public String() {
-    // magic delegation to _String
-    _String();
+    /*
+     * Call to $createString(args) must be here so that the method is referenced and not
+     * pruned before new String(args) is replaced by $createString(args) by
+     * RewriteConstructorCallsForUnboxedTypes.
+     */
+    $createString();
   }
 
   public String(byte[] bytes) {
-    // magic delegation to _String
-    _String(bytes);
+    /*
+     * Call to $createString(args) must be here so that the method is referenced and not
+     * pruned before new String(args) is replaced by $createString(args) by
+     * RewriteConstructorCallsForUnboxedTypes.
+     */
+    $createString(bytes);
   }
 
   public String(byte[] bytes, int ofs, int len) {
-    // magic delegation to _String
-    _String(bytes, ofs, len);
+    /*
+     * Call to $createString(args) must be here so that the method is referenced and not
+     * pruned before new String(args) is replaced by $createString(args) by
+     * RewriteConstructorCallsForUnboxedTypes.
+     */
+    $createString(bytes, ofs, len);
   }
 
   public String(byte[] bytes, int ofs, int len, String charsetName)
       throws UnsupportedEncodingException {
-    // magic delegation to _String
-    _String(bytes, ofs, len, charsetName);
+    /*
+     * Call to $createString(args) must be here so that the method is referenced and not
+     * pruned before new String(args) is replaced by $createString(args) by
+     * RewriteConstructorCallsForUnboxedTypes.
+     */
+    $createString(bytes, ofs, len, charsetName);
   }
 
   public String(byte[] bytes, int ofs, int len, Charset charset) {
-    // magic delegation to _String
-    _String(bytes, ofs, len, charset);
+    /*
+     * Call to $createString(args) must be here so that the method is referenced and not
+     * pruned before new String(args) is replaced by $createString(args) by
+     * RewriteConstructorCallsForUnboxedTypes.
+     */
+    $createString(bytes, ofs, len, charset);
   }
 
   public String(byte[] bytes, String charsetName)
       throws UnsupportedEncodingException {
-    // magic delegation to _String
-    _String(bytes, charsetName);
+    /*
+     * Call to $createString(args) must be here so that the method is referenced and not
+     * pruned before new String(args) is replaced by $createString(args) by
+     * RewriteConstructorCallsForUnboxedTypes.
+     */
+    $createString(bytes, charsetName);
   }
 
   public String(byte[] bytes, Charset charset)
       throws UnsupportedEncodingException {
-    // magic delegation to _String
-    _String(bytes, charset);
+    /*
+     * Call to $createString(args) must be here so that the method is referenced and not
+     * pruned before new String(args) is replaced by $createString(args) by
+     * RewriteConstructorCallsForUnboxedTypes.
+     */
+    $createString(bytes, charset);
   }
 
   public String(char value[]) {
-    // magic delegation to _String
-    _String(value);
+    /*
+     * Call to $createString(args) must be here so that the method is referenced and not
+     * pruned before new String(args) is replaced by $createString(args) by
+     * RewriteConstructorCallsForUnboxedTypes.
+     */
+    $createString(value);
   }
 
   public String(char value[], int offset, int count) {
-    // magic delegation to _String
-    _String(value, offset, count);
+    /*
+     * Call to $createString(args) must be here so that the method is referenced and not
+     * pruned before new String(args) is replaced by $createString(args) by
+     * RewriteConstructorCallsForUnboxedTypes.
+     */
+    $createString(value, offset, count);
   }
 
   public String(int codePoints[], int offset, int count) {
-    // magic delegation to _String
-    _String(codePoints, offset, count);
+    /*
+     * Call to $createString(args) must be here so that the method is referenced and not
+     * pruned before new String(args) is replaced by $createString(args) by
+     * RewriteConstructorCallsForUnboxedTypes.
+     */
+    $createString(codePoints, offset, count);
   }
 
   public String(String other) {
-    // magic delegation to _String
-    _String(other);
+    /*
+     * Call to $createString(args) must be here so that the method is referenced and not
+     * pruned before new String(args) is replaced by $createString(args) by
+     * RewriteConstructorCallsForUnboxedTypes.
+     */
+    $createString(other);
   }
 
   public String(StringBuffer sb) {
-    // magic delegation to _String
-    _String(sb);
+    /*
+     * Call to $createString(args) must be here so that the method is referenced and not
+     * pruned before new String(args) is replaced by $createString(args) by
+     * RewriteConstructorCallsForUnboxedTypes.
+     */
+    $createString(sb);
   }
 
   public String(StringBuilder sb) {
-    // magic delegation to _String
-    _String(sb);
+    /*
+     * Call to $createString(args) must be here so that the method is referenced and not
+     * pruned before new String(args) is replaced by $createString(args) by
+     * RewriteConstructorCallsForUnboxedTypes.
+     */
+    $createString(sb);
   }
 
   @Override
@@ -793,4 +748,67 @@
     }
     return start > 0 || end < length ? substring(start, end) : this;
   }
+
+  // CHECKSTYLE_OFF: Utility Methods for unboxed String.
+
+  static String $createString() {
+    return "";
+  }
+
+  static String $createString(byte[] bytes) {
+    return $createString(bytes, 0, bytes.length);
+  }
+
+  static String $createString(byte[] bytes, int ofs, int len) {
+    return $createString(bytes, ofs, len, EmulatedCharset.UTF_8);
+  }
+
+  static String $createString(byte[] bytes, int ofs, int len, String charsetName)
+      throws UnsupportedEncodingException {
+    return $createString(bytes, ofs, len, String.getCharset(charsetName));
+  }
+
+  static String $createString(byte[] bytes, int ofs, int len, Charset charset) {
+    return String.valueOf(((EmulatedCharset) charset).decodeString(bytes, ofs, len));
+  }
+
+  static String $createString(byte[] bytes, String charsetName)
+      throws UnsupportedEncodingException {
+    return $createString(bytes, 0, bytes.length, charsetName);
+  }
+
+  static String $createString(byte[] bytes, Charset charset)
+      throws UnsupportedEncodingException {
+    return $createString(bytes, 0, bytes.length, charset.name());
+  }
+
+  static String $createString(char value[]) {
+    return String.valueOf(value);
+  }
+
+  static String $createString(char value[], int offset, int count) {
+    return String.valueOf(value, offset, count);
+  }
+
+  static String $createString(int[] codePoints, int offset, int count) {
+    char[] chars = new char[count * 2];
+    int charIdx = 0;
+    while (count-- > 0) {
+      charIdx += Character.toChars(codePoints[offset++], chars, charIdx);
+    }
+    return String.valueOf(chars, 0, charIdx);
+  }
+
+  static String $createString(String other) {
+    return other;
+  }
+
+  static String $createString(StringBuffer sb) {
+    return String.valueOf(sb);
+  }
+
+  static String $createString(StringBuilder sb) {
+    return String.valueOf(sb);
+  }
+  // CHECKSTYLE_ON: end utility methods
 }
diff --git a/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java b/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
index 8996704..427345b 100644
--- a/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
@@ -363,6 +363,42 @@
     byte[] bytes = new byte[10];
     bytes[0] = (byte) '1';
     assertEquals(49, bytes[0]);
+
+    Object[] disguisedApple = apple;
+    expectArrayStoreException(disguisedApple, "Hello");
+    expectArrayStoreException(disguisedApple, true);
+    expectArrayStoreException(disguisedApple, 42.0);
+  }
+
+  private void expectArrayStoreException(Object[] array, Object o) {
+    try {
+      array[0] = o;
+      fail("Expected ArrayStoreException");
+    } catch (ArrayStoreException e) {
+    }
+  }
+
+  public void testUnboxedArrays() {
+    Double[] doubleArray = new Double[1];
+    doubleArray[0] = 42.0;
+
+    expectArrayStoreException(doubleArray, true);
+    expectArrayStoreException(doubleArray, "Hello");
+    expectArrayStoreException(doubleArray, new Integer(23));
+
+    Boolean[] booleanArray = new Boolean[1];
+    booleanArray[0] = true;
+
+    expectArrayStoreException(booleanArray, 42.0);
+    expectArrayStoreException(booleanArray, "Hello");
+    expectArrayStoreException(booleanArray, new Integer(23));
+
+    String[] stringArray = new String[1];
+    stringArray[0] = "Hello";
+
+    expectArrayStoreException(stringArray, 42.0);
+    expectArrayStoreException(stringArray, true);
+    expectArrayStoreException(stringArray, new Integer(23));
   }
 
   /**
diff --git a/user/test/com/google/gwt/emultest/java/lang/BooleanTest.java b/user/test/com/google/gwt/emultest/java/lang/BooleanTest.java
index 4b00b5c..7975d66 100644
--- a/user/test/com/google/gwt/emultest/java/lang/BooleanTest.java
+++ b/user/test/com/google/gwt/emultest/java/lang/BooleanTest.java
@@ -78,4 +78,32 @@
     assertFalse(Boolean.valueOf(false4));
   }
 
+  public void testNPE() {
+   Boolean b = Math.random() < 0 ? Boolean.TRUE : null;
+    try {
+      assertEquals(null, b.booleanValue());
+      fail("Should have thrown exception");
+    } catch (Exception e) {
+    }
+
+    try {
+      boolean bb = b;
+      fail("Should have thrown exception" + bb);
+    } catch (Exception e) {
+    }
+  }
+
+  public void testEqualityNormalizer() {
+    Boolean b = false;
+    if (b != null) {
+      assertEquals(b.booleanValue(), false);
+    } else {
+      fail("false should not evaluate to null");
+    }
+    Object s = "";
+    assertTrue(b != s);
+
+    Object d = 0.0;
+    assertTrue(b != d);
+  }
 }
diff --git a/user/test/com/google/gwt/emultest/java/lang/DoubleTest.java b/user/test/com/google/gwt/emultest/java/lang/DoubleTest.java
index 9da22bf..e18693d 100644
--- a/user/test/com/google/gwt/emultest/java/lang/DoubleTest.java
+++ b/user/test/com/google/gwt/emultest/java/lang/DoubleTest.java
@@ -90,6 +90,35 @@
     assertTrue(Double.compare(500.0, 500.0) == 0);
   }
 
+  public void testNPE() {
+    Double d = Math.random() < 0 ? 42.0 : null;
+    try {
+      assertEquals(null, d.doubleValue());
+      fail("Should have thrown exception");
+    } catch (Exception e) {
+    }
+
+    try {
+      double dd = d;
+      fail("Should have thrown exception" + dd);
+    } catch (Exception e) {
+    }
+  }
+
+  public void testEqualityNormalizer() {
+    Double d = 0.0;
+    if (d != null) {
+      assertEquals(d.doubleValue(), 0.0);
+    } else {
+      fail("0.0 should not evaluate to null");
+    }
+    Object s = "0.0";
+    assertTrue(d != s);
+
+    Object b = Boolean.FALSE;
+    assertTrue(b != s);
+  }
+
   public void testCompareTo() {
     Double zero = new Double(0.0);
     Double three = new Double(3.0);