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) & (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);