[Reflection] Support callBy for inline class interface functions with default parameters
#KT-57972
This commit is contained in:
committed by
Space Team
parent
1e7cc00dcb
commit
b0367d9399
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user