blob: 1c6b3372d894d114dada29b09c6bb5bdef470809 [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.desugar;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClasspathClass;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.conversion.ClassConverterResult;
import com.android.tools.r8.ir.conversion.D8MethodProcessor;
import com.android.tools.r8.ir.desugar.backports.BackportedMethodDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialBridgeInfo;
import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialToSelfDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.lambda.LambdaDeserializationMethodRemover;
import com.android.tools.r8.ir.desugar.lambda.LambdaDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.twr.TwrCloseResourceDesugaringEventConsumer;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
* Class that gets notified for structural changes made as a result of desugaring (e.g., the
* inserting of a new method).
*/
public abstract class CfInstructionDesugaringEventConsumer
implements BackportedMethodDesugaringEventConsumer,
InvokeSpecialToSelfDesugaringEventConsumer,
LambdaDesugaringEventConsumer,
NestBasedAccessDesugaringEventConsumer,
RecordDesugaringEventConsumer,
TwrCloseResourceDesugaringEventConsumer,
InterfaceMethodDesugaringEventConsumer,
DesugaredLibraryRetargeterEventConsumer {
public static D8CfInstructionDesugaringEventConsumer createForD8(
D8MethodProcessor methodProcessor) {
return new D8CfInstructionDesugaringEventConsumer(methodProcessor);
}
public static R8CfInstructionDesugaringEventConsumer createForR8(
AppView<? extends AppInfoWithClassHierarchy> appView,
BiConsumer<LambdaClass, ProgramMethod> lambdaClassConsumer,
BiConsumer<ProgramMethod, ProgramMethod> twrCloseResourceMethodConsumer) {
return new R8CfInstructionDesugaringEventConsumer(
appView, lambdaClassConsumer, twrCloseResourceMethodConsumer);
}
public static CfInstructionDesugaringEventConsumer createForDesugaredCode() {
return new CfInstructionDesugaringEventConsumer() {
@Override
public void acceptDesugaredLibraryRetargeterDispatchProgramClass(DexProgramClass clazz) {
assert false;
}
@Override
public void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz) {
assert false;
}
@Override
public void acceptForwardingMethod(ProgramMethod method) {
assert false;
}
@Override
public void acceptThrowMethod(ProgramMethod method, ProgramMethod context) {
assert false;
}
@Override
public void acceptInvokeStaticInterfaceOutliningMethod(
ProgramMethod method, ProgramMethod context) {
assert false;
}
@Override
public void acceptRecordClass(DexProgramClass recordClass) {
assert false;
}
@Override
public void acceptRecordMethod(ProgramMethod method) {
assert false;
}
@Override
public void acceptBackportedMethod(ProgramMethod backportedMethod, ProgramMethod context) {
assert false;
}
@Override
public void acceptInvokeSpecialBridgeInfo(InvokeSpecialBridgeInfo info) {
assert false;
}
@Override
public void acceptLambdaClass(LambdaClass lambdaClass, ProgramMethod context) {
assert false;
}
@Override
public void acceptNestFieldGetBridge(ProgramField target, ProgramMethod bridge) {
assert false;
}
@Override
public void acceptNestFieldPutBridge(ProgramField target, ProgramMethod bridge) {
assert false;
}
@Override
public void acceptNestMethodBridge(ProgramMethod target, ProgramMethod bridge) {
assert false;
}
@Override
public void acceptTwrCloseResourceMethod(ProgramMethod closeMethod, ProgramMethod context) {
assert false;
}
};
}
public static class D8CfInstructionDesugaringEventConsumer
extends CfInstructionDesugaringEventConsumer {
private final D8MethodProcessor methodProcessor;
private final Map<DexReference, InvokeSpecialBridgeInfo> pendingInvokeSpecialBridges =
new LinkedHashMap<>();
private final List<LambdaClass> synthesizedLambdaClasses = new ArrayList<>();
private D8CfInstructionDesugaringEventConsumer(D8MethodProcessor methodProcessor) {
this.methodProcessor = methodProcessor;
}
@Override
public void acceptDesugaredLibraryRetargeterDispatchProgramClass(DexProgramClass clazz) {
methodProcessor.scheduleDesugaredMethodsForProcessing(clazz.programMethods());
}
@Override
public void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz) {
// Intentionnaly empty.
}
@Override
public void acceptForwardingMethod(ProgramMethod method) {
methodProcessor.scheduleDesugaredMethodForProcessing(method);
}
@Override
public void acceptBackportedMethod(ProgramMethod backportedMethod, ProgramMethod context) {
methodProcessor.scheduleMethodForProcessing(backportedMethod, this);
}
@Override
public void acceptRecordMethod(ProgramMethod method) {
methodProcessor.scheduleDesugaredMethodForProcessing(method);
}
@Override
public void acceptInvokeSpecialBridgeInfo(InvokeSpecialBridgeInfo info) {
synchronized (pendingInvokeSpecialBridges) {
assert !pendingInvokeSpecialBridges.containsKey(info.getNewDirectMethod().getReference());
pendingInvokeSpecialBridges.put(info.getNewDirectMethod().getReference(), info);
}
}
@Override
public void acceptRecordClass(DexProgramClass recordClass) {
methodProcessor.scheduleDesugaredMethodsForProcessing(recordClass.programMethods());
}
@Override
public void acceptLambdaClass(LambdaClass lambdaClass, ProgramMethod context) {
synchronized (synthesizedLambdaClasses) {
synthesizedLambdaClasses.add(lambdaClass);
}
}
@Override
public void acceptNestFieldGetBridge(ProgramField target, ProgramMethod bridge) {
methodProcessor.scheduleDesugaredMethodForProcessing(bridge);
}
@Override
public void acceptNestFieldPutBridge(ProgramField target, ProgramMethod bridge) {
methodProcessor.scheduleDesugaredMethodForProcessing(bridge);
}
@Override
public void acceptNestMethodBridge(ProgramMethod target, ProgramMethod bridge) {
methodProcessor.scheduleDesugaredMethodForProcessing(bridge);
}
@Override
public void acceptTwrCloseResourceMethod(ProgramMethod closeMethod, ProgramMethod context) {
methodProcessor.scheduleDesugaredMethodForProcessing(closeMethod);
}
@Override
public void acceptThrowMethod(ProgramMethod method, ProgramMethod context) {
methodProcessor.scheduleDesugaredMethodForProcessing(method);
}
@Override
public void acceptInvokeStaticInterfaceOutliningMethod(
ProgramMethod method, ProgramMethod context) {
methodProcessor.scheduleDesugaredMethodForProcessing(method);
}
public List<ProgramMethod> finalizeDesugaring(
AppView<?> appView, ClassConverterResult.Builder classConverterResultBuilder) {
List<ProgramMethod> needsProcessing = new ArrayList<>();
finalizeInvokeSpecialDesugaring(appView, needsProcessing::add);
finalizeLambdaDesugaring(classConverterResultBuilder, needsProcessing::add);
return needsProcessing;
}
private void finalizeInvokeSpecialDesugaring(
AppView<?> appView, Consumer<ProgramMethod> needsProcessing) {
// Fixup the code of the new private methods have that been synthesized.
pendingInvokeSpecialBridges
.values()
.forEach(
info -> {
ProgramMethod newDirectMethod = info.getNewDirectMethod();
newDirectMethod
.getDefinition()
.setCode(info.getVirtualMethod().getDefinition().getCode(), appView);
});
// Reprocess the methods that were subject to invoke-special desugaring (because their body
// has been moved to a private method).
pendingInvokeSpecialBridges
.values()
.forEach(
info -> {
info.getVirtualMethod()
.getDefinition()
.setCode(info.getVirtualMethodCode(), appView);
needsProcessing.accept(info.getVirtualMethod());
});
pendingInvokeSpecialBridges.clear();
}
private void finalizeLambdaDesugaring(
ClassConverterResult.Builder classConverterResultBuilder,
Consumer<ProgramMethod> needsProcessing) {
for (LambdaClass lambdaClass : synthesizedLambdaClasses) {
lambdaClass.target.ensureAccessibilityIfNeeded(
classConverterResultBuilder, needsProcessing);
lambdaClass.getLambdaProgramClass().forEachProgramMethod(needsProcessing);
}
synthesizedLambdaClasses.clear();
}
public boolean verifyNothingToFinalize() {
assert pendingInvokeSpecialBridges.isEmpty();
assert synthesizedLambdaClasses.isEmpty();
return true;
}
}
public static class R8CfInstructionDesugaringEventConsumer
extends CfInstructionDesugaringEventConsumer {
private final AppView<? extends AppInfoWithClassHierarchy> appView;
// TODO(b/180091213): Remove these two consumers when synthesizing contexts are accessible from
// synthetic items.
private final BiConsumer<LambdaClass, ProgramMethod> lambdaClassConsumer;
private final BiConsumer<ProgramMethod, ProgramMethod> twrCloseResourceMethodConsumer;
private final Map<LambdaClass, ProgramMethod> synthesizedLambdaClasses =
new IdentityHashMap<>();
private final List<InvokeSpecialBridgeInfo> pendingInvokeSpecialBridges = new ArrayList<>();
public R8CfInstructionDesugaringEventConsumer(
AppView<? extends AppInfoWithClassHierarchy> appView,
BiConsumer<LambdaClass, ProgramMethod> lambdaClassConsumer,
BiConsumer<ProgramMethod, ProgramMethod> twrCloseResourceMethodConsumer) {
this.appView = appView;
this.lambdaClassConsumer = lambdaClassConsumer;
this.twrCloseResourceMethodConsumer = twrCloseResourceMethodConsumer;
}
@Override
public void acceptDesugaredLibraryRetargeterDispatchProgramClass(DexProgramClass clazz) {
// Called only in Desugared library compilation which is D8.
assert false;
}
@Override
public void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz) {
// TODO(b/188767735): R8 currently relies on IR desugaring.
// The classpath class should be marked as liveNonProgramType.
}
@Override
public void acceptForwardingMethod(ProgramMethod method) {
// TODO(b/188767735): R8 currently relies on IR desugaring.
// The method should be marked live, and assert everything it references is traced.
}
@Override
public void acceptRecordClass(DexProgramClass recordClass) {
// This is called each time an instruction or a class is found to require the record class.
assert false : "TODO(b/179146128): To be implemented";
}
@Override
public void acceptRecordMethod(ProgramMethod method) {
assert false : "TODO(b/179146128): To be implemented";
}
@Override
public void acceptThrowMethod(ProgramMethod method, ProgramMethod context) {
assert false : "TODO(b/183998768): To be implemented";
}
@Override
public void acceptInvokeStaticInterfaceOutliningMethod(
ProgramMethod method, ProgramMethod context) {
assert false : "TODO(b/183998768): To be implemented";
}
@Override
public void acceptBackportedMethod(ProgramMethod backportedMethod, ProgramMethod context) {
// Intentionally empty. The backported method will be hit by the tracing in R8 as if it was
// present in the input code, and thus nothing needs to be done.
}
@Override
public void acceptInvokeSpecialBridgeInfo(InvokeSpecialBridgeInfo info) {
synchronized (pendingInvokeSpecialBridges) {
pendingInvokeSpecialBridges.add(info);
}
}
@Override
public void acceptLambdaClass(LambdaClass lambdaClass, ProgramMethod context) {
synchronized (synthesizedLambdaClasses) {
synthesizedLambdaClasses.put(lambdaClass, context);
}
// TODO(b/180091213): Remove the recording of the synthesizing context when this is accessible
// from synthetic items.
lambdaClassConsumer.accept(lambdaClass, context);
}
@Override
public void acceptNestFieldGetBridge(ProgramField target, ProgramMethod bridge) {
// Intentionally empty. These bridges will be hit by the tracing in R8 as if they were present
// in the input code, and thus nothing needs to be done.
}
@Override
public void acceptNestFieldPutBridge(ProgramField target, ProgramMethod bridge) {
// Intentionally empty. These bridges will be hit by the tracing in R8 as if they were present
// in the input code, and thus nothing needs to be done.
}
@Override
public void acceptNestMethodBridge(ProgramMethod target, ProgramMethod bridge) {
// Intentionally empty. These bridges will be hit by the tracing in R8 as if they were present
// in the input code, and thus nothing needs to be done.
}
@Override
public void acceptTwrCloseResourceMethod(ProgramMethod closeMethod, ProgramMethod context) {
// Intentionally empty. The close method will be hit by the tracing in R8 as if they were
// present in the input code, and thus nothing needs to be done.
// TODO(b/180091213): Remove the recording of the synthesizing context when this is accessible
// from synthetic items.
twrCloseResourceMethodConsumer.accept(closeMethod, context);
}
public void finalizeDesugaring() {
finalizeInvokeSpecialDesugaring();
finalizeLambdaDesugaring();
}
private void finalizeInvokeSpecialDesugaring() {
Collections.sort(pendingInvokeSpecialBridges);
pendingInvokeSpecialBridges.forEach(
info ->
info.getVirtualMethod()
.getDefinition()
.setCode(info.getVirtualMethodCode(), appView));
}
private void finalizeLambdaDesugaring() {
Set<DexProgramClass> classesWithSerializableLambdas = Sets.newIdentityHashSet();
synthesizedLambdaClasses.forEach(
(lambdaClass, context) -> {
lambdaClass.target.ensureAccessibilityIfNeeded();
// Populate set of types with serialized lambda method for removal.
if (lambdaClass.descriptor.interfaces.contains(
appView.dexItemFactory().serializableType)) {
classesWithSerializableLambdas.add(context.getHolder());
}
});
// Remove all '$deserializeLambda$' methods which are not supported by desugaring.
LambdaDeserializationMethodRemover.run(appView, classesWithSerializableLambdas);
}
}
}