diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt index d413f600294..a66d5e5f2cb 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.kt @@ -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 } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt index 556980ff69a..afcce5fd10b 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt @@ -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() diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrCallableMethod.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrCallableMethod.kt index 5029d50d185..e544b24e681 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrCallableMethod.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrCallableMethod.kt @@ -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, 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 = + signature.valueParameters.filter { it.kind == JvmMethodParameterKind.VALUE }.map { it.asmType } + override fun toString(): String = "${Printer.OPCODES[invokeOpcode]} $owner.$asmMethod" + (if (isInterfaceMethod) " (itf)" else "") } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt index 69d1b5ef1e8..1a14d556cdc 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/MethodSignatureMapper.kt @@ -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 + } } diff --git a/compiler/testData/codegen/box/jvmPackageName/rootPackage.kt b/compiler/testData/codegen/box/jvmPackageName/rootPackage.kt index 5bbb82d7b09..9a2c21a959a 100644 --- a/compiler/testData/codegen/box/jvmPackageName/rootPackage.kt +++ b/compiler/testData/codegen/box/jvmPackageName/rootPackage.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // WITH_RUNTIME diff --git a/compiler/testData/codegen/box/jvmPackageName/simple.kt b/compiler/testData/codegen/box/jvmPackageName/simple.kt index 334c99eed27..aecccdb64cd 100644 --- a/compiler/testData/codegen/box/jvmPackageName/simple.kt +++ b/compiler/testData/codegen/box/jvmPackageName/simple.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // WITH_RUNTIME diff --git a/compiler/testData/codegen/box/jvmPackageName/withJvmName.kt b/compiler/testData/codegen/box/jvmPackageName/withJvmName.kt index 5a6e39ffc37..ee9d09a4e98 100644 --- a/compiler/testData/codegen/box/jvmPackageName/withJvmName.kt +++ b/compiler/testData/codegen/box/jvmPackageName/withJvmName.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // WITH_RUNTIME diff --git a/compiler/testData/compileKotlinAgainstKotlin/jvmStaticInObject.kt b/compiler/testData/compileKotlinAgainstKotlin/jvmStaticInObject.kt index 5e67b2cd53a..7a4693eac61 100644 --- a/compiler/testData/compileKotlinAgainstKotlin/jvmStaticInObject.kt +++ b/compiler/testData/compileKotlinAgainstKotlin/jvmStaticInObject.kt @@ -1,3 +1,4 @@ +// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // FILE: A.kt