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:
pyos
2021-05-12 10:02:12 +02:00
committed by max-kammerer
parent 34ac232a82
commit 57c934987c
5 changed files with 48 additions and 53 deletions
@@ -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
}
}
@@ -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 })
}
}
@@ -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(
@@ -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