[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)!!
|
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 {
|
private val functionReferenceClass: IrClass = WrappedClassDescriptor().let {
|
||||||
IrClassImpl(
|
IrClassImpl(
|
||||||
startOffset,endOffset,
|
startOffset,endOffset,
|
||||||
DECLARATION_ORIGIN_FUNCTION_REFERENCE_IMPL,
|
DECLARATION_ORIGIN_FUNCTION_REFERENCE_IMPL,
|
||||||
IrClassSymbolImpl(it),
|
IrClassSymbolImpl(it),
|
||||||
"${referencedFunction.name}\$FUNCTION_REFERENCE\$${context.functionReferenceCount++}".synthesizedName,
|
"${functionReferenceTarget.name}\$FUNCTION_REFERENCE\$${context.functionReferenceCount++}".synthesizedName,
|
||||||
ClassKind.CLASS,
|
ClassKind.CLASS,
|
||||||
DescriptorVisibilities.PRIVATE,
|
DescriptorVisibilities.PRIVATE,
|
||||||
Modality.FINAL,
|
Modality.FINAL,
|
||||||
@@ -202,6 +228,7 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
|||||||
private val kSuspendFunctionImplSymbol = symbols.kSuspendFunctionImpl
|
private val kSuspendFunctionImplSymbol = symbols.kSuspendFunctionImpl
|
||||||
private val kSuspendFunctionImplConstructorSymbol = kSuspendFunctionImplSymbol.constructors.single()
|
private val kSuspendFunctionImplConstructorSymbol = kSuspendFunctionImplSymbol.constructors.single()
|
||||||
|
|
||||||
|
val isLambda = functionReference.origin.isLambda
|
||||||
val isKFunction = functionReference.type.isKFunction()
|
val isKFunction = functionReference.type.isKFunction()
|
||||||
val isKSuspendFunction = functionReference.type.isKSuspendFunction()
|
val isKSuspendFunction = functionReference.type.isKSuspendFunction()
|
||||||
|
|
||||||
@@ -220,10 +247,10 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
|||||||
superTypes += suspendFunctionClass.typeWith(functionParameterTypes + referencedFunction.returnType)
|
superTypes += suspendFunctionClass.typeWith(functionParameterTypes + referencedFunction.returnType)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
superTypes += if (isKFunction)
|
superTypes += if (isLambda)
|
||||||
kFunctionImplSymbol.typeWith(referencedFunction.returnType)
|
|
||||||
else
|
|
||||||
irBuiltIns.anyType
|
irBuiltIns.anyType
|
||||||
|
else
|
||||||
|
kFunctionImplSymbol.typeWith(referencedFunction.returnType)
|
||||||
functionClass = (if (isKFunction) symbols.kFunctionN(numberOfParameters) else symbols.functionN(numberOfParameters)).owner
|
functionClass = (if (isKFunction) symbols.kFunctionN(numberOfParameters) else symbols.functionN(numberOfParameters)).owner
|
||||||
superTypes += functionClass.typeWith(functionParameterTypes + referencedFunction.returnType)
|
superTypes += functionClass.typeWith(functionParameterTypes + referencedFunction.returnType)
|
||||||
val lastParameterType = unboundFunctionParameters.lastOrNull()?.type
|
val lastParameterType = unboundFunctionParameters.lastOrNull()?.type
|
||||||
@@ -278,21 +305,23 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
|||||||
|
|
||||||
body = context.createIrBuilder(symbol, startOffset, endOffset).irBlockBody {
|
body = context.createIrBuilder(symbol, startOffset, endOffset).irBlockBody {
|
||||||
val superConstructor = when {
|
val superConstructor = when {
|
||||||
isKFunction -> kFunctionImplConstructorSymbol.owner
|
|
||||||
isKSuspendFunction -> kSuspendFunctionImplConstructorSymbol.owner
|
isKSuspendFunction -> kSuspendFunctionImplConstructorSymbol.owner
|
||||||
else -> irBuiltIns.anyClass.owner.constructors.single()
|
isLambda -> irBuiltIns.anyClass.owner.constructors.single()
|
||||||
|
else -> kFunctionImplConstructorSymbol.owner
|
||||||
}
|
}
|
||||||
+irDelegatingConstructorCall(superConstructor).apply applyIrDelegationConstructorCall@ {
|
+irDelegatingConstructorCall(superConstructor).apply applyIrDelegationConstructorCall@ {
|
||||||
if (!isKFunction && !isKSuspendFunction) return@applyIrDelegationConstructorCall
|
if (isLambda) return@applyIrDelegationConstructorCall
|
||||||
val name = ((referencedFunction as? IrSimpleFunction)?.attributeOwnerId as? IrSimpleFunction)?.name
|
val name = ((functionReferenceTarget as? IrSimpleFunction)?.attributeOwnerId as? IrSimpleFunction)?.name
|
||||||
?: referencedFunction.name
|
?: functionReferenceTarget.name
|
||||||
putValueArgument(0, irString(name.asString()))
|
|
||||||
putValueArgument(1, irString((functionReference.symbol.owner).fullName))
|
|
||||||
putValueArgument(2, irBoolean(boundFunctionParameters.isNotEmpty()))
|
|
||||||
val needReceiver = boundFunctionParameters.singleOrNull()?.descriptor is ReceiverParameterDescriptor
|
val needReceiver = boundFunctionParameters.singleOrNull()?.descriptor is ReceiverParameterDescriptor
|
||||||
val receiver = if (needReceiver) irGet(valueParameters.single()) else irNull()
|
val receiver = if (needReceiver) irGet(valueParameters.single()) else irNull()
|
||||||
putValueArgument(3, receiver)
|
val arity = unboundFunctionParameters.size + if (functionReferenceTarget.isSuspend) 1 else 0
|
||||||
putValueArgument(4, with(kTypeGenerator) { irKType(referencedFunction.returnType) })
|
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)
|
+IrInstanceInitializerCallImpl(startOffset, endOffset, functionReferenceClass.symbol, irBuiltIns.unitType)
|
||||||
// Save all arguments to fields.
|
// Save all arguments to fields.
|
||||||
@@ -306,6 +335,32 @@ internal class FunctionReferenceLowering(val context: Context): FileLoweringPass
|
|||||||
private val IrFunction.fullName: String
|
private val IrFunction.fullName: String
|
||||||
get() = parent.fqNameForIrSerialization.child(Name.identifier(functionName)).asString()
|
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 {
|
private fun buildInvokeMethod(superFunction: IrSimpleFunction): IrSimpleFunction = WrappedSimpleFunctionDescriptor().let {
|
||||||
IrFunctionImpl(
|
IrFunctionImpl(
|
||||||
startOffset, endOffset,
|
startOffset, endOffset,
|
||||||
|
|||||||
@@ -9,17 +9,25 @@ import kotlin.reflect.KFunction
|
|||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
|
|
||||||
@FixmeReflection
|
@FixmeReflection
|
||||||
open class KFunctionImpl<out R>(override val name: String, val fqName: String, val bound: Boolean, val receiver: Any?,
|
internal abstract class KFunctionImpl<out R>(
|
||||||
override val returnType: KType): KFunction<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 {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (other !is KFunctionImpl<*>) return false
|
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 {
|
private fun evalutePolynom(x: Int, vararg coeffs: Int): Int {
|
||||||
return (fqName.hashCode() * 31 + if (bound) 1 else 0) * 31 + receiver.hashCode()
|
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 {
|
override fun toString(): String {
|
||||||
return "${if (name == "<init>") "constructor" else "function " + name}"
|
return "${if (name == "<init>") "constructor" else "function " + name}"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,24 +7,12 @@ package kotlin.native.internal
|
|||||||
|
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
import kotlin.reflect.KFunction
|
import kotlin.reflect.KFunction
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
@FixmeReflection
|
@FixmeReflection
|
||||||
internal abstract class KSuspendFunctionImpl<out R>(
|
internal abstract class KSuspendFunctionImpl<out R>(
|
||||||
override val name: String, val fqName: String,
|
name: String, fqName: String, receiver: Any?,
|
||||||
val bound: Boolean, val receiver: Any?,
|
arity: Int, flags: Int, returnType: KType
|
||||||
override val returnType: KType
|
): KFunctionImpl<R>(name, fqName, receiver, arity, flags, returnType) {
|
||||||
): KFunction<R> {
|
override fun toString() = "suspend function $name"
|
||||||
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user