blob: 85154ce38d9c0ce81408323d12e2479ca41cafd5 [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.cf.code.CfFieldInstruction;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.DesugarDescription;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.EmulatedDispatchMethodDescriptor;
import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
import java.util.Collections;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import org.objectweb.asm.Opcodes;
public class DesugaredLibraryRetargeter implements CfInstructionDesugaring {
private final AppView<?> appView;
private final DesugaredLibraryRetargeterSyntheticHelper syntheticHelper;
private final Map<DexField, DexField> staticFieldRetarget;
private final Map<DexMethod, DexMethod> covariantRetarget;
private final Map<DexMethod, DexMethod> staticRetarget;
private final Map<DexMethod, DexMethod> nonEmulatedVirtualRetarget;
private final Map<DexMethod, EmulatedDispatchMethodDescriptor> emulatedVirtualRetarget;
public DesugaredLibraryRetargeter(AppView<?> appView) {
this.appView = appView;
this.syntheticHelper = new DesugaredLibraryRetargeterSyntheticHelper(appView);
MachineDesugaredLibrarySpecification specification =
appView.options().machineDesugaredLibrarySpecification;
staticFieldRetarget = specification.getStaticFieldRetarget();
covariantRetarget = specification.getCovariantRetarget();
staticRetarget = specification.getStaticRetarget();
nonEmulatedVirtualRetarget = specification.getNonEmulatedVirtualRetarget();
emulatedVirtualRetarget = specification.getEmulatedVirtualRetarget();
}
// Used by the ListOfBackportedMethods utility.
public void visit(Consumer<DexMethod> consumer) {
staticRetarget.keySet().forEach(consumer);
nonEmulatedVirtualRetarget.keySet().forEach(consumer);
emulatedVirtualRetarget.keySet().forEach(consumer);
}
@Override
public DesugarDescription compute(CfInstruction instruction, ProgramMethod context) {
if (instruction.isStaticFieldGet()) {
return computeStaticFieldGetDescription(instruction, context);
}
if (instruction.isInvoke()) {
return computeInvokeDescription(instruction, context);
}
return DesugarDescription.nothing();
}
private DesugarDescription computeInvokeDescription(
CfInstruction instruction, ProgramMethod context) {
if (appView.dexItemFactory().multiDexTypes.contains(context.getContextType())) {
return DesugarDescription.nothing();
}
CfInvoke cfInvoke = instruction.asInvoke();
DexMethod invokedMethod = cfInvoke.getMethod();
AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
MethodResolutionResult resolutionResult =
appInfo.resolveMethodLegacy(invokedMethod, cfInvoke.isInterface());
if (!resolutionResult.isSingleResolution()) {
return DesugarDescription.nothing();
}
assert resolutionResult.getSingleTarget() != null;
DexMethod singleTarget = resolutionResult.getSingleTarget().getReference();
if (cfInvoke.isInvokeStatic()) {
DexMethod retarget = staticRetarget.get(singleTarget);
return ensureInvokeRetargetingResult(retarget);
}
DesugarDescription retarget = computeNonStaticRetarget(singleTarget, false);
if (!retarget.needsDesugaring()) {
return DesugarDescription.nothing();
}
if (cfInvoke.isInvokeSuper(context.getHolderType())) {
DexClassAndMethod superTarget =
appInfo.lookupSuperTarget(invokedMethod, context, appView, appInfo);
if (superTarget != null) {
assert !superTarget.getDefinition().isStatic();
return computeNonStaticRetarget(superTarget.getReference(), true);
}
}
return retarget;
}
private DesugarDescription createWithTarget(
BiFunction<CfInstructionDesugaringEventConsumer, MethodProcessingContext, DexMethod>
methodProvider) {
return DesugarDescription.builder()
.setDesugarRewrite(
(freshLocalProvider,
localStackAllocator,
eventConsumer,
context,
methodProcessingContext,
desugarings,
dexItemFactory) -> {
DexMethod newInvokeTarget =
methodProvider.apply(eventConsumer, methodProcessingContext);
assert appView.definitionFor(newInvokeTarget.getHolderType()) != null;
assert !appView.definitionFor(newInvokeTarget.getHolderType()).isInterface();
return Collections.singletonList(
new CfInvoke(Opcodes.INVOKESTATIC, newInvokeTarget, false));
})
.build();
}
private DesugarDescription computeStaticFieldGetDescription(
CfInstruction instruction, ProgramMethod context) {
CfFieldInstruction fieldInstruction = instruction.asFieldInstruction();
DexField fieldRetarget = fieldRetarget(fieldInstruction, context);
if (fieldRetarget == null) {
return DesugarDescription.nothing();
}
return DesugarDescription.builder()
.setDesugarRewrite(
(freshLocalProvider,
localStackAllocator,
eventConsumer,
context1,
methodProcessingContext,
desugarings,
dexItemFactory) ->
Collections.singletonList(fieldInstruction.createWithField(fieldRetarget)))
.build();
}
private DexField fieldRetarget(CfFieldInstruction fieldInstruction, ProgramMethod context) {
DexEncodedField resolvedField =
appView
.appInfoForDesugaring()
.resolveField(fieldInstruction.getField(), context)
.getResolvedField();
if (resolvedField != null) {
assert resolvedField.isStatic()
|| !staticFieldRetarget.containsKey(resolvedField.getReference());
return staticFieldRetarget.get(resolvedField.getReference());
}
return null;
}
DesugarDescription ensureInvokeRetargetingResult(DexMethod retarget) {
if (retarget == null) {
return DesugarDescription.nothing();
}
return createWithTarget(
(eventConsumer, methodProcessingContext) ->
syntheticHelper.ensureRetargetMethod(retarget, eventConsumer));
}
private DesugarDescription computeNonStaticRetarget(DexMethod singleTarget, boolean superInvoke) {
EmulatedDispatchMethodDescriptor descriptor = emulatedVirtualRetarget.get(singleTarget);
if (descriptor != null) {
return createWithTarget(
(eventConsumer, methodProcessingContext) ->
superInvoke
? syntheticHelper.ensureForwardingMethod(descriptor, eventConsumer)
: syntheticHelper.ensureEmulatedHolderDispatchMethod(descriptor, eventConsumer));
}
if (covariantRetarget.containsKey(singleTarget)) {
return createWithTarget(
(eventConsumer, methodProcessingContext) ->
syntheticHelper.ensureCovariantRetargetMethod(
singleTarget,
covariantRetarget.get(singleTarget),
eventConsumer,
methodProcessingContext));
}
return ensureInvokeRetargetingResult(nonEmulatedVirtualRetarget.get(singleTarget));
}
}