Merge "Test visibility bridge methods after inlining"
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 4db858b..5aa2ee4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -674,6 +674,7 @@
                   registry.registerInvokeSuper(method);
                 }
               }));
+      builder.accessFlags.setBridge();
     }
     builder.accessFlags.setSynthetic();
     // Note that we are not marking this instance obsolete, since it is not: the newly synthesized
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningIntoVisibilityBridgeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningIntoVisibilityBridgeTest.java
new file mode 100644
index 0000000..ab3e71f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningIntoVisibilityBridgeTest.java
@@ -0,0 +1,99 @@
+// 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.ir.optimize.inliner.testclasses.InliningIntoVisibilityBridgeTestClasses.getClassA;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ir.optimize.inliner.testclasses.InliningIntoVisibilityBridgeTestClasses;
+import com.android.tools.r8.ir.optimize.inliner.testclasses.InliningIntoVisibilityBridgeTestClasses.InliningIntoVisibilityBridgeTestClassB;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** Regression test for b/120118197. */
+@RunWith(Parameterized.class)
+public class InliningIntoVisibilityBridgeTest extends TestBase {
+
+  private final boolean neverInline;
+
+  @Parameters(name = "Never inline: {0}")
+  public static Boolean[] data() {
+    return BooleanUtils.values();
+  }
+
+  public InliningIntoVisibilityBridgeTest(boolean neverInline) {
+    this.neverInline = neverInline;
+  }
+
+  @Test
+  public void test() throws Exception {
+    String expectedOutput = StringUtils.lines("Hello world", "Hello world", "Hello world");
+
+    R8TestRunResult result =
+        testForR8(Backend.DEX)
+            .addInnerClasses(InliningIntoVisibilityBridgeTest.class)
+            .addInnerClasses(InliningIntoVisibilityBridgeTestClasses.class)
+            .addKeepMainRule(TestClass.class)
+            .addKeepRules(
+                neverInline
+                    ? ("-neverinline class " + getClassA().getTypeName() + " { method(); }")
+                    : "")
+            .enableMergeAnnotations()
+            .enableProguardTestOptions()
+            .compile()
+            .run(TestClass.class)
+            .assertSuccessWithOutput(expectedOutput);
+
+    // Verify that A.method() is only there if there is an explicit -neverinline rule.
+    {
+      ClassSubject classSubject = result.inspector().clazz(getClassA());
+      assertThat(classSubject, isPresent());
+
+      MethodSubject methodSubject = classSubject.uniqueMethodWithName("method");
+      assertEquals(neverInline, methodSubject.isPresent());
+    }
+
+    // Verify that B.method() is still there, and that B.method() is neither a bridge nor a
+    // synthetic method unless there is an explicit -neverinline rule.
+    {
+      ClassSubject classSubject =
+          result.inspector().clazz(InliningIntoVisibilityBridgeTestClassB.class);
+      assertThat(classSubject, isPresent());
+
+      MethodSubject methodSubject = classSubject.uniqueMethodWithName("method");
+      assertThat(methodSubject, isPresent());
+      assertThat(methodSubject, isRenamed());
+      assertEquals(neverInline, methodSubject.isBridge());
+      assertEquals(neverInline, methodSubject.isSynthetic());
+    }
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      InliningIntoVisibilityBridgeTestClassC obj = new InliningIntoVisibilityBridgeTestClassC();
+
+      // Invoke method three times to prevent the synthetic bridge on InliningIntoVisibilityBridge-
+      // TestClassB from being inlined.
+      obj.method();
+      obj.method();
+      obj.method();
+    }
+  }
+
+  static class InliningIntoVisibilityBridgeTestClassC
+      extends InliningIntoVisibilityBridgeTestClassB {}
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/testclasses/InliningIntoVisibilityBridgeTestClasses.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/testclasses/InliningIntoVisibilityBridgeTestClasses.java
new file mode 100644
index 0000000..afa71fc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/testclasses/InliningIntoVisibilityBridgeTestClasses.java
@@ -0,0 +1,28 @@
+// 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.testclasses;
+
+import com.android.tools.r8.ForceInline;
+import com.android.tools.r8.NeverMerge;
+
+public class InliningIntoVisibilityBridgeTestClasses {
+
+  public static Class<?> getClassA() {
+    return InliningIntoVisibilityBridgeTestClassA.class;
+  }
+
+  @NeverMerge
+  static class InliningIntoVisibilityBridgeTestClassA {
+
+    @ForceInline
+    public static void method() {
+      System.out.println("Hello world");
+    }
+  }
+
+  @NeverMerge
+  public static class InliningIntoVisibilityBridgeTestClassB
+      extends InliningIntoVisibilityBridgeTestClassA {}
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentAnnotationSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentAnnotationSubject.java
index a82d9b0..610b054 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentAnnotationSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentAnnotationSubject.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.utils.codeinspector;
 
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexEncodedAnnotation;
 
 public class AbsentAnnotationSubject extends AnnotationSubject {
@@ -15,7 +16,12 @@
 
   @Override
   public boolean isRenamed() {
-    return false;
+    throw new Unreachable("Cannot determine if an absent annotation has been renamed");
+  }
+
+  @Override
+  public boolean isSynthetic() {
+    throw new Unreachable("Cannot determine if an absent annotation is synthetic");
   }
 
   @Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
index a79ed96..4aead4c 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
@@ -108,6 +108,11 @@
   }
 
   @Override
+  public boolean isSynthetic() {
+    throw new Unreachable("Cannot determine if an absent class is synthetic");
+  }
+
+  @Override
   public boolean isSynthesizedJavaLambdaClass() {
     throw new Unreachable("Cannot determine if an absent class is a synthesized lambda class");
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentFieldSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentFieldSubject.java
index 408fc15..3f8cd81 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentFieldSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentFieldSubject.java
@@ -52,6 +52,11 @@
   }
 
   @Override
+  public boolean isSynthetic() {
+    throw new Unreachable("Cannot determine if an absent field is synthetic");
+  }
+
+  @Override
   public Signature getOriginalSignature() {
     return null;
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
index 5eb2f22..c2ced01 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentMethodSubject.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.naming.MemberNaming.Signature;
 
 public class AbsentMethodSubject extends MethodSubject {
@@ -46,6 +47,11 @@
   }
 
   @Override
+  public boolean isSynthetic() {
+    throw new Unreachable("Cannot determine if an absent method is synthetic");
+  }
+
+  @Override
   public boolean isFinal() {
     throw new Unreachable("Cannot determine if an absent method is final");
   }
@@ -76,7 +82,7 @@
   }
 
   @Override
-  public Signature getOriginalSignature() {
+  public MethodSignature getOriginalSignature() {
     return null;
   }
 
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundAnnotationSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundAnnotationSubject.java
index 546ef59..0a53c91 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundAnnotationSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundAnnotationSubject.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.utils.codeinspector;
 
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexAnnotation;
 import com.android.tools.r8.graph.DexEncodedAnnotation;
 
@@ -26,6 +27,11 @@
   }
 
   @Override
+  public boolean isSynthetic() {
+    throw new Unreachable("Cannot determine if an annotation is synthetic");
+  }
+
+  @Override
   public DexEncodedAnnotation getAnnotation() {
     return annotation.annotation;
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index 1167d8f..8cd5cc7 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -40,6 +40,11 @@
   }
 
   @Override
+  public boolean isSynthetic() {
+    return dexClass.accessFlags.isSynthetic();
+  }
+
+  @Override
   public void forAllMethods(Consumer<FoundMethodSubject> inspection) {
     CodeInspector.forAll(
         dexClass.directMethods(),
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
index 92fc0a0..df23da9 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
@@ -63,6 +63,11 @@
     return clazz.naming != null && !getFinalSignature().name.equals(getOriginalSignature().name);
   }
 
+  @Override
+  public boolean isSynthetic() {
+    return dexField.accessFlags.isSynthetic();
+  }
+
   public TypeSubject type() {
     return new TypeSubject(codeInspector, dexField.field.type);
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index 5643091..d878a86 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -76,6 +76,11 @@
   }
 
   @Override
+  public boolean isSynthetic() {
+    return dexMethod.accessFlags.isSynthetic();
+  }
+
+  @Override
   public boolean isFinal() {
     return dexMethod.accessFlags.isFinal();
   }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
index a9abba1..b2dff57 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
@@ -64,6 +64,28 @@
     return name;
   }
 
+  public static Matcher<MethodSubject> isBridge() {
+    return new TypeSafeMatcher<MethodSubject>() {
+      @Override
+      protected boolean matchesSafely(MethodSubject subject) {
+        return subject.isPresent() && subject.isBridge();
+      }
+
+      @Override
+      public void describeTo(Description description) {
+        description.appendText(" bridge");
+      }
+
+      @Override
+      public void describeMismatchSafely(final MethodSubject subject, Description description) {
+        description
+            .appendText(type(subject) + " ")
+            .appendValue(name(subject))
+            .appendText(" was not");
+      }
+    };
+  }
+
   public static Matcher<Subject> isPresent() {
     return new TypeSafeMatcher<Subject>() {
       @Override
@@ -124,6 +146,28 @@
     };
   }
 
+  public static Matcher<Subject> isSynthetic() {
+    return new TypeSafeMatcher<Subject>() {
+      @Override
+      protected boolean matchesSafely(Subject subject) {
+        return subject.isPresent() && subject.isSynthetic();
+      }
+
+      @Override
+      public void describeTo(Description description) {
+        description.appendText(" synthetic");
+      }
+
+      @Override
+      public void describeMismatchSafely(final Subject subject, Description description) {
+        description
+            .appendText(type(subject) + " ")
+            .appendValue(name(subject))
+            .appendText(" was not");
+      }
+    };
+  }
+
   public static Matcher<ClassSubject> hasDefaultConstructor() {
     return new TypeSafeMatcher<ClassSubject>() {
       @Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
index 9cecb1d..b0d2c9b 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/MethodSubject.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8.utils.codeinspector;
 
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.google.common.collect.Streams;
 import java.util.Iterator;
 import java.util.function.Predicate;
@@ -20,6 +21,9 @@
 
   public abstract boolean isClassInitializer();
 
+  @Override
+  public abstract MethodSignature getOriginalSignature();
+
   public abstract String getOriginalSignatureAttribute();
 
   public abstract String getFinalSignatureAttribute();
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/Subject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/Subject.java
index d93e19a..05d83b7 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/Subject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/Subject.java
@@ -9,4 +9,6 @@
   public abstract boolean isPresent();
 
   public abstract boolean isRenamed();
+
+  public abstract boolean isSynthetic();
 }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/TypeSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/TypeSubject.java
index 6637f34..b230cea 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/TypeSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/TypeSubject.java
@@ -4,6 +4,7 @@
 
 package com.android.tools.r8.utils.codeinspector;
 
+import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexType;
 
 public class TypeSubject extends Subject {
@@ -23,7 +24,12 @@
 
   @Override
   public boolean isRenamed() {
-    return false;
+    throw new Unreachable("Cannot determine if a type is renamed");
+  }
+
+  @Override
+  public boolean isSynthetic() {
+    throw new Unreachable("Cannot determine if a type is synthetic");
   }
 
   public boolean is(String type) {