diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/inlineClassManglingUtils.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/inlineClassManglingUtils.kt index 0e49234978f..9a1cf9a03db 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/inlineClassManglingUtils.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/inlineClassManglingUtils.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.codegen.state import org.jetbrains.kotlin.codegen.coroutines.unwrapInitialDescriptorForSuspendFunction import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver @@ -23,6 +24,62 @@ const val NOT_INLINE_CLASS_PARAMETER_PLACEHOLDER = "_" private fun FunctionDescriptor.isFunctionFromStdlib(): Boolean = fqNameSafe.startsWith(Name.identifier("kotlin")) +class InfoForMangling( + val fqName: FqNameUnsafe, + val isInline: Boolean, + val isNullable: Boolean +) + +fun collectFunctionSignatureForManglingSuffix( + useOldManglingRules: Boolean, + requiresFunctionNameManglingForParameterTypes: Boolean, + fqNamesForMangling: List, + returnTypeInfo: InfoForMangling?, +): String? { + fun getSignatureElementForMangling(info: InfoForMangling?): String = buildString { + if (info == null) return "" + if (useOldManglingRules || info.isInline) { + append('L') + append(info.fqName) + if (info.isNullable) append('?') + append(';') + } else { + append(NOT_INLINE_CLASS_PARAMETER_PLACEHOLDER) + } + } + + fun collectSignatureForMangling(): String = + fqNamesForMangling.joinToString(separator = if (useOldManglingRules) ", " else "") { + getSignatureElementForMangling(it) + } + + if (useOldManglingRules) { + if (requiresFunctionNameManglingForParameterTypes) { + return collectSignatureForMangling() + } + + // If a class member function returns inline class value, mangle its name. + // NB here function can be a suspend function JVM view with return type replaced with 'Any', + // should unwrap it and take original return type instead. + if (returnTypeInfo != null) { + return ":" + getSignatureElementForMangling(returnTypeInfo) + } + } else { + // If a function accepts inline class parameters, mangle its name. + if (requiresFunctionNameManglingForParameterTypes || returnTypeInfo != null) { + // If a class member function returns inline class value, mangle its name. + // NB here function can be a suspend function JVM view with return type replaced with 'Any', + // should unwrap it and take original return type instead. + val signature = collectSignatureForMangling() + + if (returnTypeInfo != null) + ":" + getSignatureElementForMangling(returnTypeInfo) + else "" + return signature + } + } + return null +} + fun getManglingSuffixBasedOnKotlinSignature( descriptor: CallableMemberDescriptor, shouldMangleByReturnType: Boolean, @@ -38,77 +95,31 @@ fun getManglingSuffixBasedOnKotlinSignature( val unwrappedDescriptor = descriptor.unwrapInitialDescriptorForSuspendFunction() - if (useOldManglingRules || descriptor.isFunctionFromStdlib()) { - if (requiresFunctionNameManglingForParameterTypes(descriptor)) { - return "-" + md5base64(collectSignatureForMangling(descriptor, true)) - } + val resultNew = collectFunctionSignatureForManglingSuffix( + useOldManglingRules = useOldManglingRules || descriptor.isFunctionFromStdlib(), + requiresFunctionNameManglingForParameterTypes = requiresFunctionNameManglingForParameterTypes(descriptor), + fqNamesForMangling = + (listOfNotNull(descriptor.extensionReceiverParameter?.type) + descriptor.valueParameters.map { it.type }) + .map { getInfoForMangling(it) }, + returnTypeInfo = + if (shouldMangleByReturnType && requiresFunctionNameManglingForReturnType(unwrappedDescriptor)) + getInfoForMangling(unwrappedDescriptor.returnType!!) + else null + ) - // If a class member function returns inline class value, mangle its name. - // NB here function can be a suspend function JVM view with return type replaced with 'Any', - // should unwrap it and take original return type instead. - if (shouldMangleByReturnType) { - if (requiresFunctionNameManglingForReturnType(unwrappedDescriptor)) { - return "-" + md5base64( - ":" + getSignatureElementForMangling( - unwrappedDescriptor.returnType!!, true - ) - ) - } - } - } else { - // If a function accepts inline class parameters, mangle its name. - if (requiresFunctionNameManglingForParameterTypes(descriptor) || - (shouldMangleByReturnType && requiresFunctionNameManglingForReturnType(unwrappedDescriptor)) - ) { - // If a class member function returns inline class value, mangle its name. - // NB here function can be a suspend function JVM view with return type replaced with 'Any', - // should unwrap it and take original return type instead. - val signature = collectSignatureForMangling(descriptor, false) + - if (shouldMangleByReturnType && requiresFunctionNameManglingForReturnType(unwrappedDescriptor)) - ":" + getSignatureElementForMangling(unwrappedDescriptor.returnType!!, false) - else "" - return "-" + md5base64(signature) - } - } - - return null + return resultNew?.let { "-" + md5base64(it) } } -private fun collectSignatureForMangling(descriptor: CallableMemberDescriptor, useOldManglingRules: Boolean): String { - val types = listOfNotNull(descriptor.extensionReceiverParameter?.type) + descriptor.valueParameters.map { it.type } - return types.joinToString(separator = if (useOldManglingRules) ", " else "") { getSignatureElementForMangling(it, useOldManglingRules) } -} +private fun getInfoForMangling(type: KotlinType): InfoForMangling? { + val descriptor = type.constructor.declarationDescriptor ?: return null + return when (descriptor) { + is ClassDescriptor -> InfoForMangling(descriptor.fqNameUnsafe, descriptor.isInline, type.isMarkedNullable) -private fun getSignatureElementForMangling(type: KotlinType, useOldManglingRules: Boolean): String = buildString { - val descriptor = type.constructor.declarationDescriptor ?: return "" - if (useOldManglingRules) { - when (descriptor) { - is ClassDescriptor -> { - append('L') - append(descriptor.fqNameUnsafe) - if (type.isMarkedNullable) append('?') - append(';') - } - - is TypeParameterDescriptor -> { - append(getSignatureElementForMangling(descriptor.representativeUpperBound, useOldManglingRules)) - } + is TypeParameterDescriptor -> { + getInfoForMangling(descriptor.representativeUpperBound) } - } else { - when (descriptor) { - is ClassDescriptor -> if (descriptor.isInline) { - append('L') - append(descriptor.fqNameUnsafe) - if (type.isMarkedNullable) append('?') - append(';') - } else { - append(NOT_INLINE_CLASS_PARAMETER_PLACEHOLDER) - } - is TypeParameterDescriptor -> { - append(getSignatureElementForMangling(descriptor.representativeUpperBound, useOldManglingRules)) - } - } + else -> null } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt index 6311e18e4e6..6d0dbba4d4b 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt @@ -366,12 +366,13 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) private fun createInvokeMethod(receiverVar: IrValueDeclaration?): IrSimpleFunction = functionReferenceClass.addFunction { setSourceRange(if (isLambda) callee else irFunctionReference) - name = if (samSuperType == null && callee.returnType.erasedUpperBound.isInline && context.state.functionsWithInlineClassReturnTypesMangled) { - // For functions with inline class return type we need to mangle the invoke method. - // Otherwise, bridge lowering may fail to generate bridges for inline class types erasing to Any. - val suffix = InlineClassAbi.hashSuffix(callee, mangleReturnTypes = true, useOldMangleRules = false) - Name.identifier("${superMethod.owner.name.asString()}-${suffix}") - } else superMethod.owner.name + name = + if (samSuperType == null && callee.returnType.erasedUpperBound.isInline && context.state.functionsWithInlineClassReturnTypesMangled) { + // For functions with inline class return type we need to mangle the invoke method. + // Otherwise, bridge lowering may fail to generate bridges for inline class types erasing to Any. + val suffix = InlineClassAbi.hashReturnSuffix(callee) + Name.identifier("${superMethod.owner.name.asString()}-${suffix}") + } else superMethod.owner.name returnType = callee.returnType isSuspend = callee.isSuspend }.apply { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/inlineclasses/InlineClassAbi.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/inlineclasses/InlineClassAbi.kt index 0fa409b688c..2a6c25b2fbe 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/inlineclasses/InlineClassAbi.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/inlineclasses/InlineClassAbi.kt @@ -7,13 +7,15 @@ package org.jetbrains.kotlin.backend.jvm.lower.inlineclasses import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound import org.jetbrains.kotlin.builtins.StandardNames -import org.jetbrains.kotlin.codegen.state.NOT_INLINE_CLASS_PARAMETER_PLACEHOLDER +import org.jetbrains.kotlin.codegen.state.InfoForMangling +import org.jetbrains.kotlin.codegen.state.collectFunctionSignatureForManglingSuffix import org.jetbrains.kotlin.codegen.state.md5base64 import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrStatementOriginImpl import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.name.Name /** @@ -74,29 +76,7 @@ object InlineClassAbi { return Name.identifier("constructor-impl") } - val suffix = if (useOldMangleRules || irFunction.isFunctionFromStdlib()) { - when { - irFunction.fullValueParameterList.any { it.type.requiresMangling } -> - hashSuffix(irFunction, mangleReturnTypes = false, useOldMangleRules = true) - mangleReturnTypes && irFunction.hasMangledReturnType -> - returnHashSuffix(irFunction) - (irFunction.parent as? IrClass)?.isInline == true && - irFunction.origin != IrDeclarationOrigin.IR_BUILTINS_STUB -> - "impl" - else -> - return irFunction.name - } - } else { - when { - irFunction.fullValueParameterList.any { it.type.requiresMangling } || (mangleReturnTypes && irFunction.hasMangledReturnType) -> - hashSuffix(irFunction, mangleReturnTypes, false) - (irFunction.parent as? IrClass)?.isInline == true && - irFunction.origin != IrDeclarationOrigin.IR_BUILTINS_STUB -> - "impl" - else -> - return irFunction.name - } - } + val suffix = hashSuffix(irFunction, mangleReturnTypes, useOldMangleRules) ?: return irFunction.name val base = when { irFunction.isGetter -> @@ -112,44 +92,54 @@ object InlineClassAbi { return Name.identifier("$base-$suffix") } - private val IrFunction.propertyName: Name - get() = (this as IrSimpleFunction).correspondingPropertySymbol!!.owner.name + private fun hashSuffix( + irFunction: IrFunction, + mangleReturnTypes: Boolean, + useOldMangleRules: Boolean, + alwaysMangleReturnType: Boolean = false + ): String? { + val signatureForMangling = collectFunctionSignatureForManglingSuffix( + useOldManglingRules = useOldMangleRules || irFunction.isFunctionFromStdlib(), + 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 + ) - fun hashSuffix(irFunction: IrFunction, mangleReturnTypes: Boolean, useOldMangleRules: Boolean): String { - val signatureElementsForMangling = - irFunction.fullValueParameterList.mapTo(mutableListOf()) { it.type.eraseToString(useOldMangleRules) } - if (irFunction.isSuspend) { - // 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. - signatureElementsForMangling += if (useOldMangleRules) "Lkotlin.coroutines.Continuation;" - else NOT_INLINE_CLASS_PARAMETER_PLACEHOLDER + return when { + signatureForMangling != null -> md5base64(signatureForMangling) + (irFunction.parent as? IrClass)?.isInline == true && irFunction.origin != IrDeclarationOrigin.IR_BUILTINS_STUB -> "impl" + else -> null } - val signatureString = signatureElementsForMangling.joinToString(separator = if (useOldMangleRules) ", " else "") + - if (mangleReturnTypes && irFunction.hasMangledReturnType && !useOldMangleRules) - ":${irFunction.returnType.eraseToString(useOldMangleRules)}" else "" - return md5base64(signatureString) } - private fun returnHashSuffix(irFunction: IrFunction) = - md5base64(":${irFunction.returnType.eraseToString(false)}") + fun hashReturnSuffix(irFunction: IrFunction): String = + hashSuffix( + irFunction, + mangleReturnTypes = true, + useOldMangleRules = false, + alwaysMangleReturnType = true + )!! - private fun IrType.eraseToString(useOldMangleRules: Boolean) = - if (useOldMangleRules) { - buildString { - append('L') - append(erasedUpperBound.fqNameWhenAvailable!!) - if (isNullable()) append('?') - append(';') - } - } else { - if (getClass()?.isInline == true) buildString { - append('L') - append(erasedUpperBound.fqNameWhenAvailable!!) - if (isNullable()) append('?') - append(';') - } else NOT_INLINE_CLASS_PARAMETER_PLACEHOLDER - } + private fun IrType.asInfoForMangling(): InfoForMangling = + InfoForMangling( + erasedUpperBound.fqNameWhenAvailable!!.toUnsafe(), + isInline = getClass()?.isInline == true, + isNullable = isNullable() + ) + + private val IrFunction.propertyName: Name + get() = (this as IrSimpleFunction).correspondingPropertySymbol!!.owner.name } internal val IrType.requiresMangling: Boolean