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()
|
}?.toList() ?: emptyList()
|
||||||
isBoundCallableReference = (isFunctionReference || isPropertyReference) && capturedVars.isNotEmpty()
|
isBoundCallableReference = (isFunctionReference || isPropertyReference) && capturedVars.isNotEmpty()
|
||||||
|
|
||||||
val invokeName = (if (isPropertyReference) OperatorNameConventions.GET else OperatorNameConventions.INVOKE).asString()
|
val invokeNameFallback = (if (isPropertyReference) OperatorNameConventions.GET else OperatorNameConventions.INVOKE).asString()
|
||||||
val invokeDescriptor = mapAsmDescriptor(sourceCompiler, isPropertyReference)
|
val invokeMethod = mapAsmMethod(sourceCompiler, isPropertyReference)
|
||||||
// TODO: `signatureAmbiguity = true` ignores the argument types from `invokeDescriptor` and only looks at the count.
|
// 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.
|
// This effectively masks incorrect results from `mapAsmDescriptor`, which hopefully won't manifest in another way.
|
||||||
node = getMethodNode(classBytes, invokeName, invokeDescriptor, lambdaClassType, signatureAmbiguity = true)
|
node = getMethodNode(classBytes, invokeMethod.name, invokeMethod.descriptor, lambdaClassType, signatureAmbiguity = true)
|
||||||
?: error("Can't find method '$invokeName$invokeDescriptor' in '${lambdaClassType.internalName}'")
|
?: getMethodNode(classBytes, invokeNameFallback, invokeMethod.descriptor, lambdaClassType, signatureAmbiguity = true)
|
||||||
invokeMethod = Method(node.node.name, node.node.desc)
|
?: error("Can't find method '$invokeMethod' in '${lambdaClassType.internalName}'")
|
||||||
|
this.invokeMethod = Method(node.node.name, node.node.desc)
|
||||||
if (needReification) {
|
if (needReification) {
|
||||||
//nested classes could also require reification
|
//nested classes could also require reification
|
||||||
reifiedTypeParametersUsages.mergeAll(reifiedTypeInliner.reifyInstructions(node.node))
|
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 {
|
private companion object {
|
||||||
val PROPERTY_REFERENCE_SUPER_CLASSES =
|
val PROPERTY_REFERENCE_SUPER_CLASSES =
|
||||||
|
|||||||
@@ -351,7 +351,7 @@ class PsiDefaultLambda(
|
|||||||
override val invokeMethodReturnType: KotlinType?
|
override val invokeMethodReturnType: KotlinType?
|
||||||
get() = invokeMethodDescriptor.returnType
|
get() = invokeMethodDescriptor.returnType
|
||||||
|
|
||||||
override fun mapAsmDescriptor(sourceCompiler: SourceCompilerForInline, isPropertyReference: Boolean): String {
|
override fun mapAsmMethod(sourceCompiler: SourceCompilerForInline, isPropertyReference: Boolean): Method {
|
||||||
invokeMethodDescriptor = parameterDescriptor.type.memberScope
|
invokeMethodDescriptor = parameterDescriptor.type.memberScope
|
||||||
.getContributedFunctions(OperatorNameConventions.INVOKE, NoLookupLocation.FROM_BACKEND)
|
.getContributedFunctions(OperatorNameConventions.INVOKE, NoLookupLocation.FROM_BACKEND)
|
||||||
.single().let {
|
.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.
|
// have a non-erased `invoke` with an erased synthetic bridge, and we want to inline the former.
|
||||||
if (isPropertyReference) it.original else it
|
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
|
package org.jetbrains.kotlin.backend.jvm.codegen
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
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.ir.isInlineParameter
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.InlineClassAbi
|
||||||
import org.jetbrains.kotlin.codegen.IrExpressionLambda
|
import org.jetbrains.kotlin.codegen.IrExpressionLambda
|
||||||
import org.jetbrains.kotlin.codegen.JvmKotlinType
|
import org.jetbrains.kotlin.codegen.JvmKotlinType
|
||||||
import org.jetbrains.kotlin.codegen.StackValue
|
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.AsmTypes
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
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.Type
|
||||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||||
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||||
@@ -245,7 +248,7 @@ class IrDefaultLambda(
|
|||||||
override val invokeMethodReturnType: KotlinType
|
override val invokeMethodReturnType: KotlinType
|
||||||
get() = typeArguments.last().toIrBasedKotlinType()
|
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
|
val context = (sourceCompiler as IrSourceCompilerForInline).codegen.context
|
||||||
typeArguments = (irValueParameter.type as IrSimpleType).arguments.let {
|
typeArguments = (irValueParameter.type as IrSimpleType).arguments.let {
|
||||||
// Property references only have an erased `get` method, while function references and lambdas
|
// 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 }
|
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),
|
// 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.
|
// 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")
|
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 {
|
val base = when {
|
||||||
irFunction.isGetter ->
|
irFunction.isGetter ->
|
||||||
@@ -76,47 +84,28 @@ object InlineClassAbi {
|
|||||||
irFunction.name.asString()
|
irFunction.name.asString()
|
||||||
}
|
}
|
||||||
|
|
||||||
return Name.identifier("$base-$suffix")
|
return Name.identifier("$base-${suffix ?: "impl"}")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hashSuffix(
|
fun hashSuffix(
|
||||||
irFunction: IrFunction,
|
|
||||||
mangleReturnTypes: Boolean,
|
|
||||||
useOldMangleRules: Boolean,
|
useOldMangleRules: Boolean,
|
||||||
alwaysMangleReturnType: Boolean = false
|
valueParameters: List<IrType>,
|
||||||
): String? {
|
returnType: IrType?,
|
||||||
val signatureForMangling = collectFunctionSignatureForManglingSuffix(
|
addContinuation: Boolean = false
|
||||||
useOldManglingRules = useOldMangleRules,
|
): String? =
|
||||||
requiresFunctionNameManglingForParameterTypes = irFunction.fullValueParameterList.any { it.type.requiresMangling },
|
collectFunctionSignatureForManglingSuffix(
|
||||||
fqNamesForMangling = irFunction.fullValueParameterList.map {
|
useOldMangleRules,
|
||||||
it.type.asInfoForMangling()
|
valueParameters.any { it.requiresMangling },
|
||||||
} + listOfNotNull(
|
// The JVM backend computes mangled names after creating suspend function views, but before default argument
|
||||||
// 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.
|
||||||
// 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.
|
||||||
// TODO: Move suspend function view creation before JvmInlineClassLowering.
|
if (addContinuation)
|
||||||
if (irFunction.isSuspend)
|
valueParameters.map { it.asInfoForMangling() } +
|
||||||
InfoForMangling(FqNameUnsafe("kotlin.coroutines.Continuation"), isInline = false, isNullable = false)
|
InfoForMangling(FqNameUnsafe("kotlin.coroutines.Continuation"), isInline = false, isNullable = false)
|
||||||
else null
|
else
|
||||||
),
|
valueParameters.map { it.asInfoForMangling() },
|
||||||
returnTypeInfo = if (alwaysMangleReturnType || (mangleReturnTypes && irFunction.hasMangledReturnType)) {
|
returnType?.asInfoForMangling()
|
||||||
irFunction.returnType.asInfoForMangling()
|
)?.let(::md5base64)
|
||||||
} 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
|
|
||||||
)!!
|
|
||||||
|
|
||||||
private fun IrType.asInfoForMangling(): InfoForMangling =
|
private fun IrType.asInfoForMangling(): InfoForMangling =
|
||||||
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
|
// SKIP_INLINE_CHECK_IN: inlineFun-1BDWgbU$default
|
||||||
// FILE: 1.kt
|
// FILE: 1.kt
|
||||||
package test
|
package test
|
||||||
|
|||||||
Reference in New Issue
Block a user