When replacing a field reference with a nullField reference, override the type of the reference
to be the original type. This is important if the original type was a primitive type, because
otherwise the nullField would be of type null and thus incompatible with a primitive type.
Review by: scottb
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2986 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JFieldRef.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JFieldRef.java
index 5abe912..c0b7118 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JFieldRef.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JFieldRef.java
@@ -30,20 +30,33 @@
/**
* The referenced field.
*/
- private JField field;
+ private final JField field;
/**
* This can only be null if the referenced field is static.
*/
private JExpression instance;
+ /**
+ * An overridden type for this reference. Normally the type of a field
+ * reference is the same as the type of the field itself. That default
+ * can be overridden by setting this field.
+ */
+ private final JType overriddenType;
+
public JFieldRef(JProgram program, SourceInfo info, JExpression instance,
JField field, JReferenceType enclosingType) {
+ this(program, info, instance, field, enclosingType, null);
+ }
+
+ public JFieldRef(JProgram program, SourceInfo info, JExpression instance,
+ JField field, JReferenceType enclosingType, JType overriddenType) {
super(program, info, field);
assert (instance != null || field.isStatic());
this.instance = instance;
this.field = field;
this.enclosingType = enclosingType;
+ this.overriddenType = overriddenType;
}
public JReferenceType getEnclosingType() {
@@ -57,7 +70,16 @@
public JExpression getInstance() {
return instance;
}
+
+ @Override
+ public JType getType() {
+ if (overriddenType != null) {
+ return overriddenType;
+ }
+ return super.getType();
+ }
+ @Override
public boolean hasSideEffects() {
// A cross-class reference to a static, non constant field forces clinit
if (field.isStatic()
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
index 36ec779..e3f4063 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
@@ -112,7 +112,7 @@
instance = program.getLiteralNull();
}
JFieldRef fieldRef = new JFieldRef(program, x.getSourceInfo(),
- instance, program.getNullField(), null);
+ instance, program.getNullField(), null, x.getType());
ctx.replaceMe(fieldRef);
}
}
@@ -379,10 +379,12 @@
*/
private boolean myDidChange = false;
+ @Override
public boolean didChange() {
return myDidChange || super.didChange();
}
+ @Override
public void endVisit(JCastOperation x, Context ctx) {
JType argType = x.getExpr().getType();
if (!(x.getCastType() instanceof JReferenceType)
@@ -602,6 +604,7 @@
return true;
}
+ @Override
public boolean visit(JMethod x, Context ctx) {
/*
* Explicitly NOT visiting native methods since we can't infer further
diff --git a/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java b/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
index 50d8803..3a19a3d 100644
--- a/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
@@ -125,6 +125,7 @@
private static final class UninstantiableType {
public Object field;
+ public int intField;
private UninstantiableType() {
}
@@ -140,6 +141,10 @@
private static volatile boolean TRUE = true;
+ private static volatile int volatileInt;
+ private static volatile boolean volatileBoolean;
+ private static volatile UninstantiableType volatileUninstantiableType;
+
private static native void accessUninstantiableField(UninstantiableType u) /*-{
u.@com.google.gwt.dev.jjs.test.CompilerTest$UninstantiableType::field.toString();
}-*/;
@@ -162,6 +167,7 @@
return @com.google.gwt.dev.jjs.test.CompilerTest$SideEffectCauser5::causeClinitSideEffectOnRead;
}-*/;
+ @Override
public String getModuleName() {
return "com.google.gwt.dev.jjs.CompilerSuite";
}
@@ -575,6 +581,7 @@
a = foo;
}
+ @Override
public String toString() {
return new Object() {
@@ -586,6 +593,7 @@
ai = foo;
}
+ @Override
public String toString() {
// this line used to cause ICE due to no synthetic path to bar
bar.valueOf(false);
@@ -837,7 +845,7 @@
assertEquals(-7, x);
}
- public void testUninstantiableNativeAccess() {
+ public void testUninstantiableAccess() {
UninstantiableType u = null;
try {
@@ -851,6 +859,48 @@
fail("Expected JavaScriptException");
} catch (JavaScriptException expected) {
}
+
+ try {
+ volatileUninstantiableType = (UninstantiableType) (new Object());
+ fail("Expected ClassCastException");
+ } catch (ClassCastException expected) {
+ }
+
+ try {
+ volatileInt = u.intField++;
+ fail("Expected NullPointerException (1)");
+ } catch (Exception expected) {
+ }
+
+ try {
+ volatileInt = u.intField--;
+ fail("Expected NullPointerException (2)");
+ } catch (Exception expected) {
+ }
+
+ try {
+ volatileInt = ++u.intField;
+ fail("Expected NullPointerException (3)");
+ } catch (Exception expected) {
+ }
+
+ try {
+ volatileInt = --u.intField;
+ fail("Expected NullPointerException (4)");
+ } catch (Exception expected) {
+ }
+
+ try {
+ u.intField = 0;
+ fail("Expected NullPointerException (5)");
+ } catch (Exception expected) {
+ }
+
+ try {
+ volatileBoolean = u.intField == 0;
+ fail("Expected NullPointerException (6)");
+ } catch (Exception expected) {
+ }
}
private boolean returnFalse() {