Skip to content

Commit 4ad81c8

Browse files
timpeutcopybara-github
authored andcommitted
Add context mapping to desugar output.
PiperOrigin-RevId: 685773596 Change-Id: Ie7d870f279b3f50d4ae7419a3af18ecbccb21733
1 parent d11599b commit 4ad81c8

File tree

4 files changed

+93
-9
lines changed

4 files changed

+93
-9
lines changed

src/tools/java/com/google/devtools/build/android/r8/CompatDexBuilder.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@
7171
*/
7272
public class CompatDexBuilder {
7373

74+
public static final String CONTEXT_MAP_FILENAME = "META-INF/metadata/synthetic-contexts.map";
75+
7476
private static final long ONE_MEG = 1024 * 1024;
7577

7678
private static class ContextConsumer implements SyntheticInfoConsumer {
@@ -286,12 +288,14 @@ private void dexEntries(
286288
final Enumeration<? extends ZipEntry> entries = zipFile.entries();
287289
while (entries.hasMoreElements()) {
288290
ZipEntry entry = entries.nextElement();
289-
if (!entry.getName().endsWith(".class")) {
291+
if (entry.getName().endsWith(".class")) {
292+
toDex.add(entry);
293+
} else if (!entry.getName().equals(CONTEXT_MAP_FILENAME)) {
294+
// The context mapping is rebuilt from the compiler inputs so any previous map
295+
// should be removed.
290296
try (InputStream stream = zipFile.getInputStream(entry)) {
291297
ZipUtils.addEntry(entry.getName(), stream, out);
292298
}
293-
} else {
294-
toDex.add(entry);
295299
}
296300
}
297301

@@ -323,10 +327,7 @@ private void dexEntries(
323327
String contextMapping = contextMappingBuilder.toString();
324328
if (!contextMapping.isEmpty()) {
325329
ZipUtils.addEntry(
326-
"META-INF/metadata/synthetic-contexts.map",
327-
contextMapping.getBytes(UTF_8),
328-
ZipEntry.STORED,
329-
out);
330+
CONTEXT_MAP_FILENAME, contextMapping.getBytes(UTF_8), ZipEntry.STORED, out);
330331
}
331332
}
332333
} finally {

src/tools/java/com/google/devtools/build/android/r8/Desugar.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
public class Desugar {
5757

5858
public static final String DESUGAR_DEPS_FILENAME = "META-INF/desugar_deps";
59+
public static final String CONTEXT_MAP_FILENAME = "META-INF/metadata/synthetic-contexts.map";
5960
// We shard the compilation if we have more than this number of entries to avoid timing out.
6061
private static final int NUMBER_OF_ENTRIES_PER_SHARD = 100000;
6162
private static final Logger logger = Logger.getLogger(Desugar.class.getName());
@@ -423,6 +424,7 @@ private void desugar(
423424
.setGlobalSyntheticsConsumer(new NoOpGlobalSyntheticsConsumer())
424425
.setIntermediate(true)
425426
.setMinApiLevel(options.minSdkVersion)
427+
.setSyntheticInfoConsumer(consumer.getContextConsumer())
426428
.setProgramConsumer(consumer);
427429
bootclasspathProviders.forEach(builder::addLibraryResourceProvider);
428430
if (desugaredLibConfig != null) {

src/tools/java/com/google/devtools/build/android/r8/desugar/OutputConsumer.java

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import static com.google.common.base.Preconditions.checkArgument;
1717
import static com.google.devtools.build.android.r8.desugar.OutputConsumer.Flags.EXCLUDE_PATH_ENTRIES;
1818
import static com.google.devtools.build.android.r8.desugar.OutputConsumer.Flags.INCLUDE_PATH_ENTRIES;
19+
import static java.nio.charset.StandardCharsets.UTF_8;
1920
import static org.objectweb.asm.Opcodes.ACC_BRIDGE;
2021
import static org.objectweb.asm.Opcodes.ACC_INTERFACE;
2122
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
@@ -24,8 +25,11 @@
2425
import com.android.tools.r8.ByteDataView;
2526
import com.android.tools.r8.ClassFileConsumer;
2627
import com.android.tools.r8.DiagnosticsHandler;
28+
import com.android.tools.r8.SyntheticInfoConsumer;
29+
import com.android.tools.r8.SyntheticInfoConsumerData;
2730
import com.android.tools.r8.origin.Origin;
2831
import com.android.tools.r8.origin.PathOrigin;
32+
import com.android.tools.r8.references.ClassReference;
2933
import com.android.tools.r8.utils.ExceptionDiagnostic;
3034
import com.google.common.io.ByteStreams;
3135
import com.google.devtools.build.android.r8.DependencyCollector;
@@ -38,9 +42,12 @@
3842
import java.io.InputStream;
3943
import java.nio.file.Files;
4044
import java.nio.file.Path;
45+
import java.util.ArrayList;
46+
import java.util.Comparator;
4147
import java.util.Enumeration;
4248
import java.util.HashMap;
4349
import java.util.HashSet;
50+
import java.util.List;
4451
import java.util.Map;
4552
import java.util.NavigableSet;
4653
import java.util.Set;
@@ -79,6 +86,31 @@ public void finished(DiagnosticsHandler handler) {
7986
if (!finished()) {
8087
return;
8188
}
89+
90+
byte[] contextMappingContent;
91+
Map<ClassReference, Set<ClassReference>> contextToSyntheticsMap =
92+
getContextConsumer().contextToSyntheticsMap;
93+
if (contextToSyntheticsMap.isEmpty()) {
94+
contextMappingContent = null;
95+
} else {
96+
StringBuilder contextMappingBuilder = new StringBuilder();
97+
List<ClassReference> contexts = new ArrayList<>(contextToSyntheticsMap.keySet());
98+
contexts.sort(Comparator.comparing(ClassReference::getDescriptor));
99+
contexts.forEach(
100+
context -> {
101+
List<ClassReference> synthetics = new ArrayList<>(contextToSyntheticsMap.get(context));
102+
synthetics.sort(Comparator.comparing(ClassReference::getDescriptor));
103+
synthetics.forEach(
104+
synthetic ->
105+
contextMappingBuilder
106+
.append(synthetic.getBinaryName())
107+
.append(';')
108+
.append(context.getBinaryName())
109+
.append('\n'));
110+
});
111+
contextMappingContent = contextMappingBuilder.toString().getBytes(UTF_8);
112+
}
113+
82114
FilteringDependencyCollector dependencyCollector =
83115
new FilteringDependencyCollector(this.dependencyCollector);
84116
initializeInputDependencies(handler, dependencyCollector);
@@ -89,6 +121,11 @@ public void finished(DiagnosticsHandler handler) {
89121
ZipUtils.addEntry(classFile.fileName, classFile.data, ZipEntry.STORED, out);
90122
new DesugaredClassFileDependencyCollector(classFile, dependencyCollector).run();
91123
}
124+
// Add context-mapping if required.
125+
if (contextMappingContent != null) {
126+
ZipUtils.addEntry(
127+
Desugar.CONTEXT_MAP_FILENAME, contextMappingContent, ZipEntry.STORED, out);
128+
}
92129
// Add dependency metadata if required.
93130
byte[] desugarDeps = dependencyCollector.toByteArray();
94131
if (desugarDeps != null) {
@@ -98,9 +135,13 @@ public void finished(DiagnosticsHandler handler) {
98135
input,
99136
out,
100137
entryName ->
101-
("module-info.class".equals(entryName) || entryName.startsWith("META-INF/versions/"))
138+
(entryName.equals("module-info.class")
139+
// The context mapping is rebuilt from the compiler inputs so any previous map
140+
// should be removed.
141+
|| entryName.equals(Desugar.CONTEXT_MAP_FILENAME)
142+
|| entryName.startsWith("META-INF/versions/")
102143
|| ArchiveProgramResourceProvider.includeClassFileEntries(entryName)
103-
|| (entryName.endsWith("/") && flags == EXCLUDE_PATH_ENTRIES),
144+
|| (entryName.endsWith("/") && flags == EXCLUDE_PATH_ENTRIES)),
104145
name -> {
105146
final String metainfServicesPrefix = "META-INF/services/";
106147
if (name.startsWith(metainfServicesPrefix)) {
@@ -142,11 +183,29 @@ public int compareTo(ClassFileData other) {
142183
}
143184
}
144185

186+
private static class ContextConsumer implements SyntheticInfoConsumer {
187+
188+
final Map<ClassReference, Set<ClassReference>> contextToSyntheticsMap = new HashMap<>();
189+
190+
@Override
191+
public synchronized void acceptSyntheticInfo(SyntheticInfoConsumerData data) {
192+
contextToSyntheticsMap
193+
.computeIfAbsent(data.getSynthesizingContextClass(), k -> new HashSet<>())
194+
.add(data.getSyntheticClass());
195+
}
196+
197+
@Override
198+
public void finished() {
199+
// Do nothing.
200+
}
201+
}
202+
145203
private final Path archive;
146204
private final Origin origin;
147205
private final DependencyCollector dependencyCollector;
148206
private final Path input;
149207
private final Flags flags;
208+
private final ContextConsumer contextConsumer = new ContextConsumer();
150209

151210
private final NavigableSet<ClassFileData> classFiles = new TreeSet<>();
152211
private boolean finish = true;
@@ -164,6 +223,10 @@ public OutputConsumer(
164223
this.flags = flags;
165224
}
166225

226+
public ContextConsumer getContextConsumer() {
227+
return contextConsumer;
228+
}
229+
167230
@Override
168231
public void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler) {
169232
classFiles.add(

src/tools/javatests/com/google/devtools/build/android/r8/desugar/DesugarBasicTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import static com.google.common.truth.Truth.assertThat;
1717
import static com.google.devtools.build.android.r8.R8Utils.INTERFACE_COMPANION_SUFFIX;
18+
import static java.nio.charset.StandardCharsets.UTF_8;
1819
import static org.objectweb.asm.Opcodes.V1_7;
1920

2021
import com.google.common.collect.ImmutableList;
@@ -134,6 +135,15 @@ public void checkAfterDesugarClasspath() throws Exception {
134135

135136
@Test
136137
public void checkMetaDataAfterDoubleDesugaring() throws Exception {
138+
String contextMap = extractContextMaps(doubleDesugaredWithDependencyMetadata);
139+
assertThat(contextMap)
140+
.isEqualTo(
141+
String.join(
142+
"",
143+
"com/google/devtools/build/android/r8/desugar/basic/I$-CC;com/google/devtools/build/android/r8/desugar/basic/I\n",
144+
"com/google/devtools/build/android/r8/desugar/basic/J$-CC;com/google/devtools/build/android/r8/desugar/basic/J\n",
145+
"com/google/devtools/build/android/r8/desugar/basic/K$-CC;com/google/devtools/build/android/r8/desugar/basic/K\n",
146+
"com/google/devtools/build/android/r8/desugar/basic/TestClass$$ExternalSyntheticLambda0;com/google/devtools/build/android/r8/desugar/basic/TestClass\n"));
137147
DesugarDepsInfo info = extractDesugarDeps(doubleDesugaredWithDependencyMetadata);
138148
assertThat(info.getInterfaceWithCompanionCount()).isEqualTo(0);
139149
assertThat(info.getAssumePresentCount()).isEqualTo(0);
@@ -155,6 +165,14 @@ private static DesugarDepsInfo extractDesugarDeps(Path jar) throws Exception {
155165
}
156166
}
157167

168+
private static String extractContextMaps(Path jar) throws Exception {
169+
try (ZipFile zip = new ZipFile(jar.toFile())) {
170+
ZipEntry contextMapEntry = zip.getEntry(Desugar.CONTEXT_MAP_FILENAME);
171+
assertThat(contextMapEntry).isNotNull();
172+
return new String(zip.getInputStream(contextMapEntry).readAllBytes(), UTF_8);
173+
}
174+
}
175+
158176
private static DesugarDeps.Type classToType(Class<?> clazz) {
159177
return DesugarDeps.Type.newBuilder()
160178
.setBinaryName(DescriptorUtils.classToBinaryName(clazz))

0 commit comments

Comments
 (0)