From d65f88850e2b3564bcfd2199d0bb21568d7f2f3b Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Tue, 8 Sep 2020 12:17:38 +0500 Subject: [PATCH] [IR] Equals/hashCode for adapted references Fixes https://youtrack.jetbrains.com/issue/KT-39800 --- .../konan/lower/FunctionReferenceLowering.kt | 83 +++++++++++++++---- .../kotlin/native/internal/KFunctionImpl.kt | 18 ++-- .../native/internal/KSuspendFunctionImpl.kt | 22 ++--- 3 files changed, 87 insertions(+), 36 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionReferenceLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionReferenceLowering.kt index 492c4adeb1c..51631f9a692 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionReferenceLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionReferenceLowering.kt @@ -159,12 +159,38 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass typeParam.symbol to functionReference.getTypeArgument(typeParam.index)!! } + private val adapteeCall: IrFunctionAccessExpression? = + // TODO: Copied from JVM. + if (referencedFunction.origin == IrDeclarationOrigin.ADAPTER_FOR_CALLABLE_REFERENCE) { + // The body of a callable reference adapter contains either only a call, or an IMPLICIT_COERCION_TO_UNIT type operator + // applied to a call. That call's target is the original function which we need to get owner/name/signature. + val call = when (val statement = referencedFunction.body!!.statements.single()) { + is IrTypeOperatorCall -> { + assert(statement.operator == IrTypeOperator.IMPLICIT_COERCION_TO_UNIT) { + "Unexpected type operator in ADAPTER_FOR_CALLABLE_REFERENCE: ${referencedFunction.render()}" + } + statement.argument + } + is IrReturn -> statement.value + else -> statement + } + if (call !is IrFunctionAccessExpression) { + throw UnsupportedOperationException("Unknown structure of ADAPTER_FOR_CALLABLE_REFERENCE: ${referencedFunction.render()}") + } + call + } else { + null + } + + private val adaptedReferenceOriginalTarget: IrFunction? = adapteeCall?.symbol?.owner + private val functionReferenceTarget = adaptedReferenceOriginalTarget ?: referencedFunction + private val functionReferenceClass: IrClass = WrappedClassDescriptor().let { IrClassImpl( startOffset,endOffset, DECLARATION_ORIGIN_FUNCTION_REFERENCE_IMPL, IrClassSymbolImpl(it), - "${referencedFunction.name}\$FUNCTION_REFERENCE\$${context.functionReferenceCount++}".synthesizedName, + "${functionReferenceTarget.name}\$FUNCTION_REFERENCE\$${context.functionReferenceCount++}".synthesizedName, ClassKind.CLASS, DescriptorVisibilities.PRIVATE, Modality.FINAL, @@ -202,6 +228,7 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass private val kSuspendFunctionImplSymbol = symbols.kSuspendFunctionImpl private val kSuspendFunctionImplConstructorSymbol = kSuspendFunctionImplSymbol.constructors.single() + val isLambda = functionReference.origin.isLambda val isKFunction = functionReference.type.isKFunction() val isKSuspendFunction = functionReference.type.isKSuspendFunction() @@ -220,10 +247,10 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass superTypes += suspendFunctionClass.typeWith(functionParameterTypes + referencedFunction.returnType) } else { - superTypes += if (isKFunction) - kFunctionImplSymbol.typeWith(referencedFunction.returnType) - else + superTypes += if (isLambda) irBuiltIns.anyType + else + kFunctionImplSymbol.typeWith(referencedFunction.returnType) functionClass = (if (isKFunction) symbols.kFunctionN(numberOfParameters) else symbols.functionN(numberOfParameters)).owner superTypes += functionClass.typeWith(functionParameterTypes + referencedFunction.returnType) val lastParameterType = unboundFunctionParameters.lastOrNull()?.type @@ -278,21 +305,23 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass body = context.createIrBuilder(symbol, startOffset, endOffset).irBlockBody { val superConstructor = when { - isKFunction -> kFunctionImplConstructorSymbol.owner isKSuspendFunction -> kSuspendFunctionImplConstructorSymbol.owner - else -> irBuiltIns.anyClass.owner.constructors.single() + isLambda -> irBuiltIns.anyClass.owner.constructors.single() + else -> kFunctionImplConstructorSymbol.owner } +irDelegatingConstructorCall(superConstructor).apply applyIrDelegationConstructorCall@ { - if (!isKFunction && !isKSuspendFunction) return@applyIrDelegationConstructorCall - val name = ((referencedFunction as? IrSimpleFunction)?.attributeOwnerId as? IrSimpleFunction)?.name - ?: referencedFunction.name - putValueArgument(0, irString(name.asString())) - putValueArgument(1, irString((functionReference.symbol.owner).fullName)) - putValueArgument(2, irBoolean(boundFunctionParameters.isNotEmpty())) + if (isLambda) return@applyIrDelegationConstructorCall + val name = ((functionReferenceTarget as? IrSimpleFunction)?.attributeOwnerId as? IrSimpleFunction)?.name + ?: functionReferenceTarget.name val needReceiver = boundFunctionParameters.singleOrNull()?.descriptor is ReceiverParameterDescriptor val receiver = if (needReceiver) irGet(valueParameters.single()) else irNull() - putValueArgument(3, receiver) - putValueArgument(4, with(kTypeGenerator) { irKType(referencedFunction.returnType) }) + val arity = unboundFunctionParameters.size + if (functionReferenceTarget.isSuspend) 1 else 0 + putValueArgument(0, irString(name.asString())) + putValueArgument(1, irString(functionReferenceTarget.fullName)) + putValueArgument(2, receiver) + putValueArgument(3, irInt(arity)) + putValueArgument(4, irInt(getAdaptedCallableReferenceFlags())) + putValueArgument(5, with(kTypeGenerator) { irKType(referencedFunction.returnType) }) } +IrInstanceInitializerCallImpl(startOffset, endOffset, functionReferenceClass.symbol, irBuiltIns.unitType) // Save all arguments to fields. @@ -306,6 +335,32 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass private val IrFunction.fullName: String get() = parent.fqNameForIrSerialization.child(Name.identifier(functionName)).asString() + private fun getAdaptedCallableReferenceFlags(): Int { + if (adaptedReferenceOriginalTarget == null) return 0 + + val isVarargMappedToElementBit = if (hasVarargMappedToElement()) 1 else 0 + val isSuspendConvertedBit = + if (!adaptedReferenceOriginalTarget.isSuspend && referencedFunction.isSuspend) 1 else 0 + val isCoercedToUnitBit = + if (!adaptedReferenceOriginalTarget.returnType.isUnit() && referencedFunction.returnType.isUnit()) 1 else 0 + + return isVarargMappedToElementBit + + (isSuspendConvertedBit shl 1) + + (isCoercedToUnitBit shl 2) + } + + private fun hasVarargMappedToElement(): Boolean { + if (adapteeCall == null) return false + for (i in 0 until adapteeCall.valueArgumentsCount) { + val arg = adapteeCall.getValueArgument(i) ?: continue + if (arg !is IrVararg) continue + for (varargElement in arg.elements) { + if (varargElement is IrGetValue) return true + } + } + return false + } + private fun buildInvokeMethod(superFunction: IrSimpleFunction): IrSimpleFunction = WrappedSimpleFunctionDescriptor().let { IrFunctionImpl( startOffset, endOffset, diff --git a/runtime/src/main/kotlin/kotlin/native/internal/KFunctionImpl.kt b/runtime/src/main/kotlin/kotlin/native/internal/KFunctionImpl.kt index 7812e149eb4..bae3a016151 100644 --- a/runtime/src/main/kotlin/kotlin/native/internal/KFunctionImpl.kt +++ b/runtime/src/main/kotlin/kotlin/native/internal/KFunctionImpl.kt @@ -9,17 +9,25 @@ import kotlin.reflect.KFunction import kotlin.reflect.KType @FixmeReflection -open class KFunctionImpl(override val name: String, val fqName: String, val bound: Boolean, val receiver: Any?, - override val returnType: KType): KFunction { +internal abstract class KFunctionImpl( + override val name: String, val fqName: String, val receiver: Any?, + val arity: Int, val flags: Int, override val returnType: KType +): KFunction { override fun equals(other: Any?): Boolean { if (other !is KFunctionImpl<*>) return false - return fqName == other.fqName && bound == other.bound && receiver == other.receiver + return fqName == other.fqName && receiver == other.receiver + && arity == other.arity && flags == other.flags } - override fun hashCode(): Int { - return (fqName.hashCode() * 31 + if (bound) 1 else 0) * 31 + receiver.hashCode() + private fun evalutePolynom(x: Int, vararg coeffs: Int): Int { + var res = 0 + for (coeff in coeffs) + res = res * x + coeff + return res } + override fun hashCode() = evalutePolynom(31, fqName.hashCode(), receiver.hashCode(), arity, flags) + override fun toString(): String { return "${if (name == "") "constructor" else "function " + name}" } diff --git a/runtime/src/main/kotlin/kotlin/native/internal/KSuspendFunctionImpl.kt b/runtime/src/main/kotlin/kotlin/native/internal/KSuspendFunctionImpl.kt index cf2476d393b..011311e7f05 100644 --- a/runtime/src/main/kotlin/kotlin/native/internal/KSuspendFunctionImpl.kt +++ b/runtime/src/main/kotlin/kotlin/native/internal/KSuspendFunctionImpl.kt @@ -7,24 +7,12 @@ package kotlin.native.internal import kotlin.reflect.KType import kotlin.reflect.KFunction +import kotlin.reflect.KClass @FixmeReflection internal abstract class KSuspendFunctionImpl( - override val name: String, val fqName: String, - val bound: Boolean, val receiver: Any?, - override val returnType: KType -): KFunction { - - override fun equals(other: Any?): Boolean { - if (other !is KSuspendFunctionImpl<*>) return false - return fqName == other.fqName && bound == other.bound && receiver == other.receiver - } - - override fun hashCode(): Int { - return (fqName.hashCode() * 31 + if (bound) 1 else 0) * 31 + receiver.hashCode() - } - - override fun toString(): String { - return "suspend function $name" - } + name: String, fqName: String, receiver: Any?, + arity: Int, flags: Int, returnType: KType +): KFunctionImpl(name, fqName, receiver, arity, flags, returnType) { + override fun toString() = "suspend function $name" }