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();