1616import static com .google .common .base .Preconditions .checkArgument ;
1717import static com .google .devtools .build .android .r8 .desugar .OutputConsumer .Flags .EXCLUDE_PATH_ENTRIES ;
1818import static com .google .devtools .build .android .r8 .desugar .OutputConsumer .Flags .INCLUDE_PATH_ENTRIES ;
19+ import static java .nio .charset .StandardCharsets .UTF_8 ;
1920import static org .objectweb .asm .Opcodes .ACC_BRIDGE ;
2021import static org .objectweb .asm .Opcodes .ACC_INTERFACE ;
2122import static org .objectweb .asm .Opcodes .INVOKESTATIC ;
2425import com .android .tools .r8 .ByteDataView ;
2526import com .android .tools .r8 .ClassFileConsumer ;
2627import com .android .tools .r8 .DiagnosticsHandler ;
28+ import com .android .tools .r8 .SyntheticInfoConsumer ;
29+ import com .android .tools .r8 .SyntheticInfoConsumerData ;
2730import com .android .tools .r8 .origin .Origin ;
2831import com .android .tools .r8 .origin .PathOrigin ;
32+ import com .android .tools .r8 .references .ClassReference ;
2933import com .android .tools .r8 .utils .ExceptionDiagnostic ;
3034import com .google .common .io .ByteStreams ;
3135import com .google .devtools .build .android .r8 .DependencyCollector ;
3842import java .io .InputStream ;
3943import java .nio .file .Files ;
4044import java .nio .file .Path ;
45+ import java .util .ArrayList ;
46+ import java .util .Comparator ;
4147import java .util .Enumeration ;
4248import java .util .HashMap ;
4349import java .util .HashSet ;
50+ import java .util .List ;
4451import java .util .Map ;
4552import java .util .NavigableSet ;
4653import 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 (
0 commit comments