Version 1.4.96
The following changes is made directly on the branch since this
version of applymapping is no longer on newer branches.
- Allow for duplicate names for static fields and methods in mapping.
- Rewrite Signature annotation when doing applymapping.
The tests are also present on the master branch and cherry-picked:
Cherry pick: Add test for rewriting signatures when using applymapping
https://r8-review.googlesource.com/c/r8/+/38232
Cherry pick: Test using -applymapping with same static methods in type
hierarchy
https://r8-review.googlesource.com/c/r8/+/38280
Bug: 131532229
Bug: 131710444
Bug: 132593471
Change-Id: Ic89436f623e8ee12321105e1cbb722f1d0686252
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 356d63d..0190082 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "1.4.95";
+ public static final String LABEL = "1.4.96";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapApplier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapApplier.java
index 78dad02..7b7a67f 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapApplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapApplier.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.naming;
+import com.android.tools.r8.graph.AppInfo.ResolutionResult;
+import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationSet;
@@ -20,6 +22,7 @@
import com.android.tools.r8.graph.GraphLense;
import com.android.tools.r8.naming.MemberNaming.FieldSignature;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.signature.GenericSignatureRewriter;
import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.ThrowingConsumer;
@@ -34,12 +37,14 @@
public class ProguardMapApplier {
+ private final AppView<AppInfoWithLiveness> appView;
private final AppInfoWithLiveness appInfo;
private final GraphLense previousLense;
private final SeedMapper seedMapper;
public ProguardMapApplier(AppView<AppInfoWithLiveness> appView, SeedMapper seedMapper) {
assert appView.graphLense().isContextFreeForMethods();
+ this.appView = appView;
this.appInfo = appView.appInfo();
this.previousLense = appView.graphLense();
this.seedMapper = seedMapper;
@@ -61,7 +66,7 @@
private final Map<DexProto, DexProto> protoFixupCache = new IdentityHashMap<>();
MapToLenseConverter() {
- lenseBuilder = new ConflictFreeBuilder();
+ lenseBuilder = new ConflictFreeBuilder(appInfo);
}
private GraphLense run() {
@@ -303,10 +308,26 @@
private final ConflictFreeBuilder lenseBuilder;
private final GraphLense appliedLense;
private final Map<DexProto, DexProto> protoFixupCache = new IdentityHashMap<>();
+ private final GenericSignatureRewriter signatureRewriter;
TreeFixer(GraphLense appliedLense) {
- this.lenseBuilder = new ConflictFreeBuilder();
+ this.lenseBuilder = new ConflictFreeBuilder(appInfo);
this.appliedLense = appliedLense;
+ this.signatureRewriter =
+ new GenericSignatureRewriter(
+ appView,
+ (t, ignored) -> {
+ // The second argument is different from descriptor only when parsing innner type
+ // names, where ignored == NULL. When that is the case the GenericSignatureRewriter
+ // will use the read name and not perform a rewrite. By ignoring the second argument
+ // and always returning the looked-up descriptor, we basically force a rewrite with
+ // the identity name in the null case, which is equivalent, as long as b/110085899
+ // is not reported in the null case.
+ assert t != null;
+ DexType dexType = appliedLense.lookupType(t);
+ assert dexType != null;
+ return dexType.descriptor;
+ });
}
private GraphLense run() {
@@ -317,6 +338,7 @@
// PrgA#foo signature: from LibA to a renamed name.
appInfo.classes().forEach(this::fixClass);
appInfo.libraryClasses().forEach(this::fixClass);
+ signatureRewriter.run();
return lenseBuilder.build(appInfo.dexItemFactory, appliedLense);
}
@@ -442,8 +464,12 @@
}
private static class ConflictFreeBuilder extends GraphLense.Builder {
- ConflictFreeBuilder() {
+
+ AppInfoWithSubtyping appInfo;
+
+ ConflictFreeBuilder(AppInfoWithSubtyping appInfo) {
super();
+ this.appInfo = appInfo;
}
@Override
@@ -458,25 +484,35 @@
}
@Override
- public void map(DexMethod from, DexMethod to) {
+ public void move(DexMethod from, DexMethod to) {
if (methodMap.containsKey(from)) {
String keptName = methodMap.get(from).name.toString();
if (!keptName.equals(to.name.toString())) {
+ ResolutionResult resolutionResult = appInfo.resolveMethod(to.holder, to);
+ // Static methods shadow parent implementations but can be mapped to different names.
+ if (resolutionResult.hasSingleTarget() && resolutionResult.asSingleTarget().isStatic()) {
+ return;
+ }
throw ProguardMapError.keptMethodWasRenamed(from, keptName, to.name.toString());
}
}
- super.map(from, to);
+ super.move(from, to);
}
@Override
- public void map(DexField from, DexField to) {
+ public void move(DexField from, DexField to) {
if (fieldMap.containsKey(from)) {
String keptName = fieldMap.get(from).name.toString();
if (!keptName.equals(to.name.toString())) {
+ DexEncodedField field = appInfo.resolveFieldOn(to.type, to);
+ // Static fields shadow parent implementations but can be mapped to different names.
+ if (field != null && field.isStatic()) {
+ return;
+ }
throw ProguardMapError.keptFieldWasRenamed(from, keptName, to.name.toString());
}
}
- super.map(from, to);
+ super.move(from, to);
}
// Helper to determine whether to apply the class mapping on-the-fly or applied already.
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
index 20f4bc2..3f331ff 100644
--- a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
+++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
@@ -19,10 +19,11 @@
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
-import com.google.common.collect.Maps;
+import com.google.common.collect.ImmutableMap;
import java.lang.reflect.GenericSignatureFormatError;
import java.util.Map;
import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -30,15 +31,24 @@
private final AppView<AppInfoWithLiveness> appView;
private final AppInfoWithLiveness appInfo;
- private final Map<DexType, DexString> renaming;
+ private final BiFunction<DexType, DexString, DexString> renaming;
private final Reporter reporter;
+ private final GenericSignatureCollector genericSignatureCollector =
+ new GenericSignatureCollector();
+ private final GenericSignatureParser<DexType> genericSignatureParser =
+ new GenericSignatureParser<>(genericSignatureCollector);
public GenericSignatureRewriter(AppView<AppInfoWithLiveness> appView) {
- this(appView, Maps.newIdentityHashMap());
+ this(appView, ImmutableMap.of());
}
public GenericSignatureRewriter(
AppView<AppInfoWithLiveness> appView, Map<DexType, DexString> renaming) {
+ this(appView, renaming::getOrDefault);
+ }
+
+ public GenericSignatureRewriter(
+ AppView<AppInfoWithLiveness> appView, BiFunction<DexType, DexString, DexString> renaming) {
this.appView = appView;
this.appInfo = appView.appInfo();
this.renaming = renaming;
@@ -46,10 +56,6 @@
}
public void run() {
- final GenericSignatureCollector genericSignatureCollector = new GenericSignatureCollector();
- final GenericSignatureParser<DexType> genericSignatureParser =
- new GenericSignatureParser<>(genericSignatureCollector);
-
for (DexClass clazz : appInfo.classes()) {
clazz.annotations =
rewriteGenericSignatures(
@@ -164,7 +170,7 @@
if (appInfo.wasPruned(type)) {
type = appInfo.dexItemFactory.objectType;
}
- DexString renamedDescriptor = renaming.getOrDefault(type, type.descriptor);
+ DexString renamedDescriptor = renaming.apply(type, type.descriptor);
renamedSignature.append(getClassBinaryNameFromDescriptor(renamedDescriptor.toString()));
return type;
}
@@ -181,9 +187,9 @@
+ name));
String enclosingRenamedBinaryName =
getClassBinaryNameFromDescriptor(
- renaming.getOrDefault(enclosingType, enclosingType.descriptor).toString());
+ renaming.apply(enclosingType, enclosingType.descriptor).toString());
type = appView.graphLense().lookupType(type);
- DexString renamedDescriptor = renaming.get(type);
+ DexString renamedDescriptor = renaming.apply(type, null);
if (renamedDescriptor != null) {
// Pick the renamed inner class from the fully renamed binary name.
String fullRenamedBinaryName =
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingSameStaticNameTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingSameStaticNameTest.java
new file mode 100644
index 0000000..d74692b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingSameStaticNameTest.java
@@ -0,0 +1,67 @@
+// 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.naming.applymapping;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/** This test reproduces b/131532229 on d8-1.4. */
+@RunWith(Parameterized.class)
+public class ApplyMappingSameStaticNameTest extends TestBase {
+
+ public static final String pgMap =
+ StringUtils.lines(
+ A.class.getTypeName() + " -> " + A.class.getTypeName() + ":",
+ " boolean[] jacocoInit() -> a",
+ " int f1 -> c",
+ B.class.getTypeName() + " -> " + B.class.getTypeName() + ":",
+ " boolean[] jacocoInit() -> b",
+ " int f1 -> d");
+
+ public static class A {
+ public static boolean[] a() {
+ return null;
+ }
+
+ public static int c = 1;
+ }
+
+ public static class B extends A {
+ public static boolean[] b() {
+ return null;
+ }
+
+ public static int d = 2;
+ }
+
+ public static class C {}
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Backend[] data() {
+ return Backend.values();
+ }
+
+ private Backend backend;
+
+ public ApplyMappingSameStaticNameTest(Backend backend) {
+ this.backend = backend;
+ }
+
+ @Test
+ public void test_b131532229() throws CompilationFailedException {
+ testForR8(backend)
+ .noTreeShaking()
+ .noMinification()
+ .addLibraryClasses(A.class, B.class)
+ .addLibraryFiles(runtimeJar(backend))
+ .addProgramClasses(C.class)
+ .addApplyMapping(pgMap)
+ .compile();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingSignatureTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingSignatureTest.java
new file mode 100644
index 0000000..f36eef2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingSignatureTest.java
@@ -0,0 +1,73 @@
+// 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.naming.applymapping;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.StringContains.containsString;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import java.io.IOException;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/** This test reproduces b/132593471 on d8-1.4. */
+@RunWith(Parameterized.class)
+public class ApplyMappingSignatureTest extends TestBase {
+
+ public interface A {}
+
+ public static class SignatureTest {
+ Set<A> field;
+
+ public static void main(String[] args) {
+ System.out.print("HELLO");
+ }
+ }
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Backend[] data() {
+ return Backend.values();
+ }
+
+ private Backend backend;
+
+ public ApplyMappingSignatureTest(Backend backend) {
+ this.backend = backend;
+ }
+
+ @Test
+ public void testApplyMappingWithSignatureRenaming()
+ throws IOException, CompilationFailedException, ExecutionException {
+ R8TestBuilder r8TestBuilder =
+ testForR8(backend)
+ .addProgramClasses(A.class, SignatureTest.class)
+ .addKeepAllClassesRule()
+ .addKeepMainRule(SignatureTest.class)
+ .addKeepAttributes("Signature", "InnerClasses", "EnclosingMethod")
+ .addApplyMapping(A.class.getTypeName() + " -> foo:");
+ if (backend == Backend.DEX) {
+ r8TestBuilder.setMinApi(ToolHelper.getMinApiLevelForDexVm());
+ }
+ r8TestBuilder
+ .run(SignatureTest.class)
+ .assertSuccessWithOutput("HELLO")
+ .inspect(
+ inspector -> {
+ ClassSubject clazz = inspector.clazz(SignatureTest.class);
+ assertThat(clazz, isPresent());
+ FieldSubject field = clazz.uniqueFieldWithName("field");
+ assertThat(field, isPresent());
+ assertThat(field.getSignatureAnnotationValue(), containsString("foo"));
+ });
+ }
+}