Enable combining class name mappers to build partial results
Bug: b/234758957
Bug: b/201666766
Change-Id: I455d73a2c1a0b56ddbe83942dfe15f1de80a0537
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
index 9f01beb..a3dbc6c 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -65,7 +65,6 @@
private ImmutableMap<String, ClassNamingForNameMapper> buildClassNameMappings() {
ImmutableMap.Builder<String, ClassNamingForNameMapper> builder = ImmutableMap.builder();
- builder.orderEntriesByValue(Comparator.comparing(x -> x.originalName));
mapping.forEach(
(renamedName, valueBuilder) -> builder.put(renamedName, valueBuilder.build()));
return builder.build();
@@ -242,6 +241,34 @@
return originalSourceFiles.get(typeName);
}
+ public ClassNameMapper combine(ClassNameMapper other) {
+ ImmutableMap.Builder<String, ClassNamingForNameMapper> builder = ImmutableMap.builder();
+ Map<String, ClassNamingForNameMapper> otherClassMappings = other.getClassNameMappings();
+ for (Entry<String, ClassNamingForNameMapper> mappingEntry : classNameMappings.entrySet()) {
+ ClassNamingForNameMapper otherMapping = otherClassMappings.get(mappingEntry.getKey());
+ if (otherMapping == null) {
+ builder.put(mappingEntry);
+ } else {
+ builder.put(mappingEntry.getKey(), mappingEntry.getValue().combine(otherMapping));
+ }
+ }
+ otherClassMappings.forEach(
+ (otherMappingClass, otherMapping) -> {
+ // Collisions are handled above so only take non-existing mappings.
+ if (!classNameMappings.containsKey(otherMappingClass)) {
+ builder.put(otherMappingClass, otherMapping);
+ }
+ });
+ otherClassMappings.forEach(builder::put);
+ Set<MapVersionMappingInformation> newMapVersions = new LinkedHashSet<>(getMapVersions());
+ newMapVersions.addAll(other.getMapVersions());
+ Map<String, String> newSourcesFiles = new HashMap<>(originalSourceFiles);
+ // This will overwrite existing source files but the chance of that happening should be very
+ // slim.
+ newSourcesFiles.putAll(other.originalSourceFiles);
+ return new ClassNameMapper(builder.build(), newMapVersions, newSourcesFiles);
+ }
+
@Override
public boolean hasMapping(DexType type) {
String decoded = descriptorToJavaType(type.descriptor.toString());
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
index c1e5de7..352c555 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
@@ -265,6 +265,40 @@
return mappedRangesByRenamedName.get(renamedName);
}
+ public boolean isEmpty() {
+ return methodMembers.isEmpty() && fieldMembers.isEmpty();
+ }
+
+ public ClassNamingForNameMapper combine(ClassNamingForNameMapper otherMapping) {
+ if (!originalName.equals(otherMapping.originalName)) {
+ throw new RuntimeException(
+ "Cannot combine mapping for "
+ + renamedName
+ + " because it maps back to both "
+ + originalName
+ + " and "
+ + otherMapping.originalName
+ + ".");
+ }
+ if (!renamedName.equals(otherMapping.renamedName)) {
+ throw new RuntimeException(
+ "Cannot combine mapping for "
+ + originalName
+ + " because it maps forward to both "
+ + originalName
+ + " and "
+ + otherMapping.originalName
+ + ".");
+ }
+ if (this.isEmpty()) {
+ return otherMapping;
+ } else if (otherMapping.isEmpty()) {
+ return this;
+ } else {
+ throw new RuntimeException("R8 Retrace do not support merging of partial class mappings.");
+ }
+ }
+
@Override
public MemberNaming lookup(Signature renamedSignature) {
if (renamedSignature.kind() == SignatureKind.METHOD) {
diff --git a/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java b/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java
index 4e56011..d94ad7b 100644
--- a/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java
@@ -56,17 +56,20 @@
@Test
public void roundTripTest() throws IOException {
- ClassNameMapper firstMapper = ClassNameMapper.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP));
- ClassNameMapper secondMapper = ClassNameMapper.mapperFromString(firstMapper.toString());
+ ClassNameMapper firstMapper =
+ ClassNameMapper.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP)).sorted();
+ ClassNameMapper secondMapper =
+ ClassNameMapper.mapperFromString(firstMapper.toString()).sorted();
Assert.assertEquals(firstMapper, secondMapper);
}
@Test
public void roundTripTestWithLeadingBOM() throws IOException {
- ClassNameMapper firstMapper = ClassNameMapper.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP));
+ ClassNameMapper firstMapper =
+ ClassNameMapper.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP)).sorted();
assertTrue(firstMapper.toString().charAt(0) != StringUtils.BOM);
ClassNameMapper secondMapper =
- ClassNameMapper.mapperFromString(StringUtils.BOM + firstMapper.toString());
+ ClassNameMapper.mapperFromString(StringUtils.BOM + firstMapper.toString()).sorted();
assertTrue(secondMapper.toString().charAt(0) != StringUtils.BOM);
Assert.assertEquals(firstMapper, secondMapper);
byte[] bytes = Files.readAllBytes(Paths.get(ROOT, EXAMPLE_MAP));
@@ -76,7 +79,7 @@
assertEquals(0xef, Byte.toUnsignedLong(bytes[0]));
assertEquals(0xbb, Byte.toUnsignedLong(bytes[1]));
assertEquals(0xbf, Byte.toUnsignedLong(bytes[2]));
- ClassNameMapper thirdMapper = ClassNameMapper.mapperFromFile(mapFileWithBOM);
+ ClassNameMapper thirdMapper = ClassNameMapper.mapperFromFile(mapFileWithBOM).sorted();
assertTrue(thirdMapper.toString().charAt(0) != StringUtils.BOM);
Assert.assertEquals(firstMapper, thirdMapper);
}
@@ -93,7 +96,8 @@
"" + StringUtils.BOM,
StringUtils.BOM + " " + StringUtils.BOM);
for (String whitespace : ws) {
- ClassNameMapper firstMapper = ClassNameMapper.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP));
+ ClassNameMapper firstMapper =
+ ClassNameMapper.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP)).sorted();
assertTrue(firstMapper.toString().charAt(0) != StringUtils.BOM);
StringBuilder buildWithWhitespace = new StringBuilder();
char prevChar = '\0';
@@ -114,13 +118,13 @@
prevChar = c;
}
ClassNameMapper secondMapper =
- ClassNameMapper.mapperFromString(buildWithWhitespace.toString());
+ ClassNameMapper.mapperFromString(buildWithWhitespace.toString()).sorted();
assertFalse(firstMapper.toString().contains("" + StringUtils.BOM));
Assert.assertEquals(firstMapper, secondMapper);
byte[] bytes = Files.readAllBytes(Paths.get(ROOT, EXAMPLE_MAP));
assertNotEquals(0xef, Byte.toUnsignedLong(bytes[0]));
Path mapFileWithBOM = writeTextToTempFile(StringUtils.BOM + firstMapper.toString());
- ClassNameMapper thirdMapper = ClassNameMapper.mapperFromFile(mapFileWithBOM);
+ ClassNameMapper thirdMapper = ClassNameMapper.mapperFromFile(mapFileWithBOM).sorted();
assertTrue(thirdMapper.toString().charAt(0) != StringUtils.BOM);
Assert.assertEquals(firstMapper, thirdMapper);
}