blob: 1fa4b758fd11ef6c5ebf075cab6614dadb919a59 [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.desugaredlibrary.retargeter;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.ClasspathMethod;
import com.android.tools.r8.graph.ClasspathOrLibraryClass;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.DerivedMethod;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterInstructionEventConsumer;
import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterL8SynthesizerEventConsumer;
import com.android.tools.r8.ir.synthetic.EmulateDispatchSyntheticCfCodeProvider;
import com.android.tools.r8.synthesis.SyntheticClassBuilder;
import com.android.tools.r8.synthesis.SyntheticItems.SyntheticKindSelector;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import java.util.LinkedHashMap;
public class DesugaredLibraryRetargeterSyntheticHelper {
private final AppView<?> appView;
public DesugaredLibraryRetargeterSyntheticHelper(AppView<?> appView) {
this.appView = appView;
}
public DexMethod ensureRetargetMethod(
DexMethod retarget, DesugaredLibraryRetargeterInstructionEventConsumer eventConsumer) {
DexClass holderClass = appView.definitionFor(retarget.getHolderType());
if (holderClass != null && !holderClass.isClasspathClass()) {
// The holder class is a library class in orthodox set-ups where the L8 compilation
// is done in multiple steps, this is only partially supported (most notably for tests).
assert holderClass.lookupMethod(retarget) != null;
return retarget;
}
assert eventConsumer != null;
ClasspathMethod ensuredMethod =
appView
.getSyntheticItems()
.ensureFixedClasspathMethodFromType(
retarget.getName(),
retarget.getProto(),
kinds -> kinds.RETARGET_STUB,
retarget.getHolderType(),
appView,
ignored -> {},
eventConsumer::acceptDesugaredLibraryRetargeterDispatchClasspathClass,
methodBuilder ->
methodBuilder
.setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
.setCode(null));
assert ensuredMethod.getReference() == retarget;
return retarget;
}
DexMethod forwardingMethod(EmulatedDispatchMethodDescriptor descriptor) {
assert descriptor.getForwardingMethod().getHolderKind() == null;
return descriptor.getForwardingMethod().getMethod();
}
public DexMethod ensureForwardingMethod(
EmulatedDispatchMethodDescriptor descriptor,
DesugaredLibraryRetargeterInstructionEventConsumer eventConsumer) {
return ensureRetargetMethod(forwardingMethod(descriptor), eventConsumer);
}
private boolean verifyKind(DerivedMethod method, SyntheticKindSelector kindSelector) {
SyntheticKind kind = kindSelector.select(appView.getSyntheticItems().getNaming());
assert method.getHolderKind().equals(kind);
return true;
}
private DexMethod emulatedHolderDispatchMethod(DexType holder, DerivedMethod method) {
assert verifyKind(method, kinds -> kinds.RETARGET_CLASS);
DexProto newProto = appView.dexItemFactory().prependHolderToProto(method.getMethod());
return appView.dexItemFactory().createMethod(holder, newProto, method.getName());
}
DexMethod emulatedInterfaceDispatchMethod(DexType holder, DerivedMethod method) {
assert verifyKind(method, kinds -> kinds.RETARGET_INTERFACE);
return appView.dexItemFactory().createMethod(holder, method.getProto(), method.getName());
}
public DexMethod emulatedInterfaceDispatchMethod(
DexClass newInterface, EmulatedDispatchMethodDescriptor descriptor) {
DexMethod method =
emulatedInterfaceDispatchMethod(newInterface.type, descriptor.getInterfaceMethod());
assert newInterface.lookupMethod(method) != null;
return method;
}
public DexMethod ensureEmulatedHolderDispatchMethod(
EmulatedDispatchMethodDescriptor descriptor,
DesugaredLibraryRetargeterInstructionEventConsumer eventConsumer) {
assert eventConsumer != null;
DerivedMethod emulatedDispatchMethod = descriptor.getEmulatedDispatchMethod();
DexClass holderContext =
appView.contextIndependentDefinitionFor(emulatedDispatchMethod.getHolderContext());
DexClass syntheticClass;
if (appView.options().isDesugaredLibraryCompilation()) {
syntheticClass =
appView
.getSyntheticItems()
.getExistingFixedClass(
ignored -> emulatedDispatchMethod.getHolderKind(), holderContext, appView);
DexMethod dispatchMethod =
emulatedHolderDispatchMethod(syntheticClass.type, emulatedDispatchMethod);
assert syntheticClass.lookupMethod(dispatchMethod) != null;
return dispatchMethod;
} else {
DexClass itfClass = ensureEmulatedInterfaceDispatchMethod(descriptor, eventConsumer);
ClasspathOrLibraryClass context = holderContext.asClasspathOrLibraryClass();
assert context != null;
syntheticClass =
appView
.getSyntheticItems()
.ensureFixedClasspathClass(
kinds -> kinds.RETARGET_CLASS,
context,
appView,
classBuilder ->
buildHolderDispatchMethod(classBuilder, itfClass, descriptor, eventConsumer),
eventConsumer::acceptDesugaredLibraryRetargeterDispatchClasspathClass);
}
DexMethod dispatchMethod =
emulatedHolderDispatchMethod(syntheticClass.type, emulatedDispatchMethod);
assert syntheticClass.lookupMethod(dispatchMethod) != null;
return dispatchMethod;
}
public void ensureProgramEmulatedHolderDispatchMethod(
EmulatedDispatchMethodDescriptor descriptor,
DesugaredLibraryRetargeterL8SynthesizerEventConsumer eventConsumer) {
assert eventConsumer != null;
assert appView.options().isDesugaredLibraryCompilation();
DerivedMethod emulatedDispatchMethod = descriptor.getEmulatedDispatchMethod();
DexClass holderContext =
appView.contextIndependentDefinitionFor(emulatedDispatchMethod.getHolderContext());
DexClass itfClass = ensureEmulatedInterfaceDispatchMethod(descriptor, eventConsumer);
appView
.getSyntheticItems()
.ensureFixedClass(
ignored -> emulatedDispatchMethod.getHolderKind(),
holderContext,
appView,
classBuilder -> buildHolderDispatchMethod(classBuilder, itfClass, descriptor, null),
eventConsumer::acceptDesugaredLibraryRetargeterDispatchProgramClass);
}
public DexClass ensureEmulatedInterfaceDispatchMethod(
EmulatedDispatchMethodDescriptor descriptor,
DesugaredLibraryRetargeterInstructionEventConsumer eventConsumer) {
assert eventConsumer != null;
DerivedMethod itfMethod = descriptor.getInterfaceMethod();
DexClass itfContext = appView.contextIndependentDefinitionFor(itfMethod.getHolderContext());
if (appView.options().isDesugaredLibraryCompilation()) {
return appView
.getSyntheticItems()
.getExistingFixedClass(ignored -> itfMethod.getHolderKind(), itfContext, appView);
}
ClasspathOrLibraryClass context = itfContext.asClasspathOrLibraryClass();
assert context != null;
return appView
.getSyntheticItems()
.ensureFixedClasspathClass(
kinds -> kinds.RETARGET_INTERFACE,
context,
appView,
classBuilder -> buildInterfaceDispatchMethod(classBuilder, descriptor),
eventConsumer::acceptDesugaredLibraryRetargeterDispatchClasspathClass);
}
public DexClass ensureEmulatedInterfaceDispatchMethod(
EmulatedDispatchMethodDescriptor descriptor,
DesugaredLibraryRetargeterL8SynthesizerEventConsumer eventConsumer) {
assert appView.options().isDesugaredLibraryCompilation();
assert eventConsumer != null;
DerivedMethod itfMethod = descriptor.getInterfaceMethod();
DexClass itfContext = appView.contextIndependentDefinitionFor(itfMethod.getHolderContext());
return appView
.getSyntheticItems()
.ensureFixedClass(
ignore -> itfMethod.getHolderKind(),
itfContext,
appView,
classBuilder -> buildInterfaceDispatchMethod(classBuilder, descriptor),
eventConsumer::acceptDesugaredLibraryRetargeterDispatchProgramClass);
}
private void buildInterfaceDispatchMethod(
SyntheticClassBuilder<?, ?> classBuilder, EmulatedDispatchMethodDescriptor descriptor) {
classBuilder
.setInterface()
.addMethod(
methodBuilder -> {
DexMethod itfMethod =
emulatedInterfaceDispatchMethod(
classBuilder.getType(), descriptor.getInterfaceMethod());
MethodAccessFlags flags =
MethodAccessFlags.fromSharedAccessFlags(
Constants.ACC_PUBLIC | Constants.ACC_ABSTRACT | Constants.ACC_SYNTHETIC,
false);
methodBuilder
.setName(itfMethod.getName())
.setProto(itfMethod.getProto())
// Will be traced by the enqueuer.
.disableAndroidApiLevelCheck()
.setAccessFlags(flags);
});
}
private <SCB extends SyntheticClassBuilder<?, ?>> void buildHolderDispatchMethod(
SCB classBuilder,
DexClass itfClass,
EmulatedDispatchMethodDescriptor descriptor,
DesugaredLibraryRetargeterInstructionEventConsumer eventConsumer) {
classBuilder.addMethod(
methodBuilder -> {
DexMethod dispatchMethod =
emulatedHolderDispatchMethod(
classBuilder.getType(), descriptor.getEmulatedDispatchMethod());
methodBuilder
.setName(dispatchMethod.getName())
.setProto(dispatchMethod.getProto())
.setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
// Will be traced by the enqueuer.
.disableAndroidApiLevelCheck()
.setCode(
methodSig ->
appView.options().isDesugaredLibraryCompilation()
? generateEmulatedDispatchCfCode(
descriptor, itfClass, methodSig, eventConsumer)
: null);
});
}
private CfCode generateEmulatedDispatchCfCode(
EmulatedDispatchMethodDescriptor descriptor,
DexClass itfClass,
DexMethod methodSig,
DesugaredLibraryRetargeterInstructionEventConsumer eventConsumer) {
DexMethod forwardingMethod = ensureForwardingMethod(descriptor, eventConsumer);
DexMethod itfMethod = emulatedInterfaceDispatchMethod(itfClass, descriptor);
assert descriptor.getDispatchCases().isEmpty();
return new EmulateDispatchSyntheticCfCodeProvider(
methodSig.getHolderType(), forwardingMethod, itfMethod, new LinkedHashMap<>(), appView)
.generateCfCode();
}
}