Don't class-inline synchronized methods (even for CF).

Bug:112295630

Change-Id: I07600cf9229ef3aa258f8980ed393527232ed5fa
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 359223d..30712e6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1055,8 +1055,9 @@
     //      that it is not the instance being initialized.
     //
     boolean instanceInitializer = method.isInstanceInitializer();
-    if (method.accessFlags.isNative() ||
-        (!method.isNonAbstractVirtualMethod() && !instanceInitializer)) {
+    if (method.accessFlags.isNative()
+        || (!method.isNonAbstractVirtualMethod() && !instanceInitializer)
+        || method.accessFlags.isSynchronized()) {
       return;
     }
 
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineSynchronizedTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineSynchronizedTest.java
new file mode 100644
index 0000000..519c273
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineSynchronizedTest.java
@@ -0,0 +1,102 @@
+// Copyright (c) 2019, 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.inliner;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.InvokeInstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class InlineSynchronizedTest extends TestBase {
+
+  private static final List<String> METHOD_NAMES =
+      ImmutableList.of(
+          "println",
+          "normalInlinedSynchronized",
+          "classInlinedSynchronized",
+          "normalInlinedControl",
+          "classInlinedControl");
+
+  @Parameterized.Parameters(name = "Backend: {0}, ClassInlining: {1}")
+  public static Collection data() {
+    return buildParameters(Backend.values(), BooleanUtils.values());
+  }
+
+  private final Backend backend;
+  private final boolean classInlining;
+
+  public InlineSynchronizedTest(Backend backend, boolean classInlining) {
+    this.backend = backend;
+    this.classInlining = classInlining;
+  }
+
+  private void configure(InternalOptions options) {
+    options.enableClassInlining = classInlining;
+  }
+
+  @Test
+  public void test() throws Exception {
+    CodeInspector codeInspector =
+        testForR8(backend)
+            .addProgramClasses(InlineSynchronizedTestClass.class)
+            .addKeepMainRule("com.android.tools.r8.ir.optimize.inliner.InlineSynchronizedTestClass")
+            .addKeepRules("-dontobfuscate")
+            .addOptionsModification(o -> o.enableClassInlining = classInlining)
+            .compile()
+            .inspector();
+
+    ClassSubject classSubject = codeInspector.clazz(InlineSynchronizedTestClass.class);
+    assertThat(classSubject, isPresent());
+    MethodSubject methodSubject = classSubject.mainMethod();
+    Iterator<InstructionSubject> it = methodSubject.iterateInstructions();
+    int[] counts = new int[METHOD_NAMES.size()];
+    while (it.hasNext()) {
+      InstructionSubject instruction = it.next();
+      if (!instruction.isInvoke()) {
+        continue;
+      }
+      DexString invokedName = ((InvokeInstructionSubject) instruction).invokedMethod().name;
+      int idx = METHOD_NAMES.indexOf(invokedName.toString());
+      if (idx >= 0) {
+        ++counts[idx];
+      }
+    }
+    // Synchronized methods can never be inlined.
+    assertCount(counts, "normalInlinedSynchronized", 1);
+    assertCount(counts, "classInlinedSynchronized", 1);
+    // Control methods must be inlined, only the normal one or both, depending on classInlining.
+    assertCount(counts, "normalInlinedControl", 0);
+    assertCount(counts, "classInlinedControl", classInlining ? 0 : 1);
+    // Double check the total.
+    int total = 0;
+    for (int i = 0; i < counts.length; ++i) {
+      total += counts[i];
+    }
+    assertEquals(4, total);
+  }
+
+  private static void assertCount(int counts[], String methodName, int expectedCount) {
+    int idx = METHOD_NAMES.indexOf(methodName);
+    assert idx >= 0;
+    assertEquals(expectedCount, counts[idx]);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineSynchronizedTestClass.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineSynchronizedTestClass.java
new file mode 100644
index 0000000..4e2cae1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineSynchronizedTestClass.java
@@ -0,0 +1,34 @@
+// Copyright (c) 2019, 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.inliner;
+
+class InlineSynchronizedTestClass {
+  private synchronized void normalInlinedSynchronized() {
+    System.out.println("InlineSynchronizedTestClass::normalInlinedSynchronized");
+  }
+
+  public synchronized void classInlinedSynchronized() {
+    System.out.println("InlineSynchronizedTestClass::classInlinedSynchronized");
+  }
+
+  private void normalInlinedControl() {
+    System.out.println("InlineSynchronizedTestClass::normalInlinedControl");
+  }
+
+  public void classInlinedControl() {
+    System.out.println("InlineSynchronizedTestClass::classInlinedControl");
+  }
+
+  public static void main(String[] args) {
+    // Test normal inlining.
+    InlineSynchronizedTestClass testClass = new InlineSynchronizedTestClass();
+    testClass.normalInlinedSynchronized();
+    testClass.normalInlinedControl();
+
+    // Test class-inlining.
+    new InlineSynchronizedTestClass().classInlinedSynchronized();
+    new InlineSynchronizedTestClass().classInlinedControl();
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
index d300a4e..da38a47 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/IfOnAccessModifierTest.java
@@ -12,7 +12,6 @@
 
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.shaking.forceproguardcompatibility.ProguardCompatibilityTestBase;
-import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.codeinspector.ClassSubject;
 import com.android.tools.r8.utils.codeinspector.CodeInspector;
 import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -45,11 +44,6 @@
     return ImmutableList.of(Shrinker.R8_CF, Shrinker.PROGUARD6, Shrinker.R8);
   }
 
-  private void configure(InternalOptions options) {
-    // Disable inlining, otherwise classes can be pruned away if all their methods are inlined.
-    options.enableInlining = false;
-  }
-
   @Test
   public void ifOnPublic_noPublicClassForIfRule() throws Exception {
     List<String> config = ImmutableList.of(
@@ -64,7 +58,7 @@
         "  public <methods>;",
         "}"
     );
-    CodeInspector codeInspector = inspectAfterShrinking(shrinker, CLASSES, config, this::configure);
+    CodeInspector codeInspector = inspectAfterShrinking(shrinker, CLASSES, config);
     ClassSubject classSubject = codeInspector.clazz(ClassForIf.class);
     assertThat(classSubject, isPresent());
     MethodSubject methodSubject = classSubject.method(publicMethod);
@@ -93,7 +87,7 @@
         "  public <methods>;",
         "}"
     );
-    CodeInspector codeInspector = inspectAfterShrinking(shrinker, CLASSES, config, this::configure);
+    CodeInspector codeInspector = inspectAfterShrinking(shrinker, CLASSES, config);
     ClassSubject classSubject = codeInspector.clazz(ClassForIf.class);
     assertThat(classSubject, isPresent());
     MethodSubject methodSubject = classSubject.method(publicMethod);
@@ -126,7 +120,7 @@
         "  !public <methods>;",
         "}"
     );
-    CodeInspector codeInspector = inspectAfterShrinking(shrinker, CLASSES, config, this::configure);
+    CodeInspector codeInspector = inspectAfterShrinking(shrinker, CLASSES, config);
     ClassSubject classSubject = codeInspector.clazz(ClassForIf.class);
     assertThat(classSubject, isPresent());
     MethodSubject methodSubject = classSubject.method(publicMethod);
@@ -160,7 +154,7 @@
         "  public <methods>;",
         "}"
     );
-    CodeInspector codeInspector = inspectAfterShrinking(shrinker, CLASSES, config, this::configure);
+    CodeInspector codeInspector = inspectAfterShrinking(shrinker, CLASSES, config);
     ClassSubject classSubject = codeInspector.clazz(ClassForIf.class);
     assertThat(classSubject, isPresent());
     MethodSubject methodSubject = classSubject.method(publicMethod);
@@ -192,7 +186,7 @@
         "  !public <methods>;",
         "}"
     );
-    CodeInspector codeInspector = inspectAfterShrinking(shrinker, CLASSES, config, this::configure);
+    CodeInspector codeInspector = inspectAfterShrinking(shrinker, CLASSES, config);
     ClassSubject classSubject = codeInspector.clazz(ClassForIf.class);
     assertThat(classSubject, isPresent());
     MethodSubject methodSubject = classSubject.method(publicMethod);