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