blob: bed9095eaa24eb296ea1839e623469f2cd427cbc [file] [log] [blame]
// Copyright (c) 2023, 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.code;
import com.android.tools.r8.dex.code.DexInvokeCustom;
import com.android.tools.r8.dex.code.DexInvokeCustomRange;
import com.android.tools.r8.dex.code.DexInvokeDirect;
import com.android.tools.r8.dex.code.DexInvokeDirectRange;
import com.android.tools.r8.dex.code.DexInvokeInterface;
import com.android.tools.r8.dex.code.DexInvokeInterfaceRange;
import com.android.tools.r8.dex.code.DexInvokePolymorphic;
import com.android.tools.r8.dex.code.DexInvokePolymorphicRange;
import com.android.tools.r8.dex.code.DexInvokeStatic;
import com.android.tools.r8.dex.code.DexInvokeStaticRange;
import com.android.tools.r8.dex.code.DexInvokeSuper;
import com.android.tools.r8.dex.code.DexInvokeSuperRange;
import com.android.tools.r8.dex.code.DexInvokeVirtual;
import com.android.tools.r8.dex.code.DexInvokeVirtualRange;
import com.android.tools.r8.dex.code.DexNewArray;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.graph.lens.MethodLookupResult;
import org.objectweb.asm.Opcodes;
public enum InvokeType {
DIRECT(DexInvokeDirect.OPCODE, DexInvokeDirectRange.OPCODE),
INTERFACE(DexInvokeInterface.OPCODE, DexInvokeInterfaceRange.OPCODE),
STATIC(DexInvokeStatic.OPCODE, DexInvokeStaticRange.OPCODE),
SUPER(DexInvokeSuper.OPCODE, DexInvokeSuperRange.OPCODE),
VIRTUAL(DexInvokeVirtual.OPCODE, DexInvokeVirtualRange.OPCODE),
NEW_ARRAY(DexNewArray.OPCODE, Invoke.NO_SUCH_DEX_INSTRUCTION),
MULTI_NEW_ARRAY(Invoke.NO_SUCH_DEX_INSTRUCTION, Invoke.NO_SUCH_DEX_INSTRUCTION),
CUSTOM(DexInvokeCustom.OPCODE, DexInvokeCustomRange.OPCODE),
POLYMORPHIC(DexInvokePolymorphic.OPCODE, DexInvokePolymorphicRange.OPCODE);
private final int dexOpcode;
private final int dexOpcodeRange;
InvokeType(int dexOpcode, int dexOpcodeRange) {
this.dexOpcode = dexOpcode;
this.dexOpcodeRange = dexOpcodeRange;
}
public static InvokeType fromCfOpcode(
int opcode, DexMethod invokedMethod, DexClassAndMethod context, AppView<?> appView) {
return fromCfOpcode(opcode, invokedMethod, context, appView, appView.codeLens());
}
public static InvokeType fromCfOpcode(
int opcode,
DexMethod invokedMethod,
DexClassAndMethod context,
AppView<?> appView,
GraphLens codeLens) {
switch (opcode) {
case org.objectweb.asm.Opcodes.INVOKEINTERFACE:
return InvokeType.INTERFACE;
case org.objectweb.asm.Opcodes.INVOKESPECIAL:
return fromInvokeSpecial(invokedMethod, context, appView, codeLens);
case org.objectweb.asm.Opcodes.INVOKESTATIC:
return InvokeType.STATIC;
case org.objectweb.asm.Opcodes.INVOKEVIRTUAL:
return appView.dexItemFactory().polymorphicMethods.isPolymorphicInvoke(invokedMethod)
&& !appView.options().shouldDesugarVarHandle()
? InvokeType.POLYMORPHIC
: InvokeType.VIRTUAL;
default:
throw new Unreachable("unknown CfInvoke opcode " + opcode);
}
}
public static InvokeType fromInvokeSpecial(
DexMethod invokedMethod, DexClassAndMethod context, AppView<?> appView, GraphLens codeLens) {
if (invokedMethod.isInstanceInitializer(appView.dexItemFactory())) {
return InvokeType.DIRECT;
}
GraphLens graphLens = appView.graphLens();
DexMethod originalContext =
graphLens.getOriginalMethodSignature(context.getReference(), codeLens);
if (invokedMethod.getHolderType() != originalContext.getHolderType()) {
if (appView.options().isGeneratingDex()
&& appView.options().canUseNestBasedAccess()
&& context.getHolder().isInANest()) {
DexClass holderType = appView.definitionFor(invokedMethod.getHolderType());
if (holderType != null
&& holderType.isInANest()
&& holderType.isInSameNest(context.getHolder())) {
return InvokeType.DIRECT;
}
}
return InvokeType.SUPER;
}
MethodLookupResult lookupResult =
graphLens.lookupMethod(invokedMethod, context.getReference(), InvokeType.DIRECT);
if (lookupResult.getType().isStatic()) {
// This method has been staticized. The original invoke-type is DIRECT.
return InvokeType.DIRECT;
}
if (lookupResult.getType().isVirtual()) {
// This method has been publicized. The original invoke-type is DIRECT.
return InvokeType.DIRECT;
}
DexEncodedMethod definition = context.getHolder().lookupMethod(lookupResult.getReference());
if (definition == null) {
return InvokeType.SUPER;
}
// If the definition was moved to the current context from a super class due to vertical class
// merging, then this used to be an invoke-super.
DexType originalHolderOfDefinition =
graphLens.getOriginalMethodSignature(definition.getReference(), codeLens).getHolderType();
if (originalHolderOfDefinition != originalContext.getHolderType()) {
return InvokeType.SUPER;
}
boolean originalContextIsInterface =
context.getHolder().isInterface()
|| (appView.hasVerticallyMergedClasses()
&& appView
.verticallyMergedClasses()
.hasInterfaceBeenMergedIntoSubtype(originalContext.getHolderType()));
if (originalContextIsInterface) {
// On interfaces invoke-special should be mapped to invoke-super if the invoke-special
// instruction is used to target a default interface method.
if (definition.belongsToVirtualPool()) {
return InvokeType.SUPER;
}
} else {
// Due to desugaring of invoke-special instructions that target virtual methods, this should
// never target a virtual method.
assert definition.isPrivate() || lookupResult.getType().isVirtual();
}
return InvokeType.DIRECT;
}
public int getCfOpcode() {
switch (this) {
case DIRECT:
return org.objectweb.asm.Opcodes.INVOKESPECIAL;
case INTERFACE:
return org.objectweb.asm.Opcodes.INVOKEINTERFACE;
case POLYMORPHIC:
return org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
case STATIC:
return org.objectweb.asm.Opcodes.INVOKESTATIC;
case SUPER:
return org.objectweb.asm.Opcodes.INVOKESPECIAL;
case VIRTUAL:
return Opcodes.INVOKEVIRTUAL;
case NEW_ARRAY:
case MULTI_NEW_ARRAY:
default:
throw new Unreachable();
}
}
public int getDexOpcode() {
assert dexOpcode >= 0;
return dexOpcode;
}
public int getDexOpcodeRange() {
assert dexOpcodeRange >= 0;
return dexOpcodeRange;
}
public boolean isDirect() {
return this == DIRECT;
}
public boolean isInterface() {
return this == INTERFACE;
}
public boolean isStatic() {
return this == STATIC;
}
public boolean isSuper() {
return this == SUPER;
}
public boolean isVirtual() {
return this == VIRTUAL;
}
public MethodHandleType toMethodHandle(DexMethod targetMethod) {
switch (this) {
case STATIC:
return MethodHandleType.INVOKE_STATIC;
case VIRTUAL:
return MethodHandleType.INVOKE_INSTANCE;
case DIRECT:
if (targetMethod.name.toString().equals("<init>")) {
return MethodHandleType.INVOKE_CONSTRUCTOR;
} else {
return MethodHandleType.INVOKE_DIRECT;
}
case INTERFACE:
return MethodHandleType.INVOKE_INTERFACE;
case SUPER:
return MethodHandleType.INVOKE_SUPER;
default:
throw new Unreachable("Conversion to method handle with unexpected invoke type: " + this);
}
}
}