JVM IR: implement MethodSignatureMapper.mapToCallableMethod directly

Instead of calling enormously complicated
KotlinTypeMapper.mapToCallableMethod, where most of special cases are
handled in the IR backend via lowerings.
This commit is contained in:
Alexander Udalov
2019-08-20 16:34:37 +02:00
parent e0823e20c7
commit aea9642ea0
8 changed files with 98 additions and 48 deletions
@@ -1493,29 +1493,19 @@ class KotlinTypeMapper @JvmOverloads constructor(
private fun findSuperDeclaration(descriptor: FunctionDescriptor, isSuperCall: Boolean): FunctionDescriptor {
var current = descriptor
while (current.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
val overridden = current.overriddenDescriptors
if (overridden.isEmpty()) {
throw IllegalStateException("Fake override should have at least one overridden descriptor: $current")
}
var classCallable: FunctionDescriptor? = null
for (overriddenFunction in overridden) {
if (!isInterface(overriddenFunction.containingDeclaration)) {
classCallable = overriddenFunction
break
}
}
val classCallable = current.overriddenDescriptors.firstOrNull { !isInterface(it.containingDeclaration) }
if (classCallable != null) {
//prefer class callable cause of else branch
current = classCallable
continue
} else if (isSuperCall && !current.hasJvmDefaultAnnotation() && !isInterface(current.containingDeclaration)) {
}
if (isSuperCall && !current.hasJvmDefaultAnnotation() && !isInterface(current.containingDeclaration)) {
//Don't unwrap fake overrides from class to interface cause substituted override would be implicitly generated
return current
}
current = overridden.iterator().next()
current = current.overriddenDescriptors.firstOrNull()
?: error("Fake override should have at least one overridden descriptor: $current")
}
return current
}
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.kotlin.utils.keysToMap
@@ -320,8 +321,7 @@ class ExpressionCodegen(
classCodegen.context.irIntrinsics.getIntrinsic(expression.symbol)
?.invoke(expression, this, data)?.let { return it.coerce(expression.type) }
val isSuperCall = (expression as? IrCall)?.superQualifier != null
val callable = methodSignatureMapper.mapToCallableMethod(expression, isSuperCall)
val callable = methodSignatureMapper.mapToCallableMethod(expression)
val callee = expression.symbol.owner
val callGenerator = getOrCreateCallGenerator(expression, data)
val asmType = if (expression is IrConstructorCall) typeMapper.mapTypeAsDeclaration(expression.type) else expression.asmType
@@ -357,24 +357,14 @@ class ExpressionCodegen(
}
expression.dispatchReceiver?.let { receiver ->
callGenerator.genValueAndPut(
callee.dispatchReceiverParameter!!,
receiver,
if (isSuperCall) receiver.asmType else callable.dispatchReceiverType
?: throw AssertionError("No dispatch receiver type: ${expression.render()}"),
this,
data
)
val type = if ((expression as? IrCall)?.superQualifier != null) receiver.asmType else callable.owner
callGenerator.genValueAndPut(callee.dispatchReceiverParameter!!, receiver, type, this, data)
}
expression.extensionReceiver?.let { receiver ->
callGenerator.genValueAndPut(
callee.extensionReceiverParameter!!,
receiver,
callable.extensionReceiverType!!,
this,
data
)
val type = callable.signature.valueParameters.singleOrNull { it.kind == JvmMethodParameterKind.RECEIVER }?.asmType
?: error("No single extension receiver parameter: ${callable.signature.valueParameters}")
callGenerator.genValueAndPut(callee.extensionReceiverParameter!!, receiver, type, this, data)
}
callGenerator.beforeValueParametersStart()
@@ -5,19 +5,23 @@
package org.jetbrains.kotlin.backend.jvm.codegen
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.Method
import org.jetbrains.org.objectweb.asm.util.Printer
class IrCallableMethod(
val owner: Type,
val valueParameterTypes: List<Type>,
val invokeOpcode: Int,
val asmMethod: Method,
val dispatchReceiverType: Type?,
val extensionReceiverType: Type?,
val signature: JvmMethodSignature,
val isInterfaceMethod: Boolean
) {
val asmMethod: Method = signature.asmMethod
val valueParameterTypes: List<Type> =
signature.valueParameters.filter { it.kind == JvmMethodParameterKind.VALUE }.map { it.asmType }
override fun toString(): String =
"${Printer.OPCODES[invokeOpcode]} $owner.$asmMethod" + (if (isInterfaceMethod) " (itf)" else "")
}
@@ -6,20 +6,28 @@
package org.jetbrains.kotlin.backend.jvm.codegen
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.ir.hasJvmDefault
import org.jetbrains.kotlin.codegen.OwnerKind
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.util.isFakeOverride
import org.jetbrains.kotlin.ir.util.isInterface
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.load.java.getOverriddenBuiltinReflectingJvmDescriptor
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.Method
class MethodSignatureMapper(context: JvmBackendContext) {
private val kotlinTypeMapper: KotlinTypeMapper = context.state.typeMapper
private val typeMapper: IrTypeMapper = context.typeMapper
private val kotlinTypeMapper: KotlinTypeMapper = typeMapper.kotlinTypeMapper
fun mapAsmMethod(irFunction: IrFunction): Method =
kotlinTypeMapper.mapAsmMethod(irFunction.descriptor)
@@ -48,10 +56,70 @@ class MethodSignatureMapper(context: JvmBackendContext) {
fun mapSignatureWithGeneric(f: IrFunction, kind: OwnerKind): JvmMethodGenericSignature =
kotlinTypeMapper.mapSignatureWithGeneric(f.descriptor, kind)
fun mapToCallableMethod(expression: IrFunctionAccessExpression, superCall: Boolean): IrCallableMethod =
with(kotlinTypeMapper.mapToCallableMethod(expression.descriptor, superCall)) {
IrCallableMethod(
owner, valueParameterTypes, invokeOpcode, getAsmMethod(), dispatchReceiverType, extensionReceiverType, isInterfaceMethod
)
fun mapToCallableMethod(expression: IrFunctionAccessExpression): IrCallableMethod {
val callee = expression.symbol.owner
val calleeParent = callee.parent
if (calleeParent !is IrClass) {
// Non-class parent is only possible for intrinsics created in IrBuiltIns, such as dataClassArrayMemberHashCode. In that case,
// we still need to return some IrCallableMethod with some owner instance, but that owner will be ignored at the call site.
// Here we return a fake type, but this needs to be refactored so that we never call mapToCallableMethod on intrinsics.
// TODO: get rid of fake owner here
val fakeOwner = Type.getObjectType("kotlin/internal/ir/Intrinsic")
return IrCallableMethod(fakeOwner, Opcodes.INVOKESTATIC, mapSignatureSkipGeneric(callee), false)
}
val owner = typeMapper.mapClass(calleeParent)
if (callee !is IrSimpleFunction) {
check(callee is IrConstructor) { "Function must be a simple function or a constructor: ${callee.render()}" }
return IrCallableMethod(owner, Opcodes.INVOKESPECIAL, mapSignatureSkipGeneric(callee), false)
}
val isInterface = calleeParent.isJvmInterface
val isSuperCall = (expression as? IrCall)?.superQualifier != null
val invokeOpcode = when {
callee.dispatchReceiverParameter == null -> Opcodes.INVOKESTATIC
isSuperCall -> Opcodes.INVOKESPECIAL
isInterface -> Opcodes.INVOKEINTERFACE
Visibilities.isPrivate(callee.visibility) && !callee.isSuspend -> Opcodes.INVOKESPECIAL
else -> Opcodes.INVOKEVIRTUAL
}
val declaration = findSuperDeclaration(callee, isSuperCall)
val signature = mapOverriddenSpecialBuiltinIfNeeded(declaration, isSuperCall)
?: mapSignatureSkipGeneric(declaration)
return IrCallableMethod(owner, invokeOpcode, signature, isInterface)
}
// TODO: get rid of this (probably via some special lowering)
private fun mapOverriddenSpecialBuiltinIfNeeded(callee: IrFunction, superCall: Boolean): JvmMethodSignature? {
val overriddenSpecialBuiltinFunction = callee.descriptor.original.getOverriddenBuiltinReflectingJvmDescriptor()
if (overriddenSpecialBuiltinFunction != null && !superCall) {
return kotlinTypeMapper.mapSignatureSkipGeneric(overriddenSpecialBuiltinFunction.original, OwnerKind.IMPLEMENTATION)
}
return null
}
// Copied from KotlinTypeMapper.findSuperDeclaration.
private fun findSuperDeclaration(function: IrSimpleFunction, isSuperCall: Boolean): IrSimpleFunction {
var current = function
while (current.isFakeOverride) {
// TODO: probably isJvmInterface instead of isInterface, here and in KotlinTypeMapper
val classCallable = current.overriddenSymbols.firstOrNull { !it.owner.parentAsClass.isInterface }?.owner
if (classCallable != null) {
current = classCallable
continue
}
if (isSuperCall && !current.hasJvmDefault() && !current.parentAsClass.isInterface) {
return current
}
current = current.overriddenSymbols.firstOrNull()?.owner
?: error("Fake override should have at least one overridden descriptor: ${current.render()}")
}
return current
}
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_RUNTIME
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_RUNTIME
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_RUNTIME
@@ -1,3 +1,4 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// FILE: A.kt