| // Copyright (c) 2022, 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.synthetic; |
| |
| import com.android.tools.r8.cf.code.CfCheckCast; |
| import com.android.tools.r8.cf.code.CfFrame; |
| import com.android.tools.r8.cf.code.CfIf; |
| import com.android.tools.r8.cf.code.CfInstanceOf; |
| import com.android.tools.r8.cf.code.CfInstruction; |
| import com.android.tools.r8.cf.code.CfInvoke; |
| import com.android.tools.r8.cf.code.CfLabel; |
| import com.android.tools.r8.cf.code.CfLoad; |
| import com.android.tools.r8.cf.code.CfReturn; |
| import com.android.tools.r8.cf.code.CfReturnVoid; |
| import com.android.tools.r8.cf.code.frame.FrameType; |
| import com.android.tools.r8.graph.AppView; |
| import com.android.tools.r8.graph.CfCode; |
| import com.android.tools.r8.graph.DexMethod; |
| import com.android.tools.r8.graph.DexType; |
| import com.android.tools.r8.ir.code.IfType; |
| import com.android.tools.r8.ir.code.ValueType; |
| import java.util.ArrayList; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import org.objectweb.asm.Opcodes; |
| |
| public class EmulateDispatchSyntheticCfCodeProvider extends SyntheticCfCodeProvider { |
| |
| private final DexMethod forwardingMethod; |
| private final DexMethod interfaceMethod; |
| private final LinkedHashMap<DexType, DexMethod> extraDispatchCases; |
| |
| public EmulateDispatchSyntheticCfCodeProvider( |
| DexType holder, |
| DexMethod forwardingMethod, |
| DexMethod interfaceMethod, |
| LinkedHashMap<DexType, DexMethod> extraDispatchCases, |
| AppView<?> appView) { |
| super(appView, holder); |
| this.forwardingMethod = forwardingMethod; |
| this.interfaceMethod = interfaceMethod; |
| this.extraDispatchCases = extraDispatchCases; |
| } |
| |
| @Override |
| public CfCode generateCfCode() { |
| DexType receiverType = forwardingMethod.getParameter(0); |
| List<CfInstruction> instructions = new ArrayList<>(); |
| CfLabel[] labels = new CfLabel[extraDispatchCases.size() + 1]; |
| for (int i = 0; i < labels.length; i++) { |
| labels[i] = new CfLabel(); |
| } |
| int nextLabel = 0; |
| |
| CfFrame frame = |
| CfFrame.builder() |
| .appendLocal(FrameType.initialized(receiverType)) |
| .apply( |
| builder -> { |
| for (DexType parameter : interfaceMethod.getParameters()) { |
| builder.appendLocal(FrameType.initialized(parameter)); |
| } |
| }) |
| .build(); |
| |
| instructions.add(new CfLoad(ValueType.fromDexType(receiverType), 0)); |
| instructions.add(new CfInstanceOf(interfaceMethod.holder)); |
| instructions.add(new CfIf(IfType.EQ, ValueType.INT, labels[nextLabel])); |
| |
| // Branch with library call. |
| instructions.add(new CfLoad(ValueType.fromDexType(receiverType), 0)); |
| instructions.add(new CfCheckCast(interfaceMethod.holder)); |
| loadExtraParameters(instructions); |
| instructions.add(new CfInvoke(Opcodes.INVOKEINTERFACE, interfaceMethod, true)); |
| addReturn(instructions); |
| |
| // SubInterface dispatch (subInterfaces are ordered). |
| for (Map.Entry<DexType, DexMethod> dispatch : extraDispatchCases.entrySet()) { |
| // Type check basic block. |
| instructions.add(labels[nextLabel++]); |
| instructions.add(frame); |
| instructions.add(new CfLoad(ValueType.fromDexType(receiverType), 0)); |
| instructions.add(new CfInstanceOf(dispatch.getKey())); |
| instructions.add(new CfIf(IfType.EQ, ValueType.INT, labels[nextLabel])); |
| |
| // Call basic block. |
| instructions.add(new CfLoad(ValueType.fromDexType(receiverType), 0)); |
| instructions.add(new CfCheckCast(dispatch.getKey())); |
| loadExtraParameters(instructions); |
| instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, dispatch.getValue(), false)); |
| addReturn(instructions); |
| } |
| |
| // Branch with companion call. |
| instructions.add(labels[nextLabel]); |
| instructions.add(frame.clone()); |
| instructions.add(new CfLoad(ValueType.fromDexType(receiverType), 0)); |
| loadExtraParameters(instructions); |
| instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, forwardingMethod, false)); |
| addReturn(instructions); |
| return standardCfCodeFromInstructions(instructions); |
| } |
| |
| private void loadExtraParameters(List<CfInstruction> instructions) { |
| int index = 1; |
| for (DexType type : interfaceMethod.proto.parameters.values) { |
| instructions.add(new CfLoad(ValueType.fromDexType(type), index++)); |
| } |
| } |
| |
| private void addReturn(List<CfInstruction> instructions) { |
| if (interfaceMethod.proto.returnType == appView.dexItemFactory().voidType) { |
| instructions.add(new CfReturnVoid()); |
| } else { |
| instructions.add(new CfReturn(ValueType.fromDexType(interfaceMethod.proto.returnType))); |
| } |
| } |
| } |