Fixes issue #2139; web mode was throwing an ArrayStoreException trying to store a JavaScriptObject into Object[].
Reviewer's comments:
- I wish there was a less tricky way, something with an overt check for Object instead of relying on 0 being special, but having something that works is a big improvement on the current state. Anything should be storable into an Object, even a native JSO that has no typeId!
Found by: sanjiv.jivan
Review by: spoon (postmortem)
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@1961 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java
index 37d1be2..c340f98 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/CastNormalizer.java
@@ -48,10 +48,25 @@
/**
* Replace cast and instanceof operations with calls to the Cast class. Depends
- * on {@link com.google.gwt.dev.jjs.impl.CatchBlockNormalizer},
- * {@link com.google.gwt.dev.jjs.impl.CompoundAssignmentNormalizer}, and
- * {@link com.google.gwt.dev.jjs.impl.JavaScriptObjectCaster} having already
- * run.
+ * on {@link CatchBlockNormalizer}, {@link CompoundAssignmentNormalizer}, and
+ * {@link JsoDevirtualizer} having already run.
+ *
+ * <p>
+ * Object and String always get a typeId of 1 and 2, respectively. 0 is reserved
+ * as the typeId for any classes that can never be instance type of a successful
+ * dynamic cast.
+ * </p>
+ * <p>
+ * Object and String always get a queryId of 0 and 1, respectively. The 0
+ * queryId always means "always succeeds". In practice, we never generate an
+ * explicit cast with a queryId of 0; it is only used for array store checking,
+ * where the 0 queryId means that anything can be stored into an Object[].
+ * </p>
+ * <p>
+ * JavaScriptObject and subclasses have no typeId at all. JavaScriptObject has a
+ * queryId of -1, which again is only used for array store checking, to ensure
+ * that a non-JSO is not stored into a JavaScriptObject[].
+ * </p>
*/
public class CastNormalizer {
@@ -59,7 +74,7 @@
Set<JClassType> alreadyRan = new HashSet<JClassType>();
private Map<JReferenceType, Set<JReferenceType>> queriedTypes = new IdentityHashMap<JReferenceType, Set<JReferenceType>>();
- private int nextQueryId = 1; // 0 is reserved
+ private int nextQueryId = 0;
private final List<JArrayType> instantiatedArrayTypes = new ArrayList<JArrayType>();
private List<JClassType> classes = new ArrayList<JClassType>();
private List<JsonObject> jsonObjects = new ArrayList<JsonObject>();
@@ -72,6 +87,10 @@
}
}
+ // Reserve query id 0 for java.lang.Object (for array stores on JSOs).
+ recordCastInternal(program.getTypeJavaLangObject(),
+ program.getTypeJavaLangObject());
+
// Reserve query id 1 for java.lang.String to facilitate the mashup case.
// Multiple GWT modules need to modify String's prototype the same way.
recordCastInternal(program.getTypeJavaLangString(),
@@ -227,12 +246,7 @@
}
}
- // Object a typeId, to force String to have an id of 2.
- if (yesSet == null && type != program.getTypeJavaLangObject()) {
- return; // won't satisfy anything
- }
-
- // use an array to sort my yes set
+ // Use a sparse array to sort my yes set.
JReferenceType[] yesArray = new JReferenceType[nextQueryId];
if (yesSet != null) {
for (JReferenceType yesType : yesSet) {
@@ -240,9 +254,10 @@
}
}
- // create a sparse lookup object
+ // Create a sparse lookup object.
JsonObject jsonObject = new JsonObject(program);
- for (int i = 0; i < nextQueryId; ++i) {
+ // Start at 1; 0 is Object and always true.
+ for (int i = 1; i < nextQueryId; ++i) {
if (yesArray[i] != null) {
JIntLiteral labelExpr = program.getLiteralInt(i);
JIntLiteral valueExpr = program.getLiteralInt(1);
@@ -251,6 +266,16 @@
}
}
+ /*
+ * Don't add an entry for empty answer sets, except for Object and String
+ * which require typeIds.
+ */
+ if (jsonObject.propInits.isEmpty()
+ && type != program.getTypeJavaLangObject()
+ && type != program.getTypeJavaLangString()) {
+ return;
+ }
+
// add an entry for me
classes.add(type);
jsonObjects.add(jsonObject);
diff --git a/user/test/com/google/gwt/dev/jjs/test/JsoTest.java b/user/test/com/google/gwt/dev/jjs/test/JsoTest.java
index 21bd74d..685b120 100644
--- a/user/test/com/google/gwt/dev/jjs/test/JsoTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/JsoTest.java
@@ -59,7 +59,7 @@
protected Foo() {
}
-
+
public final native String getFoo() /*-{
return this.foo;
}-*/;
@@ -262,6 +262,11 @@
fail("Expected ArrayStoreException");
} catch (ArrayStoreException expected) {
}
+
+ objArray = new Object[1];
+ objArray[0] = makeJSO();
+ objArray[0] = makeFoo();
+ objArray[0] = makeBar();
}
public void testBasic() {