JVM_IR: try to load mangled invoke from default lambdas
Old compiler versions still won't be able to load default lambdas generated by JVM_IR, but this way we avoid incorrect behavior of function references taking inline class types that unbox to Any. #KT-46601 Fixed
This commit is contained in:
@@ -131,20 +131,21 @@ abstract class DefaultLambda(
|
||||
}?.toList() ?: emptyList()
|
||||
isBoundCallableReference = (isFunctionReference || isPropertyReference) && capturedVars.isNotEmpty()
|
||||
|
||||
val invokeName = (if (isPropertyReference) OperatorNameConventions.GET else OperatorNameConventions.INVOKE).asString()
|
||||
val invokeDescriptor = mapAsmDescriptor(sourceCompiler, isPropertyReference)
|
||||
val invokeNameFallback = (if (isPropertyReference) OperatorNameConventions.GET else OperatorNameConventions.INVOKE).asString()
|
||||
val invokeMethod = mapAsmMethod(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)
|
||||
node = getMethodNode(classBytes, invokeMethod.name, invokeMethod.descriptor, lambdaClassType, signatureAmbiguity = true)
|
||||
?: getMethodNode(classBytes, invokeNameFallback, invokeMethod.descriptor, lambdaClassType, signatureAmbiguity = true)
|
||||
?: error("Can't find method '$invokeMethod' in '${lambdaClassType.internalName}'")
|
||||
this.invokeMethod = Method(node.node.name, node.node.desc)
|
||||
if (needReification) {
|
||||
//nested classes could also require reification
|
||||
reifiedTypeParametersUsages.mergeAll(reifiedTypeInliner.reifyInstructions(node.node))
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun mapAsmDescriptor(sourceCompiler: SourceCompilerForInline, isPropertyReference: Boolean): String
|
||||
protected abstract fun mapAsmMethod(sourceCompiler: SourceCompilerForInline, isPropertyReference: Boolean): Method
|
||||
|
||||
private companion object {
|
||||
val PROPERTY_REFERENCE_SUPER_CLASSES =
|
||||
|
||||
@@ -351,7 +351,7 @@ class PsiDefaultLambda(
|
||||
override val invokeMethodReturnType: KotlinType?
|
||||
get() = invokeMethodDescriptor.returnType
|
||||
|
||||
override fun mapAsmDescriptor(sourceCompiler: SourceCompilerForInline, isPropertyReference: Boolean): String {
|
||||
override fun mapAsmMethod(sourceCompiler: SourceCompilerForInline, isPropertyReference: Boolean): Method {
|
||||
invokeMethodDescriptor = parameterDescriptor.type.memberScope
|
||||
.getContributedFunctions(OperatorNameConventions.INVOKE, NoLookupLocation.FROM_BACKEND)
|
||||
.single().let {
|
||||
@@ -359,6 +359,6 @@ class PsiDefaultLambda(
|
||||
// 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
|
||||
return sourceCompiler.state.typeMapper.mapSignatureSkipGeneric(invokeMethodDescriptor).asmMethod
|
||||
}
|
||||
}
|
||||
|
||||
+11
-2
@@ -6,7 +6,9 @@
|
||||
package org.jetbrains.kotlin.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isInlineClassType
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isInlineParameter
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.InlineClassAbi
|
||||
import org.jetbrains.kotlin.codegen.IrExpressionLambda
|
||||
import org.jetbrains.kotlin.codegen.JvmKotlinType
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
@@ -24,6 +26,7 @@ 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
|
||||
@@ -245,7 +248,7 @@ class IrDefaultLambda(
|
||||
override val invokeMethodReturnType: KotlinType
|
||||
get() = typeArguments.last().toIrBasedKotlinType()
|
||||
|
||||
override fun mapAsmDescriptor(sourceCompiler: SourceCompilerForInline, isPropertyReference: Boolean): String {
|
||||
override fun mapAsmMethod(sourceCompiler: SourceCompilerForInline, isPropertyReference: Boolean): Method {
|
||||
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
|
||||
@@ -256,9 +259,15 @@ class IrDefaultLambda(
|
||||
it.map { argument -> (argument as IrTypeProjection).type }
|
||||
}
|
||||
}
|
||||
val base = if (isPropertyReference) OperatorNameConventions.GET.asString() else OperatorNameConventions.INVOKE.asString()
|
||||
val name = InlineClassAbi.hashSuffix(
|
||||
context.state.useOldManglingSchemeForFunctionsWithInlineClassesInSignatures,
|
||||
typeArguments.dropLast(1),
|
||||
typeArguments.last().takeIf { it.isInlineClassType() }
|
||||
)?.let { "$base-$it" } ?: base
|
||||
// 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 })
|
||||
return Method(name, AsmTypes.OBJECT_TYPE, Array(typeArguments.size - 1) { AsmTypes.OBJECT_TYPE })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+28
-39
@@ -63,7 +63,15 @@ object InlineClassAbi {
|
||||
return Name.identifier("constructor-impl")
|
||||
}
|
||||
|
||||
val suffix = hashSuffix(irFunction, mangleReturnTypes, useOldMangleRules) ?: return irFunction.name
|
||||
val suffix = hashSuffix(
|
||||
useOldMangleRules,
|
||||
irFunction.fullValueParameterList.map { it.type },
|
||||
irFunction.returnType.takeIf { mangleReturnTypes && irFunction.hasMangledReturnType },
|
||||
irFunction.isSuspend
|
||||
)
|
||||
if (suffix == null && ((irFunction.parent as? IrClass)?.isInline != true || irFunction.origin == IrDeclarationOrigin.IR_BUILTINS_STUB)) {
|
||||
return irFunction.name
|
||||
}
|
||||
|
||||
val base = when {
|
||||
irFunction.isGetter ->
|
||||
@@ -76,47 +84,28 @@ object InlineClassAbi {
|
||||
irFunction.name.asString()
|
||||
}
|
||||
|
||||
return Name.identifier("$base-$suffix")
|
||||
return Name.identifier("$base-${suffix ?: "impl"}")
|
||||
}
|
||||
|
||||
private fun hashSuffix(
|
||||
irFunction: IrFunction,
|
||||
mangleReturnTypes: Boolean,
|
||||
fun hashSuffix(
|
||||
useOldMangleRules: Boolean,
|
||||
alwaysMangleReturnType: Boolean = false
|
||||
): String? {
|
||||
val signatureForMangling = collectFunctionSignatureForManglingSuffix(
|
||||
useOldManglingRules = useOldMangleRules,
|
||||
requiresFunctionNameManglingForParameterTypes = irFunction.fullValueParameterList.any { it.type.requiresMangling },
|
||||
fqNamesForMangling = irFunction.fullValueParameterList.map {
|
||||
it.type.asInfoForMangling()
|
||||
} + listOfNotNull(
|
||||
// The JVM backend computes mangled names after creating suspend function views, but before default argument
|
||||
// stub insertion. It would be nice if this part of the continuation lowering happened earlier in the pipeline.
|
||||
// TODO: Move suspend function view creation before JvmInlineClassLowering.
|
||||
if (irFunction.isSuspend)
|
||||
InfoForMangling(FqNameUnsafe("kotlin.coroutines.Continuation"), isInline = false, isNullable = false)
|
||||
else null
|
||||
),
|
||||
returnTypeInfo = if (alwaysMangleReturnType || (mangleReturnTypes && irFunction.hasMangledReturnType)) {
|
||||
irFunction.returnType.asInfoForMangling()
|
||||
} else null
|
||||
)
|
||||
|
||||
return when {
|
||||
signatureForMangling != null -> md5base64(signatureForMangling)
|
||||
(irFunction.parent as? IrClass)?.isInline == true && irFunction.origin != IrDeclarationOrigin.IR_BUILTINS_STUB -> "impl"
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun hashReturnSuffix(irFunction: IrFunction): String =
|
||||
hashSuffix(
|
||||
irFunction,
|
||||
mangleReturnTypes = true,
|
||||
useOldMangleRules = false,
|
||||
alwaysMangleReturnType = true
|
||||
)!!
|
||||
valueParameters: List<IrType>,
|
||||
returnType: IrType?,
|
||||
addContinuation: Boolean = false
|
||||
): String? =
|
||||
collectFunctionSignatureForManglingSuffix(
|
||||
useOldMangleRules,
|
||||
valueParameters.any { it.requiresMangling },
|
||||
// The JVM backend computes mangled names after creating suspend function views, but before default argument
|
||||
// stub insertion. It would be nice if this part of the continuation lowering happened earlier in the pipeline.
|
||||
// TODO: Move suspend function view creation before JvmInlineClassLowering.
|
||||
if (addContinuation)
|
||||
valueParameters.map { it.asInfoForMangling() } +
|
||||
InfoForMangling(FqNameUnsafe("kotlin.coroutines.Continuation"), isInline = false, isNullable = false)
|
||||
else
|
||||
valueParameters.map { it.asInfoForMangling() },
|
||||
returnType?.asInfoForMangling()
|
||||
)?.let(::md5base64)
|
||||
|
||||
private fun IrType.asInfoForMangling(): InfoForMangling =
|
||||
InfoForMangling(
|
||||
|
||||
-4
@@ -1,7 +1,3 @@
|
||||
// KT-46601
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR, JVM_MULTI_MODULE_OLD_AGAINST_IR
|
||||
// SKIP_INLINE_CHECK_IN: inlineFun-1BDWgbU$default
|
||||
// FILE: 1.kt
|
||||
package test
|
||||
|
||||
Reference in New Issue
Block a user