Disable MemberValuePropagation when -dontoptimize.

Bug: 36800551, 68292416
Change-Id: I28f99c8f8eb037ac28894c9befbc3e68ea6aff58
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index d0c99d5..62ccc67 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -222,12 +222,17 @@
     internal.skipMinification = true;
     assert internal.useTreeShaking;
     internal.useTreeShaking = false;
+
+    // Disable some of R8 optimizations.
     assert internal.inlineAccessors;
     internal.inlineAccessors = false;
     assert internal.removeSwitchMaps;
     internal.removeSwitchMaps = false;
     assert internal.outline.enabled;
     internal.outline.enabled = false;
+    assert internal.propagateMemberValue;
+    internal.propagateMemberValue = false;
+
     internal.outputMode = getOutputMode();
     internal.diagnosticsHandler = getDiagnosticsHandler();
     internal.enableDesugaring = getEnableDesugaring();
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index e93f147..c067e59 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -476,6 +476,7 @@
       internal.inlineAccessors = false;
       internal.removeSwitchMaps = false;
       internal.outline.enabled = false;
+      internal.propagateMemberValue = false;
     }
     assert !internal.skipMinification;
     internal.skipMinification = !useMinification() || !proguardConfiguration.isObfuscating();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index de6d86b..e31c592 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -102,7 +102,8 @@
       assert appInfo.hasSubtyping();
       this.inliner = new Inliner(appInfo.withSubtyping(), graphLense, options);
       this.outliner = new Outliner(appInfo, options);
-      this.memberValuePropagation = new MemberValuePropagation(appInfo);
+      this.memberValuePropagation =
+          options.propagateMemberValue ? new MemberValuePropagation(appInfo) : null;
       this.lensCodeRewriter = new LensCodeRewriter(graphLense, appInfo.withSubtyping());
       if (appInfo.hasLiveness()) {
         this.protoLiteRewriter = new ProtoLitePruner(appInfo.withLiveness());
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 282b37a..d7006af 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -59,6 +59,7 @@
   public boolean inlineAccessors = true;
   public boolean removeSwitchMaps = true;
   public final OutlineOptions outline = new OutlineOptions();
+  public boolean propagateMemberValue = true;
 
   // Number of threads to use while processing the dex files.
   public int numberOfThreads = NOT_SPECIFIED;
diff --git a/src/test/examples/write_only_field/WriteOnlyCls.java b/src/test/examples/write_only_field/WriteOnlyCls.java
new file mode 100644
index 0000000..b68e4aa
--- /dev/null
+++ b/src/test/examples/write_only_field/WriteOnlyCls.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package write_only_field;
+
+public class WriteOnlyCls {
+
+  static class DataObj {
+    final int f1;
+    DataObj(int f1) {
+      this.f1 = f1;
+    }
+  }
+
+  static DataObj static_field = new DataObj(1);
+
+  DataObj instance_field;
+
+  public WriteOnlyCls(int n) {
+    instance_field = new DataObj(n);
+  }
+
+  public static void main(String[] args) {
+    WriteOnlyCls instance = new WriteOnlyCls(2);
+  }
+
+}
diff --git a/src/test/examples/write_only_field/keep-rules-dontoptimize.txt b/src/test/examples/write_only_field/keep-rules-dontoptimize.txt
new file mode 100644
index 0000000..34957b1
--- /dev/null
+++ b/src/test/examples/write_only_field/keep-rules-dontoptimize.txt
@@ -0,0 +1,11 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class write_only_field.WriteOnlyCls {
+  public static void main(...);
+}
+
+-dontoptimize
diff --git a/src/test/examples/write_only_field/keep-rules.txt b/src/test/examples/write_only_field/keep-rules.txt
new file mode 100644
index 0000000..3cc9314
--- /dev/null
+++ b/src/test/examples/write_only_field/keep-rules.txt
@@ -0,0 +1,9 @@
+# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class write_only_field.WriteOnlyCls {
+  public static void main(...);
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
index a1692c8..261fd71 100644
--- a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
@@ -1,3 +1,6 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.classmerging;
 
 import static org.junit.Assert.assertFalse;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
new file mode 100644
index 0000000..5b1c92a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
@@ -0,0 +1,111 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.optimize;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.CompilationException;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.code.Const4;
+import com.android.tools.r8.code.InvokeDirect;
+import com.android.tools.r8.code.IputObject;
+import com.android.tools.r8.code.NewInstance;
+import com.android.tools.r8.code.ReturnVoid;
+import com.android.tools.r8.code.SputObject;
+import com.android.tools.r8.graph.DexCode;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.utils.DexInspector;
+import com.android.tools.r8.utils.DexInspector.ClassSubject;
+import com.android.tools.r8.utils.FileUtils;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class MemberValuePropagationTest {
+  private static final String WRITE_ONLY_FIELD = "write_only_field";
+  private static final Path EXAMPLE_JAR = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR)
+      .resolve(WRITE_ONLY_FIELD + FileUtils.JAR_EXTENSION);
+  private static final Path EXAMPLE_KEEP = Paths.get(ToolHelper.EXAMPLES_DIR)
+      .resolve(WRITE_ONLY_FIELD).resolve("keep-rules.txt");
+  private static final Path DONT_OPTIMIZE = Paths.get(ToolHelper.EXAMPLES_DIR)
+      .resolve(WRITE_ONLY_FIELD).resolve("keep-rules-dontoptimize.txt");
+
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+  @Test
+  public void testWriteOnlyField_putObject_gone() throws Exception {
+    Path processedApp = runR8(EXAMPLE_KEEP);
+    DexInspector inspector = new DexInspector(processedApp);
+    ClassSubject clazz = inspector.clazz(WRITE_ONLY_FIELD + ".WriteOnlyCls");
+    clazz.forAllMethods(methodSubject -> {
+      if (methodSubject.isClassInitializer()) {
+        DexEncodedMethod encodedMethod = methodSubject.getMethod();
+        DexCode code = encodedMethod.getCode().asDexCode();
+        assertEquals(4, code.instructions.length);
+        assertTrue(code.instructions[0] instanceof NewInstance);
+        assertTrue(code.instructions[1] instanceof Const4);
+        assertTrue(code.instructions[2] instanceof InvokeDirect);
+        assertTrue(code.instructions[3] instanceof ReturnVoid);
+      }
+      if (methodSubject.isInstanceInitializer()) {
+        DexEncodedMethod encodedMethod = methodSubject.getMethod();
+        DexCode code = encodedMethod.getCode().asDexCode();
+        assertEquals(4, code.instructions.length);
+        assertTrue(code.instructions[0] instanceof InvokeDirect);
+        assertTrue(code.instructions[1] instanceof NewInstance);
+        assertTrue(code.instructions[2] instanceof InvokeDirect);
+        assertTrue(code.instructions[3] instanceof ReturnVoid);
+      }
+    });
+  }
+
+  @Test
+  public void testWriteOnlyField_dontoptimize() throws Exception {
+    Path processedApp = runR8(DONT_OPTIMIZE);
+    DexInspector inspector = new DexInspector(processedApp);
+    ClassSubject clazz = inspector.clazz(WRITE_ONLY_FIELD + ".WriteOnlyCls");
+    clazz.forAllMethods(methodSubject -> {
+      if (methodSubject.isClassInitializer()) {
+        DexEncodedMethod encodedMethod = methodSubject.getMethod();
+        DexCode code = encodedMethod.getCode().asDexCode();
+        assertEquals(5, code.instructions.length);
+        assertTrue(code.instructions[0] instanceof NewInstance);
+        assertTrue(code.instructions[1] instanceof Const4);
+        assertTrue(code.instructions[2] instanceof InvokeDirect);
+        assertTrue(code.instructions[3] instanceof SputObject);
+        assertTrue(code.instructions[4] instanceof ReturnVoid);
+      }
+      if (methodSubject.isInstanceInitializer()) {
+        DexEncodedMethod encodedMethod = methodSubject.getMethod();
+        DexCode code = encodedMethod.getCode().asDexCode();
+        assertEquals(5, code.instructions.length);
+        assertTrue(code.instructions[0] instanceof InvokeDirect);
+        assertTrue(code.instructions[1] instanceof NewInstance);
+        assertTrue(code.instructions[2] instanceof InvokeDirect);
+        assertTrue(code.instructions[3] instanceof IputObject);
+        assertTrue(code.instructions[4] instanceof ReturnVoid);
+      }
+    });
+  }
+
+  private Path runR8(Path proguardConfig) throws IOException, CompilationException {
+    Path dexOutputDir = temp.newFolder().toPath();
+    ToolHelper.runR8(
+        R8Command.builder()
+            .setOutputPath(dexOutputDir)
+            .addProgramFiles(EXAMPLE_JAR)
+            .addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()))
+            .addProguardConfigurationFiles(proguardConfig)
+            .setMinification(false)
+            .build(),
+        null);
+    return dexOutputDir.resolve("classes.dex");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/DexInspector.java b/src/test/java/com/android/tools/r8/utils/DexInspector.java
index 4b2b6ae..5d7a831 100644
--- a/src/test/java/com/android/tools/r8/utils/DexInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/DexInspector.java
@@ -520,6 +520,8 @@
 
     public abstract boolean isBridge();
 
+    public abstract boolean isInstanceInitializer();
+
     public abstract boolean isClassInitializer();
 
     public abstract DexEncodedMethod getMethod();
@@ -569,6 +571,11 @@
     }
 
     @Override
+    public boolean isInstanceInitializer() {
+      return false;
+    }
+
+    @Override
     public boolean isClassInitializer() {
       return false;
     }
@@ -630,6 +637,11 @@
     }
 
     @Override
+    public boolean isInstanceInitializer() {
+      return dexMethod.isInstanceInitializer();
+    }
+
+    @Override
     public boolean isClassInitializer() {
       return dexMethod.isClassInitializer();
     }