blob: 5c6d6c1a7fadf624ac2709d69f04a2c1a85bdd17 [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;
import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeter.InvokeRetargetingResult.NO_REWRITING;
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.AppView;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
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.CfInstructionDesugaringCollection;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.FreshLocalProvider;
import com.android.tools.r8.ir.desugar.LocalStackAllocator;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterInstructionEventConsumer;
import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import org.objectweb.asm.Opcodes;
public class DesugaredLibraryRetargeter implements CfInstructionDesugaring {
private final AppView<?> appView;
private final DesugaredLibraryRetargeterSyntheticHelper syntheticHelper;
private final RetargetingInfo retargetingInfo;
private final Map<DexMethod, DexMethod> retargetLibraryMember;
private final Map<DexString, List<DexMethod>> nonFinalHolderRewrites;
private final DexClassAndMethodSet emulatedDispatchMethods;
public DesugaredLibraryRetargeter(AppView<?> appView) {
this.appView = appView;
this.syntheticHelper = new DesugaredLibraryRetargeterSyntheticHelper(appView);
retargetingInfo = RetargetingInfo.get(appView);
retargetLibraryMember = retargetingInfo.getRetargetLibraryMember();
nonFinalHolderRewrites = retargetingInfo.getNonFinalHolderRewrites();
emulatedDispatchMethods = retargetingInfo.getEmulatedDispatchMethods();
}
// Used by the ListOfBackportedMethods utility.
public void visit(Consumer<DexMethod> consumer) {
retargetLibraryMember.keySet().forEach(consumer);
}
public RetargetingInfo getRetargetingInfo() {
return retargetingInfo;
}
@Override
public Collection<CfInstruction> desugarInstruction(
CfInstruction instruction,
FreshLocalProvider freshLocalProvider,
LocalStackAllocator localStackAllocator,
CfInstructionDesugaringEventConsumer eventConsumer,
ProgramMethod context,
MethodProcessingContext methodProcessingContext,
CfInstructionDesugaringCollection desugaringCollection,
DexItemFactory dexItemFactory) {
InvokeRetargetingResult invokeRetargetingResult = computeNewInvokeTarget(instruction, context);
if (!invokeRetargetingResult.hasNewInvokeTarget()) {
return null;
}
DexMethod newInvokeTarget = invokeRetargetingResult.getNewInvokeTarget(eventConsumer);
return Collections.singletonList(
new CfInvoke(Opcodes.INVOKESTATIC, newInvokeTarget, instruction.asInvoke().isInterface()));
}
@Override
public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
return computeNewInvokeTarget(instruction, context).hasNewInvokeTarget();
}
static class InvokeRetargetingResult {
static InvokeRetargetingResult NO_REWRITING =
new InvokeRetargetingResult(false, ignored -> null);
private final boolean hasNewInvokeTarget;
private final Function<DesugaredLibraryRetargeterInstructionEventConsumer, DexMethod>
newInvokeTargetSupplier;
static InvokeRetargetingResult createInvokeRetargetingResult(DexMethod retarget) {
if (retarget == null) {
return NO_REWRITING;
}
return new InvokeRetargetingResult(true, ignored -> retarget);
}
private InvokeRetargetingResult(
boolean hasNewInvokeTarget,
Function<DesugaredLibraryRetargeterInstructionEventConsumer, DexMethod>
newInvokeTargetSupplier) {
this.hasNewInvokeTarget = hasNewInvokeTarget;
this.newInvokeTargetSupplier = newInvokeTargetSupplier;
}
public boolean hasNewInvokeTarget() {
return hasNewInvokeTarget;
}
public DexMethod getNewInvokeTarget(
DesugaredLibraryRetargeterInstructionEventConsumer eventConsumer) {
assert hasNewInvokeTarget();
return newInvokeTargetSupplier.apply(eventConsumer);
}
}
private InvokeRetargetingResult computeNewInvokeTarget(
CfInstruction instruction, ProgramMethod context) {
if (retargetLibraryMember.isEmpty() || !instruction.isInvoke()) {
return NO_REWRITING;
}
CfInvoke cfInvoke = instruction.asInvoke();
DexMethod invokedMethod = cfInvoke.getMethod();
InvokeRetargetingResult retarget =
computeRetargetedMethod(invokedMethod, cfInvoke.isInterface());
if (!retarget.hasNewInvokeTarget()) {
return NO_REWRITING;
}
if (cfInvoke.isInvokeSuper(context.getHolderType())
&& matchesNonFinalHolderRewrite(invokedMethod)) {
DexClassAndMethod superTarget =
appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context);
// Final methods can be rewritten as a normal invoke.
if (superTarget != null && !superTarget.getAccessFlags().isFinal()) {
return InvokeRetargetingResult.createInvokeRetargetingResult(
appView.options().desugaredLibraryConfiguration.retargetMethod(superTarget, appView));
}
}
return retarget;
}
private InvokeRetargetingResult computeRetargetedMethod(
DexMethod invokedMethod, boolean isInterface) {
InvokeRetargetingResult invokeRetargetingResult = computeRetargetLibraryMember(invokedMethod);
if (!invokeRetargetingResult.hasNewInvokeTarget()) {
if (!matchesNonFinalHolderRewrite(invokedMethod)) {
return NO_REWRITING;
}
// We need to force resolution, even on d8, to know if the invoke has to be rewritten.
MethodResolutionResult resolutionResult =
appView.appInfoForDesugaring().resolveMethod(invokedMethod, isInterface);
if (resolutionResult.isFailedResolution()) {
return NO_REWRITING;
}
DexEncodedMethod singleTarget = resolutionResult.getSingleTarget();
assert singleTarget != null;
invokeRetargetingResult = computeRetargetLibraryMember(singleTarget.getReference());
}
return invokeRetargetingResult;
}
private InvokeRetargetingResult computeRetargetLibraryMember(DexMethod method) {
DexClassAndMethod emulatedMethod = emulatedDispatchMethods.get(method);
if (emulatedMethod != null) {
assert !emulatedMethod.getAccessFlags().isStatic();
return new InvokeRetargetingResult(
true,
eventConsumer -> {
DexType newHolder =
syntheticHelper.ensureEmulatedHolderDispatchMethod(emulatedMethod, eventConsumer)
.type;
return computeRetargetMethod(
method, emulatedMethod.getAccessFlags().isStatic(), newHolder);
});
}
return InvokeRetargetingResult.createInvokeRetargetingResult(retargetLibraryMember.get(method));
}
private boolean matchesNonFinalHolderRewrite(DexMethod method) {
List<DexMethod> dexMethods = nonFinalHolderRewrites.get(method.name);
if (dexMethods == null) {
return false;
}
for (DexMethod dexMethod : dexMethods) {
if (method.match(dexMethod)) {
return true;
}
}
return false;
}
DexMethod computeRetargetMethod(DexMethod method, boolean isStatic, DexType newHolder) {
DexItemFactory factory = appView.dexItemFactory();
DexProto newProto = isStatic ? method.getProto() : factory.prependHolderToProto(method);
return factory.createMethod(newHolder, newProto, method.getName());
}
}