Allow Java 7+ casts from Object to primitive types.
Java 7 allows casting from Object to primitive types as in
Object o = 5;
int i = (int) o;
Bug: issue 8749.
Change-Id: Id02964ead4dc5e79a36d4ad184a388286d766311
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 b6d1667..0220f05 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
@@ -2802,9 +2802,23 @@
}
private JExpression unbox(JExpression original, int implicitConversion) {
- int typeId = implicitConversion & TypeIds.COMPILE_TYPE_MASK;
+ int compileTypeId = implicitConversion & TypeIds.COMPILE_TYPE_MASK;
ClassScope scope = curClass.scope;
- BaseTypeBinding primitiveType = (BaseTypeBinding) TypeBinding.wellKnownType(scope, typeId);
+ TypeBinding targetBinding = TypeBinding.wellKnownType(scope, compileTypeId);
+ if (!(targetBinding instanceof BaseTypeBinding)) {
+ // Direct cast from non-boxed-type reference type to a primitive type,
+ // wrap with a cast operation of the (boxed) expected type.
+ int runtimeTypeId = (implicitConversion & TypeIds.IMPLICIT_CONVERSION_MASK) >> 4;
+ TypeBinding runtimeTypeBinding = TypeBinding.wellKnownType(scope, runtimeTypeId);
+ ReferenceBinding boxType = (ReferenceBinding) scope.boxing(runtimeTypeBinding);
+ original =
+ new JCastOperation(original.getSourceInfo(), typeMap.get(boxType), original);
+ targetBinding = runtimeTypeBinding;
+ assert (targetBinding instanceof BaseTypeBinding);
+ }
+
+ BaseTypeBinding primitiveType = (BaseTypeBinding) targetBinding;
+
ReferenceBinding boxType = (ReferenceBinding) scope.boxing(primitiveType);
char[] selector = CharOperation.concat(primitiveType.simpleName, VALUE);
MethodBinding valueMethod =
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/Java7AstTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/Java7AstTest.java
index 28183b2..f0b8481 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/Java7AstTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/Java7AstTest.java
@@ -61,7 +61,6 @@
"}");
}
-
public void testCompileDiamondOperator() throws Exception {
addSnippetImport("java.util.List");
addSnippetImport("java.util.ArrayList");
@@ -70,6 +69,46 @@
"List<String> l = new ArrayList<>();");
}
+ public void testCastingToPrimitiveTypes() throws UnableToCompleteException {
+ assertEqualBlock(
+ "Object o = null; byte s = (byte) ((Byte) o).byteValue();",
+ "Object o = null; byte s = (byte) o;");
+
+ assertEqualBlock(
+ "Object o = null; short s = (short) ((Short) o).shortValue();",
+ "Object o = null; short s = (short) o;");
+
+ assertEqualBlock(
+ "Object o = null; int s = (int) ((Integer) o).intValue();",
+ "Object o = null; int s = (int) o;");
+
+ assertEqualBlock(
+ "Object o = null; long s = (long) ((Long) o).longValue();",
+ "Object o = null; long s = (long) o;");
+
+ assertEqualBlock(
+ "Object o = null; float s = (float) ((Float) o).floatValue();",
+ "Object o = null; float s = (float) o;");
+
+ assertEqualBlock(
+ "Object o = null; double s = (double) ((Double) o).doubleValue();",
+ "Object o = null; double s = (double) o;");
+
+ assertEqualBlock(
+ "Object o = null; char s = (char) ((Character) o).charValue();",
+ "Object o = null; char s = (char) o;");
+
+ assertEqualBlock(
+ "Object o = null; char s = (char) ((Character) o).charValue();",
+ "Object o = null; char s = (char) o;");
+
+ // This is the expected behaviour, however JDT 3.8.3 emits a compiler error.
+ // TODO(rluble): uncomment when JDT is updated.
+ // assertEqualBlock(
+ // "Number o = 1; int s = (int) ((Integer) o).intValue();",
+ // "Number o= 1 ; int s = (int) o;");
+ }
+
private void addAll(Resource... sourceFiles) {
for (Resource sourceFile : sourceFiles) {
sourceOracle.addOrReplace(sourceFile);
diff --git a/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java7Test.java b/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java7Test.java
index 26a32ac..631248a 100644
--- a/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java7Test.java
+++ b/user/test-super/com/google/gwt/dev/jjs/super/com/google/gwt/dev/jjs/test/Java7Test.java
@@ -1,12 +1,12 @@
/*
* Copyright 2013 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
@@ -304,4 +304,56 @@
} catch (E1 x) {
}
}
+
+ private Object unoptimizableId(Object o) {
+ if (Math.random() > -10) {
+ return o;
+ }
+ return null;
+ }
+
+ public void testPrimitiveCastsFromObject() {
+ Object o = unoptimizableId((byte) 2);
+ assertEquals((byte) 2, (byte) o);
+ o = unoptimizableId((short) 3);
+ assertEquals((short) 3, (short) o);
+ o = unoptimizableId(1);
+ assertEquals(1, (int) o);
+ o = unoptimizableId(1L);
+ assertEquals(1L, (long) o);
+ o = unoptimizableId(0.1f);
+ assertEquals(0.1f, (float) o);
+ o = unoptimizableId(0.1);
+ assertEquals(0.1, (double) o);
+ o = unoptimizableId(true);
+ assertEquals(true, (boolean) o);
+ o = unoptimizableId('a');
+ assertEquals('a', (char) o);
+ // Test cast from supers.
+ // TODO(rluble): enable these after JDT upgrade as the currenct JDT will
+ // give compilation errors.
+ // Number n = (Number) unoptimizableId(5);
+ // assertEquals(5, (int) n);
+ // Serializable s = (Serializable) unoptimizableId(6);
+ // assertEquals(6, (int) s);
+ // Comparable<Integer> c = (Comparable<Integer>) unoptimizableId(7);
+ // assertEquals(7, (int) c);
+
+ // Failing casts.
+ try {
+ Object boxedChar = unoptimizableId('a');
+ boolean b = (boolean) boxedChar;
+ fail("Should have thrown a ClassCastException");
+ } catch (ClassCastException e) {
+ // Expected.
+ }
+
+ try {
+ Object string = unoptimizableId("string");
+ int n = (int) string;
+ fail("Should have thrown a ClassCastException");
+ } catch (ClassCastException e) {
+ // Expected.
+ }
+ }
}
diff --git a/user/test/com/google/gwt/dev/jjs/CompilerSuite.java b/user/test/com/google/gwt/dev/jjs/CompilerSuite.java
index 656f31f..71841ed 100644
--- a/user/test/com/google/gwt/dev/jjs/CompilerSuite.java
+++ b/user/test/com/google/gwt/dev/jjs/CompilerSuite.java
@@ -89,6 +89,8 @@
suite.addTestSuite(InitialLoadSequenceTest.class);
suite.addTestSuite(InnerClassTest.class);
suite.addTestSuite(InnerOuterSuperTest.class);
+ // Java7Test cannot be the first one in a suite. It uses a hack
+ // to avoid executing if not in a Java 7+ environment.
suite.addTestSuite(Java7Test.class);
suite.addTestSuite(JavaAccessFromJavaScriptTest.class);
suite.addTestSuite(JsniConstructorTest.class);
diff --git a/user/test/com/google/gwt/dev/jjs/test/Java7Test.java b/user/test/com/google/gwt/dev/jjs/test/Java7Test.java
index e463279..93b9c69 100644
--- a/user/test/com/google/gwt/dev/jjs/test/Java7Test.java
+++ b/user/test/com/google/gwt/dev/jjs/test/Java7Test.java
@@ -67,4 +67,7 @@
public void testAddSuppressedExceptions() {
}
+
+ public void testPrimitiveCastsFromObject() {
+ }
}