[Reflection] Support callBy for inline class interface functions with default parameters

#KT-57972
This commit is contained in:
Evgeniy.Zhelenskiy
2024-01-29 04:04:59 +00:00
committed by Space Team
parent 1e7cc00dcb
commit b0367d9399
18 changed files with 691 additions and 13 deletions
@@ -19,7 +19,10 @@ package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.resolve.descriptorUtil.overriddenTreeAsSequence
import org.jetbrains.kotlin.resolve.isInlineClassType
import org.jetbrains.kotlin.resolve.isMultiFieldValueClass
import org.jetbrains.kotlin.resolve.isValueClass
import org.jetbrains.kotlin.resolve.jvm.shouldHideConstructorDueToValueClassTypeValueParameters
import java.lang.reflect.Constructor
import java.lang.reflect.Member
@@ -100,12 +103,17 @@ internal class KFunctionImpl private constructor(
}
override val defaultCaller: Caller<*>? by lazy(PUBLICATION) defaultCaller@{
val jvmSignature = RuntimeTypeMapper.mapSignature(descriptor)
val member: Member? = when (jvmSignature) {
is KotlinFunction -> {
val member: Member? = when (val jvmSignature = RuntimeTypeMapper.mapSignature(descriptor)) {
is KotlinFunction -> run {
if (descriptor.let { it.containingDeclaration.isMultiFieldValueClass() && it is ConstructorDescriptor && it.isPrimary }) {
throw KotlinReflectionInternalError("${descriptor.containingDeclaration} cannot have default arguments")
}
getFunctionWithDefaultParametersForValueClassOverride(descriptor)?.let { defaultImplsFunction ->
val replacingJvmSignature = RuntimeTypeMapper.mapSignature(defaultImplsFunction) as KotlinFunction
return@run container.findDefaultMethod(replacingJvmSignature.methodName, replacingJvmSignature.methodDesc, true)
}
container.findDefaultMethod(jvmSignature.methodName, jvmSignature.methodDesc, !Modifier.isStatic(caller.member!!.modifiers))
}
is KotlinConstructor -> {
@@ -141,11 +149,32 @@ internal class KFunctionImpl private constructor(
}?.createValueClassAwareCallerIfNeeded(descriptor, isDefault = true)
}
private fun getFunctionWithDefaultParametersForValueClassOverride(descriptor: FunctionDescriptor): FunctionDescriptor? {
if (
descriptor.valueParameters.none { it.declaresDefaultValue() } &&
descriptor.containingDeclaration.isValueClass() &&
Modifier.isStatic(caller.member!!.modifiers)
) {
// firstOrNull is used to mimic the wrong behaviour of regular class reflection as KT-40327 is not fixed.
// The behaviours equality is currently backed by codegen/box/reflection/callBy/brokenDefaultParametersFromDifferentFunctions.kt.
return descriptor.overriddenTreeAsSequence(useOriginal = false)
.firstOrNull { function -> function.valueParameters.any { it.declaresDefaultValue() } } as? FunctionDescriptor
}
return null
}
private val boundReceiver
get() = rawBoundReceiver.coerceToExpectedReceiverType(descriptor)
// boundReceiver is unboxed receiver when the receiver is inline class.
// However, when the expected dispatch receiver type is an interface,
// the member belongs to the interface/DefaultImpls, so the receiver should not be unboxed.
private fun useBoxedBoundReceiver(member: Method) =
descriptor.dispatchReceiverParameter?.type?.isInlineClassType() == true && member.parameterTypes.firstOrNull()?.isInterface == true
private fun createStaticMethodCaller(member: Method) =
if (isBound) CallerImpl.Method.BoundStatic(member, boundReceiver) else CallerImpl.Method.Static(member)
if (isBound) CallerImpl.Method.BoundStatic(member, if (useBoxedBoundReceiver(member)) rawBoundReceiver else boundReceiver)
else CallerImpl.Method.Static(member)
private fun createJvmStaticInObjectCaller(member: Method) =
if (isBound) CallerImpl.Method.BoundJvmStaticInObject(member) else CallerImpl.Method.JvmStaticInObject(member)
@@ -27,7 +27,7 @@ internal class KParameterImpl(
val callable: KCallableImpl<*>,
override val index: Int,
override val kind: KParameter.Kind,
computeDescriptor: () -> ParameterDescriptor
computeDescriptor: () -> ParameterDescriptor,
) : KParameter {
private val descriptor: ParameterDescriptor by ReflectProperties.lazySoft(computeDescriptor)
@@ -78,8 +78,14 @@ internal class KParameterImpl(
} else {
when (val caller = callable.caller) {
is ValueClassAwareCaller -> {
val slice = caller.getRealSlicesOfParameters(index)
val parameterTypes = caller.parameterTypes.slice(slice)
val parameterTypes = if (callable.isBound) {
val slice = caller.getRealSlicesOfParameters(index + 1)
val offset = caller.getRealSlicesOfParameters(0).last + 1
caller.parameterTypes.slice((slice.first - offset)..(slice.last - offset))
} else {
val slice = caller.getRealSlicesOfParameters(index)
caller.parameterTypes.slice(slice)
}
compoundType(*parameterTypes.toTypedArray())
}
is ValueClassAwareCaller.MultiFieldValueClassPrimaryConstructorCaller ->
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.SimpleType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.asSimpleType
import org.jetbrains.kotlin.types.typeUtil.makeNullable
import java.lang.reflect.Member
import java.lang.reflect.Method
import java.lang.reflect.Type
@@ -28,7 +29,7 @@ import kotlin.reflect.jvm.internal.toJavaClass
/**
* A caller that is used whenever the declaration has value classes in its parameter types or inline class in return type.
* Each argument of an value class type is unboxed, and the return value (if it's of an inline class type) is boxed.
* Each argument of a value class type is unboxed, and the return value (if it's of an inline class type) is boxed.
*/
internal class ValueClassAwareCaller<out M : Member?>(
descriptor: CallableMemberDescriptor,
@@ -38,7 +39,11 @@ internal class ValueClassAwareCaller<out M : Member?>(
private val caller: Caller<M> = if (oldCaller is CallerImpl.Method.BoundStatic) {
val receiverType = (descriptor.extensionReceiverParameter ?: descriptor.dispatchReceiverParameter)?.type
if (receiverType != null && receiverType.needsMfvcFlattening()) {
if (
receiverType != null &&
receiverType.needsMfvcFlattening() &&
(!isDefault || descriptor.valueParameters.any { it.declaresDefaultValue() })
) {
val unboxMethods = getMfvcUnboxMethods(receiverType.asSimpleType())!!
val boundReceiverComponents = unboxMethods.map { it.invoke(oldCaller.boundReceiver) }.toTypedArray()
@Suppress("UNCHECKED_CAST")
@@ -103,7 +108,7 @@ internal class ValueClassAwareCaller<out M : Member?>(
val flattenedShift = if (caller is CallerImpl.Method.BoundStaticMultiFieldValueClass) -caller.receiverComponentsCount else shift
val kotlinParameterTypes: List<KotlinType> = makeKotlinParameterTypes(descriptor) { isValueClass() }
val kotlinParameterTypes: List<KotlinType> = makeKotlinParameterTypes(descriptor, caller.member) { isValueClass() }
fun typeSize(type: KotlinType): Int = getMfvcUnboxMethods(type.asSimpleType())?.size ?: 1
@@ -184,7 +189,7 @@ internal class ValueClassAwareCaller<out M : Member?>(
if (index in range) {
val method = unbox[index]?.single()
val arg = args[index]
// Note that arg may be null in case we're calling a $default method, and it's an optional parameter of a inline class type
// Note that arg may be null in case we're calling a $default method, and it's an optional parameter of an inline class type
when {
method == null -> arg
arg != null -> method.invoke(arg)
@@ -270,7 +275,7 @@ private fun Caller<*>.checkParametersSize(
}
private fun makeKotlinParameterTypes(
descriptor: CallableMemberDescriptor, isSpecificClass: ClassDescriptor.() -> Boolean
descriptor: CallableMemberDescriptor, member: Member?, isSpecificClass: ClassDescriptor.() -> Boolean
): List<KotlinType> = ArrayList<KotlinType>().also { kotlinParameterTypes ->
val extensionReceiverType = descriptor.extensionReceiverParameter?.type
if (extensionReceiverType != null) {
@@ -283,13 +288,32 @@ private fun makeKotlinParameterTypes(
} else {
val containingDeclaration = descriptor.containingDeclaration
if (containingDeclaration is ClassDescriptor && containingDeclaration.isSpecificClass()) {
kotlinParameterTypes.add(containingDeclaration.defaultType)
if (member?.acceptsBoxedReceiverParameter() == true) {
// hack to forbid unboxing dispatchReceiver if it is used upcasted
// kotlinParameterTypes are used to determine shifts and calls according to whether type is MFVC/IC or not.
// If it is a MFVC/IC, boxes are unboxed. If the actual called member lies in the interface/DefaultImpls class,
// it accepts a boxed parameter as ex-dispatch receiver. Making the type nullable allows to prevent unboxing in this case.
kotlinParameterTypes.add(containingDeclaration.defaultType.makeNullable())
} else {
kotlinParameterTypes.add(containingDeclaration.defaultType)
}
}
}
descriptor.valueParameters.mapTo(kotlinParameterTypes, ValueParameterDescriptor::getType)
}
private fun Member.acceptsBoxedReceiverParameter(): Boolean {
// Method implementation can be placed either in
// * the value class itself,
// * interface$DefaultImpls,
// * interface default method (Java 8+).
// Here we need to understand that it is the second or the third case. Both of the cases cannot be value classes,
// so the simplest solution is to check declaringClass for being a value class.
val clazz = declaringClass ?: return false
return !clazz.kotlin.isValue
}
internal fun <M : Member?> Caller<M>.createValueClassAwareCallerIfNeeded(
descriptor: CallableMemberDescriptor,
isDefault: Boolean = false