blob: 8d14980cb52bd830ea2eba9973dd8f3d49ecc544 [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.itf;
import com.android.tools.r8.contexts.CompilationContext.ClassSynthesisDesugaringContext;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaring;
import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedInterfaceDescriptor;
import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceSynthesizerEventConsumer.L8ProgramEmulatedInterfaceSynthesizerEventConsumer;
import com.android.tools.r8.ir.synthetic.EmulateDispatchSyntheticCfCodeProvider;
import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
import com.android.tools.r8.utils.StringDiagnostic;
import java.util.LinkedHashMap;
public final class ProgramEmulatedInterfaceSynthesizer implements CfClassSynthesizerDesugaring {
private final AppView<?> appView;
private final InterfaceDesugaringSyntheticHelper helper;
public static ProgramEmulatedInterfaceSynthesizer create(AppView<?> appView) {
if (!appView.options().isDesugaredLibraryCompilation()
|| !appView.options().machineDesugaredLibrarySpecification.hasEmulatedInterfaces()) {
return null;
}
return new ProgramEmulatedInterfaceSynthesizer(appView);
}
public ProgramEmulatedInterfaceSynthesizer(AppView<?> appView) {
this.appView = appView;
helper = new InterfaceDesugaringSyntheticHelper(appView);
}
DexProgramClass synthesizeProgramEmulatedInterface(
DexProgramClass emulatedInterface,
EmulatedInterfaceDescriptor emulatedInterfaceDescriptor,
L8ProgramEmulatedInterfaceSynthesizerEventConsumer eventConsumer) {
return appView
.getSyntheticItems()
.ensureFixedClass(
SyntheticNaming.SyntheticKind.EMULATED_INTERFACE_CLASS,
emulatedInterface,
appView,
builder ->
synthesizeEmulateInterfaceMethods(
emulatedInterface, emulatedInterfaceDescriptor, builder),
eventConsumer::acceptProgramEmulatedInterface);
}
private void synthesizeEmulateInterfaceMethods(
DexProgramClass emulatedInterface,
EmulatedInterfaceDescriptor emulatedInterfaceDescriptor,
SyntheticProgramClassBuilder builder) {
emulatedInterface.forEachProgramVirtualMethodMatching(
m -> emulatedInterfaceDescriptor.getEmulatedMethods().containsKey(m.getReference()),
method ->
builder.addMethod(
methodBuilder ->
synthesizeEmulatedInterfaceMethod(
method,
emulatedInterfaceDescriptor.getEmulatedMethods().get(method.getReference()),
builder.getType(),
methodBuilder)));
}
private void synthesizeEmulatedInterfaceMethod(
ProgramMethod method,
EmulatedDispatchMethodDescriptor descriptor,
DexType dispatchType,
SyntheticMethodBuilder methodBuilder) {
assert !method.getDefinition().isStatic();
DexMethod emulatedMethod =
helper.emulatedInterfaceDispatchMethod(
descriptor.getEmulatedDispatchMethod(), dispatchType);
DexMethod itfMethod = helper.emulatedInterfaceInterfaceMethod(descriptor.getInterfaceMethod());
DexMethod companionMethod =
helper.ensureEmulatedInterfaceForwardingMethod(descriptor.getForwardingMethod());
LinkedHashMap<DexType, DexMethod> extraDispatchCases = resolveDispatchCases(descriptor);
methodBuilder
.setName(emulatedMethod.getName())
.setProto(emulatedMethod.getProto())
.setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
.setCode(
emulatedInterfaceMethod ->
new EmulateDispatchSyntheticCfCodeProvider(
emulatedMethod.getHolderType(),
companionMethod,
itfMethod,
extraDispatchCases,
appView)
.generateCfCode());
}
private LinkedHashMap<DexType, DexMethod> resolveDispatchCases(
EmulatedDispatchMethodDescriptor descriptor) {
LinkedHashMap<DexType, DexMethod> extraDispatchCases = new LinkedHashMap<>();
descriptor
.getDispatchCases()
.forEach(
(type, derivedMethod) ->
extraDispatchCases.put(
type, helper.ensureEmulatedInterfaceForwardingMethod(derivedMethod)));
return extraDispatchCases;
}
@Override
public String uniqueIdentifier() {
return "$emulatedInterface$";
}
@Override
public void synthesizeClasses(
ClassSynthesisDesugaringContext processingContext,
CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
assert appView.options().isDesugaredLibraryCompilation();
appView
.options()
.machineDesugaredLibrarySpecification
.getEmulatedInterfaces()
.forEach(
(emulatedInterfaceType, emulatedInterfaceDescriptor) -> {
DexClass emulatedInterfaceClazz = appView.definitionFor(emulatedInterfaceType);
if (emulatedInterfaceClazz == null || !emulatedInterfaceClazz.isProgramClass()) {
warnMissingEmulatedInterface(emulatedInterfaceType);
return;
}
DexProgramClass emulatedInterface = emulatedInterfaceClazz.asProgramClass();
assert emulatedInterface != null;
if (!appView.isAlreadyLibraryDesugared(emulatedInterface)
&& !emulatedInterfaceDescriptor.getEmulatedMethods().isEmpty()) {
synthesizeProgramEmulatedInterface(
emulatedInterface, emulatedInterfaceDescriptor, eventConsumer);
}
});
}
private void warnMissingEmulatedInterface(DexType interfaceType) {
StringDiagnostic warning =
new StringDiagnostic(
"Cannot emulate interface "
+ interfaceType.getName()
+ " because the interface is missing.");
appView.options().reporter.warning(warning);
}
}