[IR] Equals/hashCode for adapted references
Fixes https://youtrack.jetbrains.com/issue/KT-39800
This commit is contained in:
+69
-14
@@ -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,
|
||||
|
||||
@@ -9,17 +9,25 @@ import kotlin.reflect.KFunction
|
||||
import kotlin.reflect.KType
|
||||
|
||||
@FixmeReflection
|
||||
open class KFunctionImpl<out R>(override val name: String, val fqName: String, val bound: Boolean, val receiver: Any?,
|
||||
override val returnType: KType): KFunction<R> {
|
||||
internal abstract class KFunctionImpl<out R>(
|
||||
override val name: String, val fqName: String, val receiver: Any?,
|
||||
val arity: Int, val flags: Int, override val returnType: KType
|
||||
): KFunction<R> {
|
||||
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 == "<init>") "constructor" else "function " + name}"
|
||||
}
|
||||
|
||||
@@ -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<out R>(
|
||||
override val name: String, val fqName: String,
|
||||
val bound: Boolean, val receiver: Any?,
|
||||
override val returnType: KType
|
||||
): KFunction<R> {
|
||||
|
||||
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<R>(name, fqName, receiver, arity, flags, returnType) {
|
||||
override fun toString() = "suspend function $name"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user