Test visibility bridge methods after inlining
Bug: 120118197
Change-Id: I5c8d16c4b8a052002dd76a3d48f7427814e31da1
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 4934336..97023f2 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) {