Use original field/method signatures in Proguard map
Change-Id: I10013606757b636b370c66fae7667287f43a5c50
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 16858af..bca3e00 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -490,6 +490,7 @@
ClassNameMapper classNameMapper =
LineNumberOptimizer.run(
application,
+ appView.getGraphLense(),
namingLens,
options.lineNumberOptimization == LineNumberOptimization.IDENTITY_MAPPING);
timing.end();
diff --git a/src/main/java/com/android/tools/r8/graph/DexField.java b/src/main/java/com/android/tools/r8/graph/DexField.java
index 98c523d..dcab5ae 100644
--- a/src/main/java/com/android/tools/r8/graph/DexField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexField.java
@@ -117,6 +117,10 @@
return clazz;
}
+ public String qualifiedName() {
+ return clazz + "." + name;
+ }
+
@Override
public String toSmaliString() {
return clazz.toSmaliString() + "->" + name + ":" + type.toSmaliString();
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNaming.java b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
index c766c37..094dcaf 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
@@ -134,7 +134,12 @@
}
public static FieldSignature fromDexField(DexField field) {
- return new FieldSignature(field.name.toSourceString(),
+ return fromDexField(field, false);
+ }
+
+ public static FieldSignature fromDexField(DexField field, boolean withQualifiedName) {
+ return new FieldSignature(
+ withQualifiedName ? field.qualifiedName() : field.name.toSourceString(),
field.type.toSourceString());
}
diff --git a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
index 6d12230..66490b2 100644
--- a/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/utils/LineNumberOptimizer.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.ClassNaming;
@@ -35,6 +36,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.function.Function;
import java.util.function.Supplier;
public class LineNumberOptimizer {
@@ -202,7 +204,10 @@
}
public static ClassNameMapper run(
- DexApplication application, NamingLens namingLens, boolean identityMapping) {
+ DexApplication application,
+ GraphLense graphLense,
+ NamingLens namingLens,
+ boolean identityMapping) {
ClassNameMapper.Builder classNameMapperBuilder = ClassNameMapper.builder();
// Collect which files contain which classes that need to have their line numbers optimized.
for (DexProgramClass clazz : application.classes()) {
@@ -230,7 +235,7 @@
addClassToClassNaming(clazz, renamedClassName, onDemandClassNamingBuilder);
// First transfer renamed fields to classNamingBuilder.
- addFieldsToClassNaming(namingLens, clazz, onDemandClassNamingBuilder);
+ addFieldsToClassNaming(graphLense, namingLens, clazz, onDemandClassNamingBuilder);
// Then process the methods, ordered by renamed name.
List<DexString> renamedMethodNames = new ArrayList<>(methodsByRenamedName.keySet());
@@ -261,7 +266,9 @@
}
}
- MethodSignature originalSignature = MethodSignature.fromDexMethod(method.method);
+ DexMethod originalMethod = graphLense.getOriginalMethodSignature(method.method);
+ MethodSignature originalSignature =
+ MethodSignature.fromDexMethod(originalMethod, originalMethod.holder != clazz.type);
DexString obfuscatedNameDexString = namingLens.lookupName(method.method);
String obfuscatedName = obfuscatedNameDexString.toString();
@@ -269,7 +276,8 @@
// Add simple "a() -> b" mapping if we won't have any other with concrete line numbers
if (mappedPositions.isEmpty()) {
// But only if it's been renamed.
- if (obfuscatedNameDexString != method.method.name) {
+ if (obfuscatedNameDexString != originalMethod.name
+ || originalMethod.holder != clazz.type) {
onDemandClassNamingBuilder
.get()
.addMappedRange(null, originalSignature, null, obfuscatedName);
@@ -278,7 +286,16 @@
}
Map<DexMethod, MethodSignature> signatures = new IdentityHashMap<>();
- signatures.put(method.method, originalSignature);
+ signatures.put(originalMethod, originalSignature);
+ Function<DexMethod, MethodSignature> getOriginalMethodSignature =
+ m -> {
+ DexMethod original = graphLense.getOriginalMethodSignature(m);
+ return signatures.computeIfAbsent(
+ original,
+ key ->
+ MethodSignature.fromDexMethod(
+ original, original.holder != clazz.getType()));
+ };
MemberNaming memberNaming = new MemberNaming(originalSignature, obfuscatedName);
onDemandClassNamingBuilder.get().addMemberEntry(memberNaming);
@@ -309,23 +326,14 @@
ClassNaming.Builder classNamingBuilder = onDemandClassNamingBuilder.get();
classNamingBuilder.addMappedRange(
obfuscatedRange,
- signatures.computeIfAbsent(
- firstPosition.method,
- m ->
- MethodSignature.fromDexMethod(
- m, firstPosition.method.holder != clazz.getType())),
+ getOriginalMethodSignature.apply(firstPosition.method),
originalRange,
obfuscatedName);
Position caller = firstPosition.caller;
while (caller != null) {
- Position finalCaller = caller;
classNamingBuilder.addMappedRange(
obfuscatedRange,
- signatures.computeIfAbsent(
- caller.method,
- m ->
- MethodSignature.fromDexMethod(
- m, finalCaller.method.holder != clazz.getType())),
+ getOriginalMethodSignature.apply(caller.method),
Math.max(caller.line, 0), // Prevent against "no-position".
obfuscatedName);
caller = caller.callerPosition;
@@ -381,16 +389,20 @@
}
}
- private static void addFieldsToClassNaming(NamingLens namingLens, DexProgramClass clazz,
+ private static void addFieldsToClassNaming(
+ GraphLense graphLense,
+ NamingLens namingLens,
+ DexProgramClass clazz,
Supplier<Builder> onDemandClassNamingBuilder) {
clazz.forEachField(
dexEncodedField -> {
DexField dexField = dexEncodedField.field;
+ DexField originalField = graphLense.getOriginalFieldSignature(dexField);
DexString renamedName = namingLens.lookupName(dexField);
- if (renamedName != dexField.name) {
- FieldSignature signature =
- new FieldSignature(dexField.name.toString(), dexField.type.toString());
- MemberNaming memberNaming = new MemberNaming(signature, renamedName.toString());
+ if (renamedName != originalField.name || originalField.clazz != clazz.type) {
+ FieldSignature originalSignature =
+ FieldSignature.fromDexField(originalField, originalField.clazz != clazz.type);
+ MemberNaming memberNaming = new MemberNaming(originalSignature, renamedName.toString());
onDemandClassNamingBuilder.get().addMemberEntry(memberNaming);
}
});
diff --git a/src/test/examples/classmerging/ProguardFieldMapTest.java b/src/test/examples/classmerging/ProguardFieldMapTest.java
new file mode 100644
index 0000000..0cbf8a1
--- /dev/null
+++ b/src/test/examples/classmerging/ProguardFieldMapTest.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2018, 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 classmerging;
+
+public class ProguardFieldMapTest {
+
+ public static void main(String[] args) {
+ B b = new B();
+ b.test();
+ }
+
+ public static class A {
+
+ public String f = "A.f";
+ }
+
+ public static class B extends A {
+
+ public void test() {
+ System.out.println(f);
+ }
+ }
+}
diff --git a/src/test/examples/classmerging/ProguardMethodMapTest.java b/src/test/examples/classmerging/ProguardMethodMapTest.java
new file mode 100644
index 0000000..9ce0ad7
--- /dev/null
+++ b/src/test/examples/classmerging/ProguardMethodMapTest.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2018, 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 classmerging;
+
+public class ProguardMethodMapTest {
+
+ public static void main(String[] args) {
+ B b = new B();
+ b.method();
+ }
+
+ public static class A {
+
+ public void method() {
+ System.out.println("In A.method()");
+ }
+ }
+
+ public static class B extends A {
+
+ @Override
+ public void method() {
+ System.out.println("In B.method()");
+ super.method();
+ }
+ }
+}
diff --git a/src/test/examples/classmerging/keep-rules.txt b/src/test/examples/classmerging/keep-rules.txt
index 37a3010..3a70504 100644
--- a/src/test/examples/classmerging/keep-rules.txt
+++ b/src/test/examples/classmerging/keep-rules.txt
@@ -37,6 +37,12 @@
-keep public class classmerging.PinnedParameterTypesTest$TestClass {
public static void method(...);
}
+-keep public class classmerging.ProguardFieldMapTest {
+ public static void main(...);
+}
+-keep public class classmerging.ProguardMethodMapTest {
+ public static void main(...);
+}
-keep public class classmerging.SimpleInterfaceAccessTest {
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 6f3c97a..18e8c83 100644
--- a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.OutputMode;
import com.android.tools.r8.R8Command;
+import com.android.tools.r8.StringConsumer.FileConsumer;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
@@ -34,8 +35,11 @@
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.base.Joiner;
+import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
+import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -45,6 +49,7 @@
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.function.Predicate;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -97,7 +102,7 @@
);
@Test
- public void testClassesHaveBeenMerged() throws Exception {
+ public void testClassesHaveBeenMerged() throws Throwable {
runR8(EXAMPLE_KEEP, this::configure);
// GenericInterface should be merged into GenericInterfaceImpl.
for (String candidate : CAN_BE_MERGED) {
@@ -109,7 +114,7 @@
}
@Test
- public void testClassesHaveNotBeenMerged() throws Exception {
+ public void testClassesHaveNotBeenMerged() throws Throwable {
runR8(DONT_OPTIMIZE, null);
for (String candidate : CAN_BE_MERGED) {
assertTrue(inspector.clazz(candidate).isPresent());
@@ -124,7 +129,7 @@
// Assuming that the chance of observing one of the two orderings is 50%, this test therefore has
// approximately 3% chance of succeeding although there is an issue.
@Test
- public void testCallGraphCycle() throws Exception {
+ public void testCallGraphCycle() throws Throwable {
String main = "classmerging.CallGraphCycleTest";
Path[] programFiles =
new Path[] {
@@ -140,7 +145,7 @@
}
@Test
- public void testConflictInGeneratedName() throws Exception {
+ public void testConflictInGeneratedName() throws Throwable {
String main = "classmerging.ConflictInGeneratedNameTest";
Path[] programFiles =
new Path[] {
@@ -204,14 +209,14 @@
}
@Test
- public void testConflictWasDetected() throws Exception {
+ public void testConflictWasDetected() throws Throwable {
runR8(EXAMPLE_KEEP, this::configure);
assertTrue(inspector.clazz("classmerging.ConflictingInterface").isPresent());
assertTrue(inspector.clazz("classmerging.ConflictingInterfaceImpl").isPresent());
}
@Test
- public void testFieldCollision() throws Exception {
+ public void testFieldCollision() throws Throwable {
String main = "classmerging.FieldCollisionTest";
Path[] programFiles =
new Path[] {
@@ -227,7 +232,7 @@
}
@Test
- public void testLambdaRewriting() throws Exception {
+ public void testLambdaRewriting() throws Throwable {
String main = "classmerging.LambdaRewritingTest";
Path[] programFiles =
new Path[] {
@@ -250,7 +255,7 @@
}
@Test
- public void testMethodCollision() throws Exception {
+ public void testMethodCollision() throws Throwable {
String main = "classmerging.MethodCollisionTest";
Path[] programFiles =
new Path[] {
@@ -275,7 +280,7 @@
}
@Test
- public void testNestedDefaultInterfaceMethodsTest() throws Exception {
+ public void testNestedDefaultInterfaceMethodsTest() throws Throwable {
String main = "classmerging.NestedDefaultInterfaceMethodsTest";
Path[] programFiles =
new Path[] {
@@ -294,7 +299,7 @@
}
@Test
- public void testNestedDefaultInterfaceMethodsWithCDumpTest() throws Exception {
+ public void testNestedDefaultInterfaceMethodsWithCDumpTest() throws Throwable {
String main = "classmerging.NestedDefaultInterfaceMethodsTest";
Path[] programFiles =
new Path[] {
@@ -319,7 +324,7 @@
}
@Test
- public void testPinnedParameterTypes() throws Exception {
+ public void testPinnedParameterTypes() throws Throwable {
String main = "classmerging.PinnedParameterTypesTest";
Path[] programFiles =
new Path[] {
@@ -342,7 +347,213 @@
}
@Test
- public void testSuperCallWasDetected() throws Exception {
+ public void testProguardFieldMap() throws Throwable {
+ String main = "classmerging.ProguardFieldMapTest";
+ Path[] programFiles =
+ new Path[] {
+ CF_DIR.resolve("ProguardFieldMapTest.class"),
+ CF_DIR.resolve("ProguardFieldMapTest$A.class"),
+ CF_DIR.resolve("ProguardFieldMapTest$B.class")
+ };
+
+ // Try without vertical class merging, to check that output is as expected.
+ String expectedProguardMapWithoutClassMerging =
+ Joiner.on(System.lineSeparator())
+ .join(
+ "classmerging.ProguardFieldMapTest -> classmerging.ProguardFieldMapTest:",
+ " 1:2:void main(java.lang.String[]):10:11 -> main",
+ "classmerging.ProguardFieldMapTest$A -> classmerging.ProguardFieldMapTest$A:",
+ " 1:1:void <init>():14:14 -> <init>",
+ " 2:2:void <init>():16:16 -> <init>",
+ "classmerging.ProguardFieldMapTest$B -> classmerging.ProguardFieldMapTest$B:",
+ " 1:1:void <init>():19:19 -> <init>",
+ " 1:1:void test():22:22 -> test");
+ runTestOnInput(
+ main,
+ readProgramFiles(programFiles),
+ Predicates.alwaysTrue(),
+ getProguardConfig(EXAMPLE_KEEP),
+ options -> {
+ configure(options);
+ options.enableClassMerging = false;
+ options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
+ options.proguardMapConsumer =
+ (proguardMap, handler) ->
+ assertThat(proguardMap, containsString(expectedProguardMapWithoutClassMerging));
+ });
+
+ // Try with vertical class merging.
+ String expectedProguardMapWithClassMerging =
+ Joiner.on(System.lineSeparator())
+ .join(
+ "classmerging.ProguardFieldMapTest -> classmerging.ProguardFieldMapTest:",
+ " 1:2:void main(java.lang.String[]):10:11 -> main",
+ "classmerging.ProguardFieldMapTest$B -> classmerging.ProguardFieldMapTest$B:",
+ " java.lang.String classmerging.ProguardFieldMapTest$A.f -> f",
+ " 1:1:void classmerging.ProguardFieldMapTest$A.<init>():14:14 -> <init>",
+ " 1:1:void <init>():19 -> <init>",
+ " 2:2:void classmerging.ProguardFieldMapTest$A.<init>():16:16 -> <init>",
+ " 2:2:void <init>():19 -> <init>",
+ " 1:1:void test():22:22 -> test");
+ Set<String> preservedClassNames =
+ ImmutableSet.of("classmerging.ProguardFieldMapTest", "classmerging.ProguardFieldMapTest$B");
+ runTestOnInput(
+ main,
+ readProgramFiles(programFiles),
+ preservedClassNames::contains,
+ getProguardConfig(EXAMPLE_KEEP),
+ options -> {
+ configure(options);
+ options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
+ options.proguardMapConsumer =
+ (proguardMap, handler) ->
+ assertThat(proguardMap, containsString(expectedProguardMapWithClassMerging));
+ });
+ }
+
+ @Test
+ public void testProguardMethodMap() throws Throwable {
+ String main = "classmerging.ProguardMethodMapTest";
+ Path[] programFiles =
+ new Path[] {
+ CF_DIR.resolve("ProguardMethodMapTest.class"),
+ CF_DIR.resolve("ProguardMethodMapTest$A.class"),
+ CF_DIR.resolve("ProguardMethodMapTest$B.class")
+ };
+
+ // Try without vertical class merging, to check that output is as expected.
+ String expectedProguardMapWithoutClassMerging =
+ Joiner.on(System.lineSeparator())
+ .join(
+ "classmerging.ProguardMethodMapTest -> classmerging.ProguardMethodMapTest:",
+ " 1:2:void main(java.lang.String[]):10:11 -> main",
+ "classmerging.ProguardMethodMapTest$A -> classmerging.ProguardMethodMapTest$A:",
+ " 1:1:void <init>():14:14 -> <init>",
+ " 1:1:void method():17:17 -> method",
+ "classmerging.ProguardMethodMapTest$B -> classmerging.ProguardMethodMapTest$B:",
+ " 1:1:void <init>():21:21 -> <init>",
+ " 1:2:void method():25:26 -> method");
+ runTestOnInput(
+ main,
+ readProgramFiles(programFiles),
+ Predicates.alwaysTrue(),
+ getProguardConfig(EXAMPLE_KEEP),
+ options -> {
+ configure(options);
+ options.enableClassMerging = false;
+ options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
+ options.proguardMapConsumer =
+ (proguardMap, handler) ->
+ assertThat(proguardMap, containsString(expectedProguardMapWithoutClassMerging));
+ });
+
+ // Try with vertical class merging.
+ String expectedProguardMapWithClassMerging =
+ Joiner.on(System.lineSeparator())
+ .join(
+ "classmerging.ProguardMethodMapTest -> classmerging.ProguardMethodMapTest:",
+ " 1:2:void main(java.lang.String[]):10:11 -> main",
+ "classmerging.ProguardMethodMapTest$B -> classmerging.ProguardMethodMapTest$B:",
+ // TODO(christofferqa): Should this be "...<init>():14 -> <init>" to reflect that
+ // A.<init> has been inlined into B.<init>?
+ " 1:1:void classmerging.ProguardMethodMapTest$A.<init>():14:14 -> <init>",
+ // TODO(christofferqa): Should this be " ...<init>():21:21 -> <init>"?
+ " 1:1:void <init>():21 -> <init>",
+ " 1:2:void method():25:26 -> method",
+ " 1:1:void classmerging.ProguardMethodMapTest$A.method():17:17 -> "
+ + "method$classmerging$ProguardMethodMapTest$A");
+ Set<String> preservedClassNames =
+ ImmutableSet.of(
+ "classmerging.ProguardMethodMapTest", "classmerging.ProguardMethodMapTest$B");
+ runTestOnInput(
+ main,
+ readProgramFiles(programFiles),
+ preservedClassNames::contains,
+ getProguardConfig(EXAMPLE_KEEP),
+ options -> {
+ configure(options);
+ options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
+ options.proguardMapConsumer =
+ (proguardMap, handler) ->
+ assertThat(proguardMap, containsString(expectedProguardMapWithClassMerging));
+ });
+ }
+
+ // TODO(christofferqa): The sets appInfo.forceInline and appInfo.neverInline must be rewritten
+ // with the graph lense after vertical class merging.
+ @Ignore
+ @Test
+ public void testProguardMethodMapAfterInlining() throws Throwable {
+ String main = "classmerging.ProguardMethodMapTest";
+ Path[] programFiles =
+ new Path[] {
+ CF_DIR.resolve("ProguardMethodMapTest.class"),
+ CF_DIR.resolve("ProguardMethodMapTest$A.class"),
+ CF_DIR.resolve("ProguardMethodMapTest$B.class")
+ };
+
+ // Try without vertical class merging, to check that output is as expected.
+ String expectedProguardMapWithoutClassMerging =
+ Joiner.on(System.lineSeparator())
+ .join(
+ "classmerging.ProguardMethodMapTest -> classmerging.ProguardMethodMapTest:",
+ " 1:2:void main(java.lang.String[]):10:11 -> main",
+ "classmerging.ProguardMethodMapTest$A -> classmerging.ProguardMethodMapTest$A:",
+ " 1:1:void <init>():14:14 -> <init>",
+ "classmerging.ProguardMethodMapTest$B -> classmerging.ProguardMethodMapTest$B:",
+ " 1:1:void <init>():21:21 -> <init>",
+ " 1:1:void method():25:25 -> method",
+ " 2:2:void classmerging.ProguardMethodMapTest$A.method():17:17 -> method",
+ " 2:2:void method():26 -> method");
+ runTestOnInput(
+ main,
+ readProgramFiles(programFiles),
+ Predicates.alwaysTrue(),
+ getProguardConfig(
+ EXAMPLE_KEEP,
+ "-forceinline class classmerging.ProguardMethodMapTest$A { public void method(); }"),
+ options -> {
+ configure(options);
+ options.enableClassMerging = false;
+ options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
+ options.proguardMapConsumer =
+ (proguardMap, handler) ->
+ assertThat(proguardMap, containsString(expectedProguardMapWithoutClassMerging));
+ });
+
+ // Try with vertical class merging.
+ String expectedProguardMapWithClassMerging =
+ Joiner.on(System.lineSeparator())
+ .join(
+ "classmerging.ProguardMethodMapTest -> classmerging.ProguardMethodMapTest:",
+ " 1:2:void main(java.lang.String[]):10:11 -> main",
+ "classmerging.ProguardMethodMapTest$B -> classmerging.ProguardMethodMapTest$B:",
+ " 1:1:void classmerging.ProguardMethodMapTest$A.<init>():14:14 -> <init>",
+ " 1:1:void <init>():21 -> <init>",
+ " 1:1:void method():25:25 -> method",
+ " 2:2:void classmerging.ProguardMethodMapTest$A.method():17:17 -> method",
+ " 2:2:void method():26 -> method");
+ Set<String> preservedClassNames =
+ ImmutableSet.of(
+ "classmerging.ProguardMethodMapTest", "classmerging.ProguardMethodMapTest$B");
+ runTestOnInput(
+ main,
+ readProgramFiles(programFiles),
+ preservedClassNames::contains,
+ getProguardConfig(
+ EXAMPLE_KEEP,
+ "-forceinline class classmerging.ProguardMethodMapTest$A { public void method(); }"),
+ options -> {
+ configure(options);
+ options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
+ options.proguardMapConsumer =
+ (proguardMap, handler) ->
+ assertThat(proguardMap, containsString(expectedProguardMapWithClassMerging));
+ });
+ }
+
+ @Test
+ public void testSuperCallWasDetected() throws Throwable {
String main = "classmerging.SuperCallRewritingTest";
Path[] programFiles =
new Path[] {
@@ -366,7 +577,7 @@
// targets SubClassThatReferencesSuperMethod.referencedMethod. When running without class
// merging, R8 should not rewrite the invoke-super instruction into invoke-direct.
@Test
- public void testSuperCallNotRewrittenToDirect() throws Exception {
+ public void testSuperCallNotRewrittenToDirect() throws Throwable {
String main = "classmerging.SuperCallRewritingTest";
Path[] programFiles =
new Path[] {
@@ -446,7 +657,7 @@
// }
@Test
@IgnoreForRangeOfVmVersions(from = Version.V5_1_1, to = Version.V6_0_1)
- public void testSuperCallToMergedClassIsRewritten() throws Exception {
+ public void testSuperCallToMergedClassIsRewritten() throws Throwable {
String main = "classmerging.SuperCallToMergedClassIsRewrittenTest";
Set<String> preservedClassNames =
ImmutableSet.of(
@@ -569,7 +780,7 @@
}
@Test
- public void testConflictingInterfaceSignatures() throws Exception {
+ public void testConflictingInterfaceSignatures() throws Throwable {
String main = "classmerging.ConflictingInterfaceSignaturesTest";
Path[] programFiles =
new Path[] {
@@ -588,7 +799,7 @@
// If an exception class A is merged into another exception class B, then all exception tables
// should be updated, and class A should be removed entirely.
@Test
- public void testExceptionTables() throws Exception {
+ public void testExceptionTables() throws Throwable {
String main = "classmerging.ExceptionTest";
Path[] programFiles =
new Path[] {
@@ -624,7 +835,7 @@
}
@Test
- public void testMergeDefaultMethodIntoClass() throws Exception {
+ public void testMergeDefaultMethodIntoClass() throws Throwable {
String main = "classmerging.MergeDefaultMethodIntoClassTest";
Path[] programFiles =
new Path[] {
@@ -658,7 +869,7 @@
}
@Test
- public void testNativeMethod() throws Exception {
+ public void testNativeMethod() throws Throwable {
String main = "classmerging.ClassWithNativeMethodTest";
Path[] programFiles =
new Path[] {
@@ -676,7 +887,7 @@
}
@Test
- public void testNoIllegalClassAccess() throws Exception {
+ public void testNoIllegalClassAccess() throws Throwable {
String main = "classmerging.SimpleInterfaceAccessTest";
Path[] programFiles =
new Path[] {
@@ -701,7 +912,7 @@
}
@Test
- public void testNoIllegalClassAccessWithAccessModifications() throws Exception {
+ public void testNoIllegalClassAccessWithAccessModifications() throws Throwable {
// If access modifications are allowed then SimpleInterface should be merged into
// SimpleInterfaceImpl.
String main = "classmerging.SimpleInterfaceAccessTest";
@@ -736,7 +947,7 @@
// into an invoke-direct instruction after it gets merged into class C. We should add a test that
// checks that this works with and without inlining of the method B.m().
@Test
- public void testRewritePinnedMethod() throws Exception {
+ public void testRewritePinnedMethod() throws Throwable {
String main = "classmerging.RewritePinnedMethodTest";
Path[] programFiles =
new Path[] {
@@ -759,7 +970,7 @@
}
@Test
- public void testTemplateMethodPattern() throws Exception {
+ public void testTemplateMethodPattern() throws Throwable {
String main = "classmerging.TemplateMethodTest";
Path[] programFiles =
new Path[] {
@@ -774,7 +985,7 @@
}
private CodeInspector runTest(
- String main, Path[] programFiles, Predicate<String> preservedClassNames) throws Exception {
+ String main, Path[] programFiles, Predicate<String> preservedClassNames) throws Throwable {
return runTest(main, programFiles, preservedClassNames, getProguardConfig(EXAMPLE_KEEP));
}
@@ -783,14 +994,14 @@
Path[] programFiles,
Predicate<String> preservedClassNames,
String proguardConfig)
- throws Exception {
+ throws Throwable {
return runTestOnInput(
main, readProgramFiles(programFiles), preservedClassNames, proguardConfig);
}
private CodeInspector runTestOnInput(
String main, AndroidApp input, Predicate<String> preservedClassNames, String proguardConfig)
- throws Exception {
+ throws Throwable {
return runTestOnInput(main, input, preservedClassNames, proguardConfig, this::configure);
}
@@ -800,8 +1011,20 @@
Predicate<String> preservedClassNames,
String proguardConfig,
Consumer<InternalOptions> optionsConsumer)
- throws Exception {
- AndroidApp output = compileWithR8(input, proguardConfig, optionsConsumer);
+ throws Throwable {
+ Path proguardMapPath = File.createTempFile("mapping", ".txt", temp.getRoot()).toPath();
+ R8Command.Builder commandBuilder =
+ ToolHelper.prepareR8CommandBuilder(input)
+ .addProguardConfiguration(ImmutableList.of(proguardConfig), Origin.unknown());
+ ToolHelper.allowTestProguardOptions(commandBuilder);
+ AndroidApp output =
+ ToolHelper.runR8(
+ commandBuilder.build(),
+ options -> {
+ optionsConsumer.accept(options);
+ options.proguardMapConsumer =
+ new FileConsumer(proguardMapPath, options.proguardMapConsumer);
+ });
CodeInspector inputInspector = new CodeInspector(input);
CodeInspector outputInspector = new CodeInspector(output);
// Check that all classes in [preservedClassNames] are in fact preserved.