Restrict class inliner to maintain visible side-effects.

In the case of class inlining an object from a static field,
make sure that methods inlined do not modify the object state
as those updates could be visible to other accesses to the
object in the static field.

Bug: 160942326

Change-Id: I3088e56c8184a9b2e4a409e67284852b09ccc412
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerEligibilityInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerEligibilityInfo.java
index 8d178a3..35eaaf5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerEligibilityInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerEligibilityInfo.java
@@ -22,13 +22,16 @@
   final OptionalBool returnsReceiver;
 
   final boolean hasMonitorOnReceiver;
+  final boolean modifiesInstanceFields;
 
   public ClassInlinerEligibilityInfo(
       List<Pair<Invoke.Type, DexMethod>> callsReceiver,
       OptionalBool returnsReceiver,
-      boolean hasMonitorOnReceiver) {
+      boolean hasMonitorOnReceiver,
+      boolean modifiesInstanceFields) {
     this.callsReceiver = callsReceiver;
     this.returnsReceiver = returnsReceiver;
     this.hasMonitorOnReceiver = hasMonitorOnReceiver;
+    this.modifiesInstanceFields = modifiesInstanceFields;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index 72be050..3824962 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -1065,6 +1065,12 @@
         // We will not be able to remove the monitor instruction afterwards.
         return false;
       }
+      if (eligibility.modifiesInstanceFields) {
+        // The static instance could be accessed from elsewhere. Therefore, we cannot
+        // allow side-effects to be removed and therefore cannot class inline method
+        // calls that modifies the instance.
+        return false;
+      }
     }
 
     // If the method returns receiver and the return value is actually
@@ -1215,8 +1221,8 @@
     }
 
     if (root.isStaticGet()) {
-      // If we are class inlining a singleton instance from a static-get, then we don't the value of
-      // the fields.
+      // If we are class inlining a singleton instance from a static-get, then we don't know
+      // the value of the fields.
       if (parameterUsage.hasFieldRead) {
         return false;
       }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index 8a25765..cc24603 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -192,6 +192,7 @@
     List<Pair<Invoke.Type, DexMethod>> callsReceiver = new ArrayList<>();
     boolean seenSuperInitCall = false;
     boolean seenMonitor = false;
+    boolean modifiesInstanceFields = false;
 
     AliasedValueConfiguration configuration =
         AssumeAndCheckCastAliasedValueConfiguration.getInstance();
@@ -220,6 +221,7 @@
               if (isReceiverAlias.test(instancePutInstruction.value())) {
                 return;
               }
+              modifiesInstanceFields = true;
             }
             DexField field = insn.asFieldInstruction().getField();
             if (appView.appInfo().resolveField(field).isFailedOrUnknownResolution()) {
@@ -293,7 +295,8 @@
         new ClassInlinerEligibilityInfo(
             callsReceiver,
             new ClassInlinerReceiverAnalysis(appView, definition, code).computeReturnsReceiver(),
-            seenMonitor || synchronizedVirtualMethod));
+            seenMonitor || synchronizedVirtualMethod,
+            modifiesInstanceFields));
   }
 
   private void identifyParameterUsages(