Consider native fields that are only written to live. Change-Id: I31b90f5d562867e40bfdc53439f7e3273e7cd543
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java index 9d9faa4..1c404a3 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ControlFlowAnalyzer.java
@@ -161,37 +161,37 @@ if ((x.getOp() == JBinaryOperator.CONCAT || x.getOp() == JBinaryOperator.ASG_CONCAT)) { rescueByConcat(x.getLhs().getType()); rescueByConcat(x.getRhs().getType()); - } else if (x.getOp() == JBinaryOperator.ASG) { - // Don't rescue variables that are merely assigned to and never read - boolean doSkip = false; - JExpression lhs = x.getLhs(); - if (lhs.hasSideEffects() || isVolatileField(lhs)) { - /* - * If the lhs has side effects, skipping it would lose the side - * effect. If the lhs is volatile, also keep it. This behavior - * provides a useful idiom for test cases to prevent code from being - * pruned. - */ - } else if (lhs instanceof JLocalRef) { - // locals are ok to skip - doSkip = true; - } else if (lhs instanceof JParameterRef) { - // parameters are ok to skip - doSkip = true; - } else if (lhs instanceof JFieldRef) { - // fields must rescue the qualifier - doSkip = true; - JFieldRef fieldRef = (JFieldRef) lhs; - JExpression instance = fieldRef.getInstance(); - if (instance != null) { - accept(instance); - } + } + + JExpression lhs = x.getLhs(); + if (x.getOp() != JBinaryOperator.ASG || lhs.hasSideEffects() || isVolatileField(lhs)) { + // Continue the normal visitor process for lhs and rhs. + return true; + } + + // Assignments where the lhs does not have side effects (save for volatile fields) are special + // treated here. The idea is to not consider live a field/local/parameter that is only + // written to. + if (lhs instanceof JLocalRef || lhs instanceof JParameterRef) { + // if the lhs is a local or parameter, do not consider it live just because it is being + // written to. + accept(x.getRhs()); + return false; + } else if (lhs instanceof JFieldRef) { + JFieldRef fieldRef = (JFieldRef) lhs; + JField field = fieldRef.getField(); + if (field.canBeImplementedExternally()) { + // Proceed normally to consider native fields live even if they are only written to. + return true; } - if (doSkip) { - accept(x.getRhs()); - return false; + // Fields that are only written to still need to process their qualifier. + JExpression instance = fieldRef.getInstance(); + if (instance != null) { + accept(instance); } + accept(x.getRhs()); + return false; } return true; }
diff --git a/user/test/com/google/gwt/core/interop/JsExportTest.java b/user/test/com/google/gwt/core/interop/JsExportTest.java index bfdee8a..e250b97 100644 --- a/user/test/com/google/gwt/core/interop/JsExportTest.java +++ b/user/test/com/google/gwt/core/interop/JsExportTest.java
@@ -496,5 +496,4 @@ assertEquals("L", X.m("L")); assertEquals("M", callM("M")); } - }
diff --git a/user/test/com/google/gwt/core/interop/JsPropertyTest.java b/user/test/com/google/gwt/core/interop/JsPropertyTest.java index 95c3432..875c11b 100644 --- a/user/test/com/google/gwt/core/interop/JsPropertyTest.java +++ b/user/test/com/google/gwt/core/interop/JsPropertyTest.java
@@ -498,4 +498,18 @@ assertFalse("Field '" + field + "' should not be exported", hasField(obj, field)); } } + + static class B { + @JsProperty + public String field; + } + private native String getB(B b)/*-{ + return b.field; + }-*/; + + public void testNotReadExpoprtedFieldNotPruned() { + B b = new B(); + b.field = "secret"; + assertEquals("secret", getB(b)); + } }
diff --git a/user/test/com/google/gwt/dev/jjs/test/BasicJsInteropTest.java b/user/test/com/google/gwt/dev/jjs/test/BasicJsInteropTest.java index a375bd9..d840cb9 100644 --- a/user/test/com/google/gwt/dev/jjs/test/BasicJsInteropTest.java +++ b/user/test/com/google/gwt/dev/jjs/test/BasicJsInteropTest.java
@@ -19,11 +19,13 @@ import com.google.gwt.junit.Platform; import com.google.gwt.junit.client.GWTTestCase; +import jsinterop.annotations.JsPackage; import jsinterop.annotations.JsType; /** * Tests for JsInterop that should work with -nogenerateJsInteropExports. */ +@DoNotRunWith(Platform.Devel) public class BasicJsInteropTest extends GWTTestCase { @Override @@ -44,9 +46,27 @@ return { field: "AA" }; }-*/; - @DoNotRunWith(Platform.Devel) - public void testANotPruned() { + public void testNotSetNativeFieldNotNulled() { A a = createA(); assertEquals("aa", a.field.toLowerCase()); } + + /** + * Tests that a native field that is not written to in Java code (but has some initial value in + * the native implementation) is not assumed to be null. + */ + @JsType(isNative = true, name = "Object", namespace = JsPackage.GLOBAL) + static class B { + public String field; + } + + private native String getB(B b)/*-{ + return b.field; + }-*/; + + public void testNotAccessedNativeFieldNotPruned() { + B b = new B(); + b.field = "secret"; + assertEquals("secret", getB(b)); + } }