Fix ArrayStoreException in assignments to an element of an array of a single jso interface type.
Addresses issue 6448
Review at http://gwt-code-reviews.appspot.com/1470801
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10414 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java
index 6fe6034..3e25125 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java
@@ -104,7 +104,21 @@
if (elementType instanceof JReferenceType) {
JReferenceType elementRefType = (JReferenceType) elementType;
elementType = elementRefType.getUnderlyingType();
+ if (program.typeOracle.isEffectivelyJavaScriptObject(elementRefType)) {
+ /*
+ * treat types that are effectively JSO's as JSO's, for the purpose of
+ * castability checking
+ */
+ elementRefType = program.getJavaScriptObject();
+ }
elementQueryId = program.getQueryId(elementRefType);
+ if (program.typeOracle.isDualJsoInterface(elementRefType)) {
+ /*
+ * invert the queryId, to indicate dual castability for JSO's and the
+ * Java type represented by the inverse of the queryId
+ */
+ elementQueryId *= -1;
+ }
}
return new JsQueryType(sourceInfo, elementType, elementQueryId);
}
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 348e5da..60c809f 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
@@ -180,14 +180,46 @@
}
/**
- * Performs an array assignment, checking for valid index and type.
+ * Performs an array assignment, after validating the type of the value being
+ * stored. The form of the type check depends on the value of queryId, as
+ * follows:
+ * <p>
+ * If the queryId is > 0, this indicates a normal cast check should be
+ * performed, using the queryId as the cast destination type.
+ * JavaScriptObjects cannot be stored in this case.
+ * <p>
+ * If the queryId == 0, this is the cast target for the Object type, in which
+ * case all types can be stored, including JavaScriptObject.
+ * <p>
+ * If the queryId == -1, this indicates that only JavaScriptObjects can be
+ * stored (-1 is the cast target for JavaScriptObject, by convention).
+ * <p>
+ * If the queryId is < -1, this indicates that both JavaScriptObjects, and
+ * Java types can be stored. In the case of Java types, the inverse of the
+ * queryId is used for castability testing. This case is provided to support
+ * arrays declared with an interface type, which has dual implementations
+ * (i.e. interface types which have both Java and JavaScriptObject
+ * implementations).
+ * <p>
+ * Note, by convention, a queryId of 1 is reserved for String, which is a
+ * final class, and can't implement an interface, and thus, it's inverse, -1,
+ * can safely be interpreted as a special case, as stated above.
+ * <p>
+ * Attempting to store an object that cannot satisfy the castability check
+ * throws an {@link ArrayStoreException}.
*/
public static Object setCheck(Array array, int index, Object value) {
if (value != null) {
if (array.queryId > 0 && !Cast.canCastUnsafe(value, array.queryId)) {
+ // value must be castable to queryId
throw new ArrayStoreException();
- }
- if (array.queryId < 0 && Cast.isJavaObject(value)) {
+ } else if (array.queryId == -1 && Cast.isJavaObject(value)) {
+ // value must be a JavaScriptObject
+ throw new ArrayStoreException();
+ } else if (array.queryId < -1 && !Cast.isJavaScriptObject(value)
+ && !Cast.canCastUnsafe(value, -array.queryId)) {
+ // value must be a JavaScriptObject, or else castable to the inverse of
+ // queryId
throw new ArrayStoreException();
}
}
@@ -283,9 +315,10 @@
protected Class<?> arrayClass = null;
/**
- * The necessary cast target for objects stored into this array. Attempting to
- * store an object that cannot satisfy the query id throws and
- * {@link ArrayStoreException}.
+ * A representation of the necessary cast target for objects stored into this
+ * array.
+ *
+ * @see #setCheck
*/
protected int queryId = 0;
}
diff --git a/user/test/com/google/gwt/dev/jjs/test/SingleJsoImplTest.java b/user/test/com/google/gwt/dev/jjs/test/SingleJsoImplTest.java
index ae07751..9381aa8 100644
--- a/user/test/com/google/gwt/dev/jjs/test/SingleJsoImplTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/SingleJsoImplTest.java
@@ -442,6 +442,28 @@
}
/**
+ * An interface that has dual JSO and non-JSO implementations.
+ */
+ interface DualSimple {
+ String a();
+ }
+
+ static class JavaDualSimple implements DualSimple {
+ public String a() {
+ return "object";
+ }
+ }
+
+ static final class JsoDualSimple extends JavaScriptObject implements DualSimple {
+ protected JsoDualSimple() {
+ }
+
+ public String a() {
+ return "jso";
+ }
+ }
+
+ /**
* Ensure that a Java-only implementation of a SingleJsoImpl interface works.
*/
static class SimpleOnlyJava implements SimpleOnlyJavaInterface {
@@ -514,6 +536,115 @@
return "com.google.gwt.dev.jjs.CompilerSuite";
}
+ /*
+ * These "testAssign*" tests below are inspired by the issue reported here:
+ *
+ * @see http://code.google.com/p/google-web-toolkit/issues/detail?id=6448
+ */
+ public void testAssignToDualJavaJsoImplInterfaceArray() {
+ int i = 0;
+ DualSimple[] dualSimples = new DualSimple[10];
+
+ DualSimple dualSimple = (DualSimple) JavaScriptObject.createObject();
+ dualSimples[i] = dualSimple;
+ assertEquals("jso", dualSimples[i++].a());
+
+ JsoDualSimple jsoDualSimple = (JsoDualSimple) JavaScriptObject.createObject();
+ dualSimples[i] = jsoDualSimple;
+ assertEquals("jso", dualSimples[i++].a());
+
+ DualSimple javaDualSimple = new JavaDualSimple();
+ dualSimples[i] = javaDualSimple;
+ assertEquals("object", dualSimples[i++].a());
+
+ Object[] objects = dualSimples;
+ objects[i++] = dualSimple;
+ objects[i++] = jsoDualSimple;
+ objects[i++] = javaDualSimple;
+ try {
+ objects[i++] = new Object();
+ fail("ArrayStoreException expected");
+ } catch (ArrayStoreException expected) {
+ }
+ }
+
+ public void testAssignToJsoArray() {
+ int i = 0;
+ JsoDualSimple[] jsoDualSimples = new JsoDualSimple[10];
+
+ JsoDualSimple jsoDualSimple = (JsoDualSimple) JavaScriptObject.createObject();
+ jsoDualSimples[i] = jsoDualSimple;
+ assertEquals("jso", jsoDualSimples[i++].a());
+
+ DualSimple dualSimple = (DualSimple) JavaScriptObject.createObject();
+ jsoDualSimples[i] = (JsoDualSimple) dualSimple;
+ assertEquals("jso", jsoDualSimples[i++].a());
+
+ DualSimple[] dualSimples = jsoDualSimples;
+ try {
+ DualSimple javaDualSimple = new JavaDualSimple();
+ dualSimples[i++] = javaDualSimple;
+ fail("ArrayStoreException expected");
+ } catch (ArrayStoreException expected) {
+ }
+ }
+
+ public void testAssignToJavaArray() {
+ int i = 0;
+ JavaDualSimple[] javaDualSimples = new JavaDualSimple[10];
+
+ JavaDualSimple javaDualSimple = new JavaDualSimple();
+ javaDualSimples[i] = javaDualSimple;
+ assertEquals("object", javaDualSimples[i++].a());
+
+ DualSimple[] dualSimples = javaDualSimples;
+ try {
+ DualSimple jsoDualSimple = (DualSimple) JavaScriptObject.createObject();
+ dualSimples[i++] = jsoDualSimple;
+ fail("ArrayStoreException expected");
+ } catch (ArrayStoreException expected) {
+ }
+ }
+
+ public void testAssignToObjectArray() {
+ int i = 0;
+ Object[] objects = new Object[10];
+
+ Simple simple = (Simple) JavaScriptObject.createObject();
+ objects[i++] = simple;
+
+ JsoSimple jsoSimple = (JsoSimple) JavaScriptObject.createObject();
+ objects[i++] = jsoSimple;
+
+ JavaScriptObject jso = JavaScriptObject.createObject();
+ objects[i++] = jso;
+
+ Object object = new Object();
+ objects[i++] = object;
+
+ Integer integer = new Integer(1);
+ objects[i++] = integer;
+ }
+
+ public void testAssignToSingleJsoImplInterfaceArray() {
+ int i = 0;
+ Simple[] simples = new Simple[10];
+ Simple simple = (Simple) JavaScriptObject.createObject();
+ simples[i] = simple;
+ assertEquals("a", simples[i++].a());
+
+ Simple jsoSimple = (JsoSimple) JavaScriptObject.createObject();
+ simples[i] = jsoSimple;
+ assertEquals("a", simples[i++].a());
+
+ Object[] objects = simples;
+ try {
+ objects[i++] = new Object();
+ fail("ArrayStoreException expected");
+ } catch (ArrayStoreException expected) {
+ }
+ }
+
public void testCallsToInnerTypes() {
CallsMethodInInnerType a = (CallsMethodInInnerType) JavaScriptObject.createObject();
InnerType i = (InnerType) JavaScriptObject.createObject();