blob: 83e0ff58a9e5d4eb2d57ac20398447925b4384ca [file] [log] [blame]
// Copyright (c) 2021, 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.ir.conversion;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.desugar.CfClassDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.CfClassDesugaringEventConsumer.D8CfClassDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer.D8CfInstructionDesugaringEventConsumer;
import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
public abstract class ClassConverter {
protected final AppView<?> appView;
private final IRConverter converter;
private final D8MethodProcessor methodProcessor;
ClassConverter(AppView<?> appView, IRConverter converter, D8MethodProcessor methodProcessor) {
this.appView = appView;
this.converter = converter;
this.methodProcessor = methodProcessor;
}
public static ClassConverter create(
AppView<?> appView, IRConverter converter, D8MethodProcessor methodProcessor) {
return appView.options().desugarSpecificOptions().allowAllDesugaredInput
? new LibraryDesugaredClassConverter(appView, converter, methodProcessor)
: new DefaultClassConverter(appView, converter, methodProcessor);
}
public ClassConverterResult convertClasses(ExecutorService executorService)
throws ExecutionException {
ClassConverterResult.Builder resultBuilder = ClassConverterResult.builder();
internalConvertClasses(resultBuilder, executorService);
notifyAllClassesConverted();
return resultBuilder.build();
}
private void internalConvertClasses(
ClassConverterResult.Builder resultBuilder, ExecutorService executorService)
throws ExecutionException {
List<DexProgramClass> classes = appView.appInfo().classes();
D8CfClassDesugaringEventConsumer classDesugaringEventConsumer =
CfClassDesugaringEventConsumer.createForD8(methodProcessor);
converter.desugarClassesForD8(classes, classDesugaringEventConsumer, executorService);
while (!classes.isEmpty()) {
Set<DexType> seenNestHosts = Sets.newIdentityHashSet();
List<DexProgramClass> deferred = new ArrayList<>(classes.size() / 2);
List<DexProgramClass> wave = new ArrayList<>(classes.size());
for (DexProgramClass clazz : classes) {
if (clazz.isInANest() && !seenNestHosts.add(clazz.getNestHost())) {
deferred.add(clazz);
} else {
wave.add(clazz);
// TODO(b/179755192): Avoid marking classes as scheduled by building up waves of methods.
methodProcessor.addScheduled(clazz);
}
}
D8CfInstructionDesugaringEventConsumer instructionDesugaringEventConsumer =
CfInstructionDesugaringEventConsumer.createForD8(methodProcessor);
// Process the wave and wait for all IR processing to complete.
methodProcessor.newWave();
ThreadUtils.processItems(
wave, clazz -> convertClass(clazz, instructionDesugaringEventConsumer), executorService);
methodProcessor.awaitMethodProcessing();
// Finalize the desugaring of the processed classes. This may require processing (and
// reprocessing) of some methods.
List<ProgramMethod> needsProcessing =
instructionDesugaringEventConsumer.finalizeDesugaring(appView, resultBuilder);
if (!needsProcessing.isEmpty()) {
// Create a new processor context to ensure unique method processing contexts.
methodProcessor.newWave();
// Process the methods that require reprocessing. These are all simple bridge methods and
// should therefore not lead to additional desugaring.
ThreadUtils.processItems(
needsProcessing,
method -> {
DexEncodedMethod definition = method.getDefinition();
if (definition.isProcessed()) {
definition.markNotProcessed();
}
methodProcessor.processMethod(method, instructionDesugaringEventConsumer);
},
executorService);
// Verify there is nothing to finalize once method processing finishes.
methodProcessor.awaitMethodProcessing();
assert instructionDesugaringEventConsumer.verifyNothingToFinalize();
}
converter.finalizeDesugaredLibraryRetargeting(instructionDesugaringEventConsumer);
assert instructionDesugaringEventConsumer.verifyNothingToFinalize();
classes = deferred;
}
}
abstract void convertClass(
DexProgramClass clazz, D8CfInstructionDesugaringEventConsumer desugaringEventConsumer);
void convertMethods(
DexProgramClass clazz, D8CfInstructionDesugaringEventConsumer desugaringEventConsumer) {
converter.convertMethods(clazz, desugaringEventConsumer, methodProcessor);
}
abstract void notifyAllClassesConverted();
static class DefaultClassConverter extends ClassConverter {
DefaultClassConverter(
AppView<?> appView, IRConverter converter, D8MethodProcessor methodProcessor) {
super(appView, converter, methodProcessor);
}
@Override
void convertClass(
DexProgramClass clazz, D8CfInstructionDesugaringEventConsumer desugaringEventConsumer) {
convertMethods(clazz, desugaringEventConsumer);
}
@Override
void notifyAllClassesConverted() {
// Intentionally empty.
}
}
static class LibraryDesugaredClassConverter extends ClassConverter {
private final Set<DexType> alreadyLibraryDesugared = Sets.newConcurrentHashSet();
LibraryDesugaredClassConverter(
AppView<?> appView, IRConverter converter, D8MethodProcessor methodProcessor) {
super(appView, converter, methodProcessor);
}
@Override
void convertClass(
DexProgramClass clazz, D8CfInstructionDesugaringEventConsumer desugaringEventConsumer) {
// Classes which has already been through library desugaring will not go through IR
// processing again.
LibraryDesugaredChecker libraryDesugaredChecker = new LibraryDesugaredChecker(appView);
if (libraryDesugaredChecker.isClassLibraryDesugared(clazz)) {
alreadyLibraryDesugared.add(clazz.getType());
} else {
convertMethods(clazz, desugaringEventConsumer);
}
}
@Override
void notifyAllClassesConverted() {
appView.setAlreadyLibraryDesugared(alreadyLibraryDesugared);
}
}
}