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 +}