Fixes bugs with for-each element initialization when crossed with auto-unboxing. Also adds tests for implicit boxing/unboxing checking.
Review by: spoon (pair prog)
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2178 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
index b51c265..0956e3f 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
@@ -176,6 +176,7 @@
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
import org.eclipse.jdt.internal.compiler.util.Util;
+import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
@@ -1193,13 +1194,7 @@
JExpression fieldRef = new JFieldRef(program, info, instance, field,
currentClass);
- if (type != field.getType()) {
- // Must be a generic; insert a cast operation.
- JReferenceType toType = (JReferenceType) type;
- return new JCastOperation(program, info, toType, fieldRef);
- } else {
- return fieldRef;
- }
+ return maybeCast(type, fieldRef);
}
JExpression processExpression(InstanceOfExpression x) {
@@ -1247,13 +1242,7 @@
// The arguments come first...
addCallArgs(x.arguments, call, x.binding);
- if (type != method.getType()) {
- // Must be a generic; insert a cast operation.
- JReferenceType toType = (JReferenceType) type;
- return new JCastOperation(program, info, toType, call);
- } else {
- return call;
- }
+ return maybeCast(type, call);
}
@SuppressWarnings("unused")
@@ -1763,17 +1752,26 @@
program.getIndexedMethod("Iterator.hasNext"));
// T elementVar = (T) i$iterator.next();
- JMethodCall nextCall = new JMethodCall(program, info,
+ elementDecl.initializer = new JMethodCall(program, info,
createVariableRef(info, iteratorVar),
program.getIndexedMethod("Iterator.next"));
- JType elementType = elementDecl.getVariableRef().getType();
- if (elementType != nextCall.getType()) {
- // Must be a generic; insert a cast operation.
- elementDecl.initializer = new JCastOperation(program, info,
- elementType, nextCall);
- } else {
- elementDecl.initializer = nextCall;
+ // Perform any implicit reference type casts (due to generics).
+ // Note this occurs before potential unboxing.
+ if (elementVar.getType() != program.getTypeJavaLangObject()) {
+ TypeBinding collectionType;
+ try {
+ Field privateField = ForeachStatement.class.getDeclaredField("collectionElementType");
+ privateField.setAccessible(true);
+ collectionType = (TypeBinding) privateField.get(x);
+ } catch (Exception e) {
+ throw new InternalCompilerException(elementDecl,
+ "Failed to retreive collectionElementType through reflection",
+ e);
+ }
+ JType toType = (JType) typeMap.get(collectionType);
+ assert (toType instanceof JReferenceType);
+ elementDecl.initializer = maybeCast(toType, elementDecl.initializer);
}
body.statements.add(0, elementDecl);
@@ -1782,14 +1780,14 @@
Collections.<JExpressionStatement> emptyList(), body);
}
- // May need to box or unbox the assignment.
+ // May need to box or unbox the element assignment.
if (x.elementVariableImplicitWidening != -1) {
if ((x.elementVariableImplicitWidening & TypeIds.BOXING) != 0) {
elementDecl.initializer = box(elementDecl.initializer,
- (JClassType) elementVar.getType());
+ (JPrimitiveType) elementDecl.initializer.getType());
} else if ((x.elementVariableImplicitWidening & TypeIds.UNBOXING) != 0) {
elementDecl.initializer = unbox(elementDecl.initializer,
- (JPrimitiveType) elementVar.getType());
+ (JClassType) elementDecl.initializer.getType());
}
}
return result;
@@ -2106,12 +2104,14 @@
}
}
- private JExpression box(JExpression toBox, JClassType wrapperType) {
- JPrimitiveType primitiveType = getPrimitiveTypeForWrapperType(wrapperType);
- if (primitiveType == null) {
- throw new InternalCompilerException(toBox,
- "Failed to find primitive type for wrapper type '"
- + wrapperType.getName() + "'", null);
+ private JExpression box(JExpression toBox, JPrimitiveType primitiveType) {
+ // Find the wrapper type for this primitive type.
+ String wrapperTypeName = primitiveType.getWrapperTypeName();
+ JClassType wrapperType = (JClassType) program.getFromTypeMap(wrapperTypeName);
+ if (wrapperType == null) {
+ throw new InternalCompilerException(toBox, "Cannot find wrapper type '"
+ + wrapperTypeName + "' associated with primitive type '"
+ + primitiveType.getName() + "'", null);
}
// Find the correct valueOf() method.
@@ -2145,18 +2145,6 @@
return call;
}
- private JExpression box(JExpression toBox, JPrimitiveType primitiveType) {
- // Find the wrapper type for this primitive type.
- String wrapperTypeName = primitiveType.getWrapperTypeName();
- JClassType wrapperType = (JClassType) program.getFromTypeMap(wrapperTypeName);
- if (wrapperType == null) {
- throw new InternalCompilerException(toBox, "Cannot find wrapper type '"
- + wrapperTypeName + "' associated with primitive type '"
- + primitiveType.getName() + "'", null);
- }
- return box(toBox, wrapperType);
- }
-
private JDeclarationStatement createDeclaration(SourceInfo info,
JLocal local, JExpression value) {
return new JDeclarationStatement(program, info, new JLocalRef(program,
@@ -2380,6 +2368,17 @@
currentFileName);
}
+ private JExpression maybeCast(JType expected, JExpression expression) {
+ if (expected != expression.getType()) {
+ // Must be a generic; insert a cast operation.
+ JReferenceType toType = (JReferenceType) expected;
+ return new JCastOperation(program, expression.getSourceInfo(), toType,
+ expression);
+ } else {
+ return expression;
+ }
+ }
+
/**
* Sometimes a variable reference can be to a local or parameter in an an
* enclosing method. This is a tricky situation to detect. There's no
@@ -2615,18 +2614,6 @@
return unboxCall;
}
- private JExpression unbox(JExpression toUnbox, JPrimitiveType primitiveType) {
- String wrapperTypeName = primitiveType.getWrapperTypeName();
- JClassType wrapperType = (JClassType) program.getFromTypeMap(wrapperTypeName);
- if (wrapperType == null) {
- throw new InternalCompilerException(toUnbox,
- "Cannot find wrapper type '" + wrapperTypeName
- + "' associated with primitive type '"
- + primitiveType.getName() + "'", null);
- }
- return unbox(toUnbox, wrapperType);
- }
-
private void writeEnumValueOfMethod(JEnumType type, JField mapField) {
// return Enum.valueOf(map, name);
JFieldRef mapRef = new JFieldRef(program, null, null, mapField, type);
diff --git a/user/test/com/google/gwt/dev/jjs/test/AutoboxTest.java b/user/test/com/google/gwt/dev/jjs/test/AutoboxTest.java
index 28e88a3..db1d304 100644
--- a/user/test/com/google/gwt/dev/jjs/test/AutoboxTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/AutoboxTest.java
@@ -97,12 +97,17 @@
assertTrue(unbox(unboxedDouble) == unboxedDouble);
}
- /*
- * TODO: Determine whether we fully support the JLS spec in regards to caching
- * of autoboxed values.
- *
- * public void testCaching() { }
+ /**
+ * Verify that .valueOf() methods return identical references for types within
+ * certain ranges.
*/
+ public void testCaching() {
+ assertSame((byte) 3, (byte) 3);
+ assertSame('A', 'A');
+ assertSame((short) 120, (short) 120);
+ assertSame(-13, -13);
+ assertSame(7L, 7L);
+ }
public void testUnboxing() {
boolean boolean_ = boxedBoolean;
@@ -143,6 +148,52 @@
assertTrue(box(boxedDouble).doubleValue() == unboxedDouble);
}
+ public void testUnboxingDifferentType() {
+ {
+ short short_ = boxedByte;
+ assertTrue(short_ == boxedByte.byteValue());
+ int int_ = boxedByte;
+ assertTrue(int_ == boxedByte.byteValue());
+ long long_ = boxedByte;
+ assertTrue(long_ == boxedByte.byteValue());
+ float float_ = boxedByte;
+ assertTrue(float_ == boxedByte.byteValue());
+ double double_ = boxedByte;
+ assertTrue(double_ == boxedByte.byteValue());
+ }
+
+ {
+ int int_ = boxedShort;
+ assertTrue(int_ == boxedShort.shortValue());
+ long long_ = boxedShort;
+ assertTrue(long_ == boxedShort.shortValue());
+ float float_ = boxedShort;
+ assertTrue(float_ == boxedShort.shortValue());
+ double double_ = boxedShort;
+ assertTrue(double_ == boxedShort.shortValue());
+ }
+
+ {
+ int int_ = boxedChar;
+ assertTrue(int_ == boxedChar.charValue());
+ long long_ = boxedChar;
+ assertTrue(long_ == boxedChar.charValue());
+ float float_ = boxedChar;
+ assertTrue(float_ == boxedChar.charValue());
+ double double_ = boxedChar;
+ assertTrue(double_ == boxedChar.charValue());
+ }
+
+ {
+ long long_ = boxedInt;
+ assertTrue(long_ == boxedInt.intValue());
+ float float_ = boxedInt;
+ assertTrue(float_ == boxedInt.intValue());
+ double double_ = boxedInt;
+ assertTrue(double_ == boxedInt.intValue());
+ }
+ }
+
private Boolean box(boolean b) {
return b;
}
diff --git a/user/test/com/google/gwt/dev/jjs/test/EnhancedForLoopTest.java b/user/test/com/google/gwt/dev/jjs/test/EnhancedForLoopTest.java
index ec742e8..e8688b7 100644
--- a/user/test/com/google/gwt/dev/jjs/test/EnhancedForLoopTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/EnhancedForLoopTest.java
@@ -47,6 +47,14 @@
}
assertTrue(out.equals(items));
+ // Tests auto-unboxing.
+ List<Long> itemsL = Arrays.asList(1L, 2L, 3L, 4L, 5L);
+ List<Long> outL = new ArrayList<Long>();
+ for (long l : items) {
+ outL.add(l);
+ }
+ assertTrue(outL.equals(itemsL));
+
int[] unboxedItems = new int[] {1, 2, 3, 4, 5};
out.clear();
@@ -54,6 +62,7 @@
out.add(i);
}
+ // Tests auto-boxing.
for (int i = 0; i < 5; ++i) {
assertTrue(out.get(i).intValue() == unboxedItems[i]);
}
@@ -76,4 +85,4 @@
}
assertTrue(out.equals(items));
}
-}
\ No newline at end of file
+}