[Compose] Compose mapping information
Bug: b/241763080
Bug: b/242682464
Bug: b/242673239
Change-Id: I3580cedc32a33a3990149dbe5cee3d7ee0ffa0cc
diff --git a/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
index 403aa85..6e1bc00 100644
--- a/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
+++ b/src/main/java/com/android/tools/r8/naming/ComposingBuilder.java
@@ -9,7 +9,14 @@
import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRangesOfName;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
+import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
+import com.android.tools.r8.naming.mappinginformation.MappingInformation;
+import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation;
+import com.android.tools.r8.naming.mappinginformation.RewriteFrameMappingInformation.ThrowsCondition;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.BiMapContainer;
import com.android.tools.r8.utils.ChainableStringConsumer;
+import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.SegmentTree;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
@@ -26,14 +33,26 @@
/**
* To ensure we can do alpha renaming of classes and members without polluting the existing
- * mapping, we use a committed map that we update for each class name mapping. That allows us to
+ * mappping, we use a committed map that we update for each class name mapping. That allows us to
* rename to existing renamed names as long as these are also renamed later in the map.
*/
private final Map<String, ComposingClassBuilder> committed = new HashMap<>();
private Map<String, ComposingClassBuilder> current = new HashMap<>();
+ private MapVersionMappingInformation currentMapVersion = null;
+
+ private final ComposingSharedData sharedData = new ComposingSharedData();
+
public void compose(ClassNameMapper classNameMapper) throws MappingComposeException {
+ MapVersionMappingInformation thisMapVersion = classNameMapper.getFirstMapVersionInformation();
+ if (thisMapVersion != null) {
+ if (currentMapVersion == null
+ || currentMapVersion.getMapVersion().isLessThan(thisMapVersion.getMapVersion())) {
+ currentMapVersion = thisMapVersion;
+ }
+ }
+ sharedData.patchupMappingInformation(classNameMapper);
for (ClassNamingForNameMapper classMapping : classNameMapper.getClassNameMappings().values()) {
compose(classMapping);
}
@@ -45,7 +64,7 @@
ComposingClassBuilder composingClassBuilder = committed.get(originalName);
String renamedName = classMapping.renamedName;
if (composingClassBuilder == null) {
- composingClassBuilder = new ComposingClassBuilder(originalName, renamedName);
+ composingClassBuilder = new ComposingClassBuilder(originalName, renamedName, sharedData);
} else {
composingClassBuilder.setRenamedName(renamedName);
committed.remove(originalName);
@@ -88,6 +107,10 @@
List<ComposingClassBuilder> classBuilders = new ArrayList<>(committed.values());
classBuilders.sort(Comparator.comparing(ComposingClassBuilder::getOriginalName));
StringBuilder sb = new StringBuilder();
+ // TODO(b/241763080): Keep preamble of mapping files"
+ if (currentMapVersion != null) {
+ sb.append("# ").append(currentMapVersion.serialize()).append("\n");
+ }
ChainableStringConsumer wrap = ChainableStringConsumer.wrap(sb::append);
for (ComposingClassBuilder classBuilder : classBuilders) {
classBuilder.write(wrap);
@@ -95,6 +118,37 @@
return sb.toString();
}
+ public static class ComposingSharedData {
+
+ /**
+ * RewriteFrameInformation contains condition clauses that are bound to the residual program. As
+ * a result of that, we have to patch up the conditions when we compose new class mappings.
+ */
+ private final List<RewriteFrameMappingInformation> mappingInformationToPatchUp =
+ new ArrayList<>();
+
+ private void patchupMappingInformation(ClassNameMapper classNameMapper) {
+ BiMapContainer<String, String> obfuscatedToOriginalMapping =
+ classNameMapper.getObfuscatedToOriginalMapping();
+ for (RewriteFrameMappingInformation rewriteMappingInfo : mappingInformationToPatchUp) {
+ rewriteMappingInfo
+ .getConditions()
+ .forEach(
+ rewriteCondition -> {
+ ThrowsCondition throwsCondition = rewriteCondition.asThrowsCondition();
+ if (throwsCondition != null) {
+ String originalName = throwsCondition.getClassReference().getTypeName();
+ String obfuscatedName = obfuscatedToOriginalMapping.inverse.get(originalName);
+ if (obfuscatedName != null) {
+ throwsCondition.setClassReferenceInternal(
+ Reference.classFromTypeName(obfuscatedName));
+ }
+ }
+ });
+ }
+ }
+ }
+
public static class ComposingClassBuilder {
private static final String INDENTATION = " ";
@@ -108,10 +162,14 @@
// starting position uniquely identifies a method. If no position is given there can be only
// one method since any shrinker should put in line numbers for overloads.
private final Map<String, SegmentTree<List<MappedRange>>> methodMembers = new HashMap<>();
+ private List<MappingInformation> additionalMappingInfo = null;
+ private final ComposingSharedData sharedData;
- private ComposingClassBuilder(String originalName, String renamedName) {
+ private ComposingClassBuilder(
+ String originalName, String renamedName, ComposingSharedData sharedData) {
this.originalName = originalName;
this.renamedName = renamedName;
+ this.sharedData = sharedData;
}
public void setRenamedName(String renamedName) {
@@ -127,6 +185,13 @@
}
public void compose(ClassNamingForNameMapper mapper) throws MappingComposeException {
+ List<MappingInformation> newMappingInfo = mapper.getAdditionalMappingInfo();
+ if (newMappingInfo != null && !newMappingInfo.isEmpty()) {
+ if (additionalMappingInfo == null) {
+ additionalMappingInfo = new ArrayList<>();
+ }
+ additionalMappingInfo.addAll(newMappingInfo);
+ }
composeFieldNamings(mapper);
composeMethodNamings(mapper);
}
@@ -232,6 +297,12 @@
&& !isInlineMappedRange(mappedRanges, i)) {
break;
}
+ for (MappingInformation mappingInformation : thisMappedRange.getAdditionalMappingInfo()) {
+ if (mappingInformation.isRewriteFrameMappingInformation()) {
+ sharedData.mappingInformationToPatchUp.add(
+ mappingInformation.asRewriteFrameMappingInformation());
+ }
+ }
seenMappedRanges.add(thisMappedRange);
lastSeen = thisMappedRange;
}
@@ -405,12 +476,17 @@
new Range(newOriginalStart, newOriginalStart + newMinifiedRange.span() - 1);
}
}
- newComposedRanges.add(
+ MappedRange computedRange =
new MappedRange(
newMinifiedRange,
existingMappedRange.signature,
newOriginalRange,
- newMappedRange.renamedName));
+ newMappedRange.renamedName);
+ existingMappedRange
+ .getAdditionalMappingInfo()
+ .forEach(
+ info -> computedRange.addMappingInformation(info, ConsumerUtils.emptyConsumer()));
+ newComposedRanges.add(computedRange);
}
}
@@ -426,7 +502,10 @@
public void write(ChainableStringConsumer consumer) {
consumer.accept(originalName).accept(" -> ").accept(renamedName).accept(":\n");
- // TODO(b/241763080): Support mapping information.
+ if (additionalMappingInfo != null) {
+ additionalMappingInfo.forEach(
+ info -> consumer.accept("# " + info.serialize()).accept("\n"));
+ }
writeFields(consumer);
writeMethods(consumer);
}
@@ -438,7 +517,9 @@
}
fieldNamings.sort(Comparator.comparing(MemberNaming::getOriginalName));
fieldNamings.forEach(
- naming -> consumer.accept(INDENTATION).accept(naming.toString()).accept("\n"));
+ naming -> {
+ consumer.accept(INDENTATION).accept(naming.toString()).accept("\n");
+ });
}
private void writeMethods(ChainableStringConsumer consumer) {
@@ -462,8 +543,12 @@
signatureToMappedRanges
.get(key)
.forEach(
- mappedRange ->
- consumer.accept(INDENTATION).accept(mappedRange.toString()).accept("\n"));
+ mappedRange -> {
+ consumer.accept(INDENTATION).accept(mappedRange.toString()).accept("\n");
+ for (MappingInformation info : mappedRange.getAdditionalMappingInfo()) {
+ consumer.accept(INDENTATION).accept("# ").accept(info.serialize()).accept("\n");
+ }
+ });
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/RewriteFrameMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/RewriteFrameMappingInformation.java
index 82b2a0b..00eb800 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/RewriteFrameMappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/RewriteFrameMappingInformation.java
@@ -162,7 +162,7 @@
static final String FUNCTION_NAME = "throws";
- private final ClassReference classReference;
+ private ClassReference classReference;
private ThrowsCondition(ClassReference classReference) {
this.classReference = classReference;
@@ -183,6 +183,14 @@
return this;
}
+ public void setClassReferenceInternal(ClassReference reference) {
+ this.classReference = reference;
+ }
+
+ public ClassReference getClassReference() {
+ return classReference;
+ }
+
@Override
public boolean evaluate(RetraceStackTraceContextImpl context) {
return classReference.equals(context.getThrownException());
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeAlphaRenamingTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeAlphaRenamingTest.java
index 38a9899..a3f010d 100644
--- a/src/test/java/com/android/tools/r8/mappingcompose/ComposeAlphaRenamingTest.java
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeAlphaRenamingTest.java
@@ -31,19 +31,19 @@
private static final String mapping =
StringUtils.unixLines(
"a -> b:",
- " int x -> y",
- " void x() -> y",
+ " int a -> b",
+ " void a() -> b",
"b -> a:",
- " int y -> x",
- " void y() -> x");
+ " int b -> a",
+ " void b() -> a");
private static final String mappingResult =
StringUtils.unixLines(
"a -> a:",
- " int x -> x",
- " void x() -> x",
+ " int a -> a",
+ " void a() -> a",
"b -> b:",
- " int y -> y",
- " void y() -> y");
+ " int b -> b",
+ " void b() -> b");
@Test
public void testCompose() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeHelpers.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeHelpers.java
new file mode 100644
index 0000000..61656a3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeHelpers.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2022, 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.mappingcompose;
+
+public class ComposeHelpers {
+
+ public static String doubleToSingleQuote(String str) {
+ return str.replace("\"", "'");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeMapVersionTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeMapVersionTest.java
index 1774246..bbc6c01 100644
--- a/src/test/java/com/android/tools/r8/mappingcompose/ComposeMapVersionTest.java
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeMapVersionTest.java
@@ -4,7 +4,8 @@
package com.android.tools.r8.mappingcompose;
-import static org.junit.Assert.assertNotEquals;
+import static com.android.tools.r8.mappingcompose.ComposeHelpers.doubleToSingleQuote;
+import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -35,14 +36,13 @@
StringUtils.unixLines("# { id: 'com.android.tools.r8.mapping', version: '2.0' }", "a -> b:");
private static final String mappingResult =
StringUtils.unixLines(
- "# { id: 'com.android.tools.r8.mapping', version: '2.0' }", "com.bar -> b:");
+ "# {'id':'com.android.tools.r8.mapping','version':'2.0'}", "com.foo -> b:");
@Test
public void testCompose() throws Exception {
ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
- // TODO(b/241763080): Support mapping information.
- assertNotEquals(mappingResult, composed);
+ assertEquals(mappingResult, doubleToSingleQuote(composed));
}
}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineTest.java
index 2f89df6..98f1cd5 100644
--- a/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineTest.java
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeOutlineTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.mappingcompose;
+import static com.android.tools.r8.mappingcompose.ComposeHelpers.doubleToSingleQuote;
import static org.junit.Assert.assertNotEquals;
import com.android.tools.r8.TestBase;
@@ -50,24 +51,24 @@
" 42:42:int s(int):27:27 -> o");
private static final String mappingResult =
StringUtils.unixLines(
- "# { id: 'com.android.tools.r8.mapping', version: '2.0' }",
- "outline.Class -> b:",
- " 4:5:int some.inlinee():75:76 -> a",
- " 4:5:int outline():0 -> a",
- " # { 'id':'com.android.tools.r8.outline' }",
+ "# {'id':'com.android.tools.r8.mapping','version':'2.0'}",
"outline.Callsite -> c:",
" 8:8:int outlineCaller(int):23 -> o",
" 9:9:int foo.bar.baz.outlineCaller(int):98:98 -> o",
" 9:9:int outlineCaller(int):24 -> o",
" 42:42:int outlineCaller(int):0:0 -> o",
- " # { 'id':'com.android.tools.r8.outlineCallsite', 'positions': { '4': 8, '5': 9 } }");
+ " # { 'id':'com.android.tools.r8.outlineCallsite', 'positions': { '4': 8, '5': 9 } }",
+ "outline.Class -> b:",
+ " 4:5:int some.inlinee():75:76 -> m",
+ " 4:5:int outline():0 -> m",
+ " # {'id':'com.android.tools.r8.outline'}");
@Test
public void testCompose() throws Exception {
ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
- // TODO(b/241763080): Support mapping information.
- assertNotEquals(mappingResult, composed);
+ // TODO(b/242682464): Update this test when the link has been added to the mapping information.
+ assertNotEquals(mappingResult, doubleToSingleQuote(composed));
}
}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeRewriteFrameTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeRewriteFrameTest.java
index b6a93a6..cae1f89 100644
--- a/src/test/java/com/android/tools/r8/mappingcompose/ComposeRewriteFrameTest.java
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeRewriteFrameTest.java
@@ -4,7 +4,8 @@
package com.android.tools.r8.mappingcompose;
-import static org.junit.Assert.assertNotEquals;
+import static com.android.tools.r8.mappingcompose.ComposeHelpers.doubleToSingleQuote;
+import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -42,23 +43,22 @@
"# { id: 'com.android.tools.r8.mapping', version: '2.0' }",
"a -> b:",
"x -> c:",
- " void a(Other.Class) -> m");
+ " 8:8:void a(Other.Class):4:4 -> m");
private static final String mappingResult =
StringUtils.unixLines(
- "# { id: 'com.android.tools.r8.mapping', version: '2.0' }",
- "my.CustomException -> b:",
+ "# {'id':'com.android.tools.r8.mapping','version':'2.0'}",
"foo.Bar -> c:",
- " 4:4:void other.Class.inlinee():23:23 -> m",
- " 4:4:void caller(other.Class):7 -> m",
- " # { id: 'com.android.tools.r8.rewriteFrame', "
- + "conditions: ['throws(Lb;)'], actions: ['removeInnerFrames(1)'] }");
+ " 8:8:void other.Class.inlinee():23:23 -> m",
+ " 8:8:void caller(other.Class):7 -> m",
+ " # {'id':'com.android.tools.r8.rewriteFrame','conditions':['throws(Lb;)'],"
+ + "'actions':['removeInnerFrames(1)']}",
+ "my.CustomException -> b:");
@Test
public void testCompose() throws Exception {
ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
- // TODO(b/241763080): Support mapping information.
- assertNotEquals(mappingResult, composed);
+ assertEquals(mappingResult, doubleToSingleQuote(composed));
}
}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeSourceFileTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeSourceFileTest.java
index 9cd77f2..9ec3ec6 100644
--- a/src/test/java/com/android/tools/r8/mappingcompose/ComposeSourceFileTest.java
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeSourceFileTest.java
@@ -4,7 +4,8 @@
package com.android.tools.r8.mappingcompose;
-import static org.junit.Assert.assertNotEquals;
+import static com.android.tools.r8.mappingcompose.ComposeHelpers.doubleToSingleQuote;
+import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -36,16 +37,15 @@
private static final String mappingResult =
StringUtils.unixLines(
"com.bar -> c:",
- " # {'id':'sourceFile','fileName':'Bar.kt'}",
+ "# {'id':'sourceFile','fileName':'Bar.kt'}",
"com.foo -> b:",
- " # {'id':'sourceFile','fileName':'Foo.kt'}");
+ "# {'id':'sourceFile','fileName':'Foo.kt'}");
@Test
public void testCompose() throws Exception {
ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
- // TODO(b/241763080): Support mapping information.
- assertNotEquals(mappingResult, composed);
+ assertEquals(mappingResult, doubleToSingleQuote(composed));
}
}
diff --git a/src/test/java/com/android/tools/r8/mappingcompose/ComposeSyntheticTest.java b/src/test/java/com/android/tools/r8/mappingcompose/ComposeSyntheticTest.java
index ad939a6..20585e9 100644
--- a/src/test/java/com/android/tools/r8/mappingcompose/ComposeSyntheticTest.java
+++ b/src/test/java/com/android/tools/r8/mappingcompose/ComposeSyntheticTest.java
@@ -4,7 +4,8 @@
package com.android.tools.r8.mappingcompose;
-import static org.junit.Assert.assertNotEquals;
+import static com.android.tools.r8.mappingcompose.ComposeHelpers.doubleToSingleQuote;
+import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -30,6 +31,7 @@
private static final String mappingFoo =
StringUtils.unixLines(
+ "# { id: 'com.android.tools.r8.mapping', version: '1.0' }",
"com.foo -> a:",
"# { id: 'com.android.tools.r8.synthesized' }",
" int f -> a",
@@ -38,6 +40,7 @@
" # { id: 'com.android.tools.r8.synthesized' }");
private static final String mappingBar =
StringUtils.unixLines(
+ "# { id: 'com.android.tools.r8.mapping', version: '1.0' }",
"a -> b:",
" int a -> b",
"com.bar -> c:",
@@ -46,23 +49,23 @@
" # { id: 'com.android.tools.r8.synthesized' }");
private static final String mappingResult =
StringUtils.unixLines(
- "com.foo -> b:",
- "# { id: 'com.android.tools.r8.synthesized' }",
- " int f -> b",
- " # { id: 'com.android.tools.r8.synthesized' }",
- " void m() -> b",
- " # { id: 'com.android.tools.r8.synthesized' }",
+ "# {'id':'com.android.tools.r8.mapping','version':'1.0'}",
"com.bar -> c:",
- "# { id: 'com.android.tools.r8.synthesized' }",
+ "# {'id':'com.android.tools.r8.synthesized'}",
" void bar() -> a",
- " # { id: 'com.android.tools.r8.synthesized' }");
+ " # {'id':'com.android.tools.r8.synthesized'}",
+ "com.foo -> b:",
+ "# {'id':'com.android.tools.r8.synthesized'}",
+ " int f -> b",
+ // TODO(b/242673239): When fixed, we should emit synthetized info here as well.
+ " void m() -> b",
+ " # {'id':'com.android.tools.r8.synthesized'}");
@Test
public void testCompose() throws Exception {
ClassNameMapper mappingForFoo = ClassNameMapper.mapperFromString(mappingFoo);
ClassNameMapper mappingForBar = ClassNameMapper.mapperFromString(mappingBar);
String composed = MappingComposer.compose(mappingForFoo, mappingForBar);
- // TODO(b/241763080): Support mapping information.
- assertNotEquals(mappingResult, composed);
+ assertEquals(mappingResult, doubleToSingleQuote(composed));
}
}