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));
+  }
 }