diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.kt index 6630cc88f07..38fc7d23047 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.kt @@ -7,7 +7,6 @@ package org.jetbrains.kotlin.codegen.inline import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.context.EnclosedValueDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.resolve.jvm.AsmTypes.* import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.util.OperatorNameConventions @@ -101,14 +100,6 @@ abstract class DefaultLambda( final override lateinit var invokeMethod: Method private set - private lateinit var invokeMethodDescriptor: FunctionDescriptor - - override val invokeMethodParameters: List - get() = invokeMethodDescriptor.valueParameters.map { it.returnType } - - override val invokeMethodReturnType: KotlinType? - get() = invokeMethodDescriptor.returnType - final override lateinit var capturedVars: List private set @@ -123,10 +114,10 @@ abstract class DefaultLambda( val isPropertyReference = superName in PROPERTY_REFERENCE_SUPER_CLASSES val isFunctionReference = superName == FUNCTION_REFERENCE.internalName || superName == FUNCTION_REFERENCE_IMPL.internalName - val descriptor = Type.getMethodDescriptor(Type.VOID_TYPE, *capturedArgs) - val constructor = getMethodNode(classBytes, "", descriptor, lambdaClassType)?.node + val constructorDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, *capturedArgs) + val constructor = getMethodNode(classBytes, "", constructorDescriptor, lambdaClassType)?.node assert(constructor != null || capturedArgs.isEmpty()) { - "Can't find non-default constructor $descriptor for default lambda $lambdaClassType" + "Can't find non-default constructor $constructorDescriptor for default lambda $lambdaClassType" } capturedVars = if (isFunctionReference || isPropertyReference) @@ -140,11 +131,12 @@ abstract class DefaultLambda( }?.toList() ?: emptyList() isBoundCallableReference = (isFunctionReference || isPropertyReference) && capturedVars.isNotEmpty() - invokeMethodDescriptor = findInvokeMethodDescriptor(isPropertyReference) - val methodName = (if (isPropertyReference) OperatorNameConventions.GET else OperatorNameConventions.INVOKE).asString() - val signature = mapAsmSignature(sourceCompiler, invokeMethodDescriptor) - node = getMethodNode(classBytes, methodName, signature.descriptor, lambdaClassType, signatureAmbiguity = true) - ?: error("Can't find method '$signature' in '${lambdaClassType.internalName}'") + val invokeName = (if (isPropertyReference) OperatorNameConventions.GET else OperatorNameConventions.INVOKE).asString() + val invokeDescriptor = mapAsmDescriptor(sourceCompiler, isPropertyReference) + // TODO: `signatureAmbiguity = true` ignores the argument types from `invokeDescriptor` and only looks at the count. + // This effectively masks incorrect results from `mapAsmDescriptor`, which hopefully won't manifest in another way. + node = getMethodNode(classBytes, invokeName, invokeDescriptor, lambdaClassType, signatureAmbiguity = true) + ?: error("Can't find method '$invokeName$invokeDescriptor' in '${lambdaClassType.internalName}'") invokeMethod = Method(node.node.name, node.node.desc) if (needReification) { //nested classes could also require reification @@ -152,10 +144,7 @@ abstract class DefaultLambda( } } - protected abstract fun mapAsmSignature(sourceCompiler: SourceCompilerForInline, descriptor: FunctionDescriptor): Method - - // TODO: get rid of this; descriptors should *only* be used by PsiDefaultLambda - protected abstract fun findInvokeMethodDescriptor(isPropertyReference: Boolean): FunctionDescriptor + protected abstract fun mapAsmDescriptor(sourceCompiler: SourceCompilerForInline, isPropertyReference: Boolean): String private companion object { val PROPERTY_REFERENCE_SUPER_CLASSES = diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/PsiInlineCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/PsiInlineCodegen.kt index e2aa7860d56..38d4114e5c9 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/PsiInlineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/PsiInlineCodegen.kt @@ -343,14 +343,22 @@ class PsiDefaultLambda( offset: Int, needReification: Boolean ) : DefaultLambda(capturedArgs, parameterDescriptor.isCrossinline, offset, needReification) { - override fun mapAsmSignature(sourceCompiler: SourceCompilerForInline, descriptor: FunctionDescriptor): Method = - sourceCompiler.state.typeMapper.mapSignatureSkipGeneric(descriptor).asmMethod + private lateinit var invokeMethodDescriptor: FunctionDescriptor - override fun findInvokeMethodDescriptor(isPropertyReference: Boolean): FunctionDescriptor = - parameterDescriptor.type.memberScope + override val invokeMethodParameters: List + get() = invokeMethodDescriptor.explicitParameters.map { it.returnType } + + override val invokeMethodReturnType: KotlinType? + get() = invokeMethodDescriptor.returnType + + override fun mapAsmDescriptor(sourceCompiler: SourceCompilerForInline, isPropertyReference: Boolean): String { + invokeMethodDescriptor = parameterDescriptor.type.memberScope .getContributedFunctions(OperatorNameConventions.INVOKE, NoLookupLocation.FROM_BACKEND) .single().let { - // property reference generates erased 'get' method + // Property references only have an erased `get` method, while function references and lambdas + // have a non-erased `invoke` with an erased synthetic bridge, and we want to inline the former. if (isPropertyReference) it.original else it } + return sourceCompiler.state.typeMapper.mapSignatureSkipGeneric(invokeMethodDescriptor).asmMethod.descriptor + } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt index 32a248cfade..5faa21bea18 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt @@ -14,17 +14,16 @@ import org.jetbrains.kotlin.codegen.ValueKind import org.jetbrains.kotlin.codegen.inline.* import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor -import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.descriptors.* import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.types.classOrNull +import org.jetbrains.kotlin.ir.types.IrTypeProjection import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.Method import org.jetbrains.org.objectweb.asm.tree.MethodNode @@ -234,18 +233,33 @@ class IrExpressionLambdaImpl( class IrDefaultLambda( override val lambdaClassType: Type, capturedArgs: Array, - irValueParameter: IrValueParameter, + private val irValueParameter: IrValueParameter, offset: Int, needReification: Boolean ) : DefaultLambda(capturedArgs, irValueParameter.isCrossinline, offset, needReification) { - // TODO: this always produces an erased signature, but it should be non-erased if this isn't a property reference - private val invoke = irValueParameter.type.classOrNull!!.owner.functions.single { it.name == OperatorNameConventions.INVOKE } + private lateinit var typeArguments: List - override fun mapAsmSignature(sourceCompiler: SourceCompilerForInline, descriptor: FunctionDescriptor): Method = - (sourceCompiler as IrSourceCompilerForInline).codegen.context.methodSignatureMapper.mapSignatureSkipGeneric(invoke).asmMethod + override val invokeMethodParameters: List + get() = typeArguments.dropLast(1).map { it.toIrBasedKotlinType() } - override fun findInvokeMethodDescriptor(isPropertyReference: Boolean): FunctionDescriptor = - invoke.toIrBasedDescriptor() + override val invokeMethodReturnType: KotlinType + get() = typeArguments.last().toIrBasedKotlinType() + + override fun mapAsmDescriptor(sourceCompiler: SourceCompilerForInline, isPropertyReference: Boolean): String { + val context = (sourceCompiler as IrSourceCompilerForInline).codegen.context + typeArguments = (irValueParameter.type as IrSimpleType).arguments.let { + // Property references only have an erased `get` method, while function references and lambdas + // have a non-erased `invoke` with an erased synthetic bridge, and we want to inline the former. + if (isPropertyReference) { + List(it.size) { context.irBuiltIns.anyNType } + } else { + it.map { argument -> (argument as IrTypeProjection).type } + } + } + // TODO: while technically only the number of arguments here matters right now (see DefaultLambdaInfo.generateLambdaBody), + // it would be better to map to a non-erased signature if not a property reference. + return Type.getMethodDescriptor(AsmTypes.OBJECT_TYPE, *Array(typeArguments.size - 1) { AsmTypes.OBJECT_TYPE }) + } } fun IrExpression.isInlineIrExpression() =