| // 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(); |
| } |
| } |