[IR] Support reflection for MFVC

Signed-off-by: Evgeniy.Zhelenskiy <Evgeniy.Zhelenskiy@jetbrains.com>

#KT-1179
This commit is contained in:
Evgeniy.Zhelenskiy
2023-03-02 01:48:02 +01:00
committed by Space Team
parent 1ff4906880
commit 88f293d4a9
52 changed files with 2910 additions and 172 deletions
@@ -8,13 +8,14 @@ package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
import org.jetbrains.kotlin.types.asSimpleType
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.lang.reflect.WildcardType
import java.util.*
import kotlin.coroutines.Continuation
import kotlin.reflect.*
import kotlin.reflect.jvm.internal.calls.Caller
import kotlin.reflect.jvm.internal.calls.getMfvcUnboxMethods
import kotlin.reflect.jvm.javaType
import kotlin.reflect.jvm.jvmErasure
import java.lang.reflect.Array as ReflectArray
@@ -113,7 +114,9 @@ internal abstract class KCallableImpl<out R> : KCallable<R>, KTypeParameterOwner
private val _absentArguments = ReflectProperties.lazySoft {
val parameterSize = parameters.size + (if (isSuspend) 1 else 0)
val maskSize = (parameters.size + Integer.SIZE - 1) / Integer.SIZE
val flattenedParametersSize =
if (parametersNeedMFVCFlattening.value) parameters.sumOf { getParameterTypeSize(it) } else parameters.size
val maskSize = (flattenedParametersSize + Integer.SIZE - 1) / Integer.SIZE
// Array containing the actual function arguments, masks, and +1 for DefaultConstructorMarker or MethodHandle.
val arguments = arrayOfNulls<Any?>(parameterSize + maskSize + 1)
@@ -161,14 +164,23 @@ internal abstract class KCallableImpl<out R> : KCallable<R>, KTypeParameterOwner
var valueParameterIndex = 0
var anyOptional = false
val hasMfvcParameters = parametersNeedMFVCFlattening.value
for (parameter in parameters) {
val parameterTypeSize = if (hasMfvcParameters) getParameterTypeSize(parameter) else 1
when {
args.containsKey(parameter) -> {
arguments[parameter.index] = args[parameter]
}
parameter.isOptional -> {
val maskIndex = parameterSize + (valueParameterIndex / Integer.SIZE)
arguments[maskIndex] = (arguments[maskIndex] as Int) or (1 shl (valueParameterIndex % Integer.SIZE))
if (hasMfvcParameters) {
for (valueSubParameterIndex in valueParameterIndex until (valueParameterIndex + parameterTypeSize)) {
val maskIndex = parameterSize + (valueSubParameterIndex / Integer.SIZE)
arguments[maskIndex] = (arguments[maskIndex] as Int) or (1 shl (valueSubParameterIndex % Integer.SIZE))
}
} else {
val maskIndex = parameterSize + (valueParameterIndex / Integer.SIZE)
arguments[maskIndex] = (arguments[maskIndex] as Int) or (1 shl (valueParameterIndex % Integer.SIZE))
}
anyOptional = true
}
parameter.isVararg -> {}
@@ -178,7 +190,7 @@ internal abstract class KCallableImpl<out R> : KCallable<R>, KTypeParameterOwner
}
if (parameter.kind == KParameter.Kind.VALUE) {
valueParameterIndex++
valueParameterIndex += parameterTypeSize
}
}
@@ -197,6 +209,20 @@ internal abstract class KCallableImpl<out R> : KCallable<R>, KTypeParameterOwner
}
}
private val parametersNeedMFVCFlattening = lazy(LazyThreadSafetyMode.PUBLICATION) {
parameters.any { it.type.needsMultiFieldValueClassFlattening }
}
private fun getParameterTypeSize(parameter: KParameter): Int {
require(parametersNeedMFVCFlattening.value) { "Check if parametersNeedMFVCFlattening is true before" }
return if (parameter.type.needsMultiFieldValueClassFlattening) {
val type = (parameter.type as KTypeImpl).type.asSimpleType()
getMfvcUnboxMethods(type)!!.size
} else {
1
}
}
private fun callAnnotationConstructor(args: Map<KParameter, Any?>): R {
val arguments = parameters.map { parameter ->
when {
@@ -24,10 +24,12 @@ import org.jetbrains.kotlin.descriptors.runtime.structure.wrapperByPrimitive
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.isMultiFieldValueClass
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import java.lang.reflect.Constructor
import java.lang.reflect.Method
import kotlin.jvm.internal.ClassBasedDeclarationContainer
import kotlin.reflect.jvm.internal.calls.toJvmDescriptor
internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContainer {
abstract inner class Data {
@@ -119,9 +121,25 @@ internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContain
}
fun findFunctionDescriptor(name: String, signature: String): FunctionDescriptor {
val members = if (name == "<init>") constructorDescriptors.toList() else getFunctions(Name.identifier(name))
val functions = members.filter { descriptor ->
RuntimeTypeMapper.mapSignature(descriptor).asString() == signature
val members: Collection<FunctionDescriptor>
val functions: List<FunctionDescriptor>
if (name == "<init>") {
members = constructorDescriptors.toList()
functions = members.filter { descriptor ->
val descriptorSignature = if (descriptor.isPrimary && descriptor.containingDeclaration.isMultiFieldValueClass()) {
val initial = RuntimeTypeMapper.mapSignature(descriptor).asString()
require(initial.startsWith("constructor-impl") && initial.endsWith(")V")) {
"Invalid signature of $descriptor: $initial"
}
initial.removeSuffix("V") + descriptor.containingDeclaration.toJvmDescriptor()
} else {
RuntimeTypeMapper.mapSignature(descriptor).asString()
}
descriptorSignature == signature
}
} else {
members = getFunctions(Name.identifier(name))
functions = members.filter { descriptor -> RuntimeTypeMapper.mapSignature(descriptor).asString() == signature }
}
if (functions.size != 1) {
@@ -17,8 +17,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.jvm.shouldHideConstructorDueToInlineClassTypeValueParameters
import org.jetbrains.kotlin.resolve.isMultiFieldValueClass
import org.jetbrains.kotlin.resolve.jvm.shouldHideConstructorDueToValueClassTypeValueParameters
import java.lang.reflect.Constructor
import java.lang.reflect.Member
import java.lang.reflect.Method
@@ -66,7 +68,14 @@ internal class KFunctionImpl private constructor(
return@caller AnnotationConstructorCaller(container.jClass, parameters.map { it.name!! }, POSITIONAL_CALL, KOTLIN)
container.findConstructorBySignature(jvmSignature.constructorDesc)
}
is KotlinFunction -> container.findMethodBySignature(jvmSignature.methodName, jvmSignature.methodDesc)
is KotlinFunction -> {
if (descriptor.let { it.containingDeclaration.isMultiFieldValueClass() && it is ConstructorDescriptor && it.isPrimary }) {
return@caller ValueClassAwareCaller.MultiFieldValueClassPrimaryConstructorCaller(
descriptor, container, jvmSignature.methodDesc, descriptor.valueParameters
)
}
container.findMethodBySignature(jvmSignature.methodName, jvmSignature.methodDesc)
}
is JavaMethod -> jvmSignature.method
is JavaConstructor -> jvmSignature.constructor
is FakeJavaAnnotationConstructor -> {
@@ -87,13 +96,16 @@ internal class KFunctionImpl private constructor(
createStaticMethodCaller(member)
}
else -> throw KotlinReflectionInternalError("Could not compute caller for function: $descriptor (member = $member)")
}.createInlineClassAwareCallerIfNeeded(descriptor)
}.createValueClassAwareCallerIfNeeded(descriptor)
}
override val defaultCaller: Caller<*>? by lazy(PUBLICATION) defaultCaller@{
val jvmSignature = RuntimeTypeMapper.mapSignature(descriptor)
val member: Member? = when (jvmSignature) {
is KotlinFunction -> {
if (descriptor.let { it.containingDeclaration.isMultiFieldValueClass() && it is ConstructorDescriptor && it.isPrimary }) {
throw KotlinReflectionInternalError("${descriptor.containingDeclaration} cannot have default arguments")
}
container.findDefaultMethod(jvmSignature.methodName, jvmSignature.methodDesc, !Modifier.isStatic(caller.member!!.modifiers))
}
is KotlinConstructor -> {
@@ -126,7 +138,7 @@ internal class KFunctionImpl private constructor(
createStaticMethodCaller(member)
}
else -> null
}?.createInlineClassAwareCallerIfNeeded(descriptor, isDefault = true)
}?.createValueClassAwareCallerIfNeeded(descriptor, isDefault = true)
}
private val boundReceiver
@@ -144,7 +156,7 @@ internal class KFunctionImpl private constructor(
private fun createConstructorCaller(
member: Constructor<*>, descriptor: FunctionDescriptor, isDefault: Boolean
): CallerImpl<Constructor<*>> {
return if (!isDefault && shouldHideConstructorDueToInlineClassTypeValueParameters(descriptor)) {
return if (!isDefault && shouldHideConstructorDueToValueClassTypeValueParameters(descriptor)) {
if (isBound)
CallerImpl.AccessorForHiddenBoundConstructor(member, boundReceiver)
else
@@ -18,8 +18,10 @@ package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.resolve.descriptorUtil.declaresOrInheritsDefaultValue
import java.lang.reflect.Type
import kotlin.reflect.KParameter
import kotlin.reflect.KType
import kotlin.reflect.jvm.internal.calls.ValueClassAwareCaller
internal class KParameterImpl(
val callable: KCallableImpl<*>,
@@ -39,6 +41,27 @@ internal class KParameterImpl(
return if (name.isSpecial) null else name.asString()
}
private fun compoundType(vararg types: Type): Type = when (types.size) {
0 -> throw KotlinReflectionNotSupportedError("Expected at least 1 type for compound type")
1 -> types.single()
else -> CompoundTypeImpl(types)
}
private class CompoundTypeImpl(val types: Array<out Type>) : Type {
private val hashCode = types.contentHashCode()
override fun getTypeName(): String {
return types.joinToString(", ", "[", "]")
}
override fun equals(other: Any?): Boolean =
other is CompoundTypeImpl && this.types contentEquals other.types
override fun hashCode(): Int = hashCode
override fun toString(): String = typeName
}
override val type: KType
get() = KTypeImpl(descriptor.type) {
val descriptor = descriptor
@@ -53,7 +76,16 @@ internal class KParameterImpl(
(callable.descriptor.containingDeclaration as ClassDescriptor).toJavaClass()
?: throw KotlinReflectionInternalError("Cannot determine receiver Java type of inherited declaration: $descriptor")
} else {
callable.caller.parameterTypes[index]
when (val caller = callable.caller) {
is ValueClassAwareCaller -> {
val slice = caller.getRealSlicesOfParameters(index)
val parameterTypes = caller.parameterTypes.slice(slice)
compoundType(*parameterTypes.toTypedArray())
}
is ValueClassAwareCaller.MultiFieldValueClassPrimaryConstructorCaller ->
compoundType(*caller.originalParametersGroups[index].toTypedArray())
else -> caller.parameterTypes[index]
}
}
}
@@ -271,8 +271,9 @@ private fun KPropertyImpl.Accessor<*, *>.computeCallerForAccessor(isGetter: Bool
if (property.descriptor.isUnderlyingPropertyOfInlineClass() &&
property.descriptor.visibility == DescriptorVisibilities.INTERNAL
) {
val unboxMethod = property.descriptor.containingDeclaration.toInlineClass()?.getUnboxMethod(property.descriptor)
?: throw KotlinReflectionInternalError("Underlying property of inline class $property should have a field")
val unboxMethod =
property.descriptor.containingDeclaration.toInlineClass()?.getInlineClassUnboxMethod(property.descriptor)
?: throw KotlinReflectionInternalError("Underlying property of inline class $property should have a field")
if (isBound) InternalUnderlyingValOfInlineClass.Bound(unboxMethod, boundReceiver)
else InternalUnderlyingValOfInlineClass.Unbound(unboxMethod)
} else {
@@ -316,7 +317,7 @@ private fun KPropertyImpl.Accessor<*, *>.computeCallerForAccessor(isGetter: Bool
return if (isBound) CallerImpl.Method.BoundInstance(accessor, boundReceiver)
else CallerImpl.Method.Instance(accessor)
}
}.createInlineClassAwareCallerIfNeeded(descriptor)
}.createValueClassAwareCallerIfNeeded(descriptor)
}
private fun PropertyDescriptor.isJvmFieldPropertyInCompanionObject(): Boolean {
@@ -44,6 +44,7 @@ import org.jetbrains.kotlin.resolve.DescriptorFactory
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.propertyIfAccessor
import org.jetbrains.kotlin.resolve.isInlineClass
import org.jetbrains.kotlin.resolve.isMultiFieldValueClass
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
@@ -51,6 +52,7 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ
import java.lang.reflect.Constructor
import java.lang.reflect.Field
import java.lang.reflect.Method
import kotlin.reflect.jvm.internal.calls.toJvmDescriptor
internal sealed class JvmFunctionSignature {
abstract fun asString(): String
@@ -174,10 +176,27 @@ internal object RuntimeTypeMapper {
}
if (proto is ProtoBuf.Constructor) {
JvmProtoBufUtil.getJvmConstructorSignature(proto, function.nameResolver, function.typeTable)?.let { signature ->
return if (possiblySubstitutedFunction.containingDeclaration.isInlineClass())
JvmFunctionSignature.KotlinFunction(signature)
else
JvmFunctionSignature.KotlinConstructor(signature)
return when {
possiblySubstitutedFunction.containingDeclaration.isInlineClass() ->
JvmFunctionSignature.KotlinFunction(signature)
possiblySubstitutedFunction.containingDeclaration.isMultiFieldValueClass() -> {
val realSignature = if ((possiblySubstitutedFunction as ConstructorDescriptor).isPrimary) {
require(signature.name == "constructor-impl" && signature.desc.endsWith(")V")) { "Invalid signature: $signature" }
signature
} else {
require(signature.name == "constructor-impl") { "Invalid signature: $signature" }
val constructedClass = possiblySubstitutedFunction.constructedClass.toJvmDescriptor()
if (signature.desc.endsWith(")V")) {
signature.copy(desc = signature.desc.removeSuffix("V") + constructedClass)
} else {
require(signature.desc.endsWith(constructedClass)) { "Invalid signature: $signature" }
signature
}
}
JvmFunctionSignature.KotlinFunction(realSignature)
}
else -> JvmFunctionSignature.KotlinConstructor(signature)
}
}
}
return mapJvmFunctionSignature(function)
@@ -122,7 +122,7 @@ internal sealed class CallerImpl<out M : Member>(
}
}
class BoundStatic(method: ReflectMethod, private val boundReceiver: Any?) : BoundCaller, Method(
class BoundStatic(method: ReflectMethod, internal val boundReceiver: Any?) : BoundCaller, Method(
method, requiresInstance = false, parameterTypes = method.genericParameterTypes.dropFirst()
) {
override fun call(args: Array<*>): Any? {
@@ -131,6 +131,21 @@ internal sealed class CallerImpl<out M : Member>(
}
}
class BoundStaticMultiFieldValueClass(
method: ReflectMethod, internal val boundReceiverComponents: Array<Any?>
) : BoundCaller, Method(
method = method,
requiresInstance = false,
parameterTypes = method.genericParameterTypes.drop(boundReceiverComponents.size).toTypedArray()
) {
override fun call(args: Array<*>): Any? {
checkArguments(args)
return callMethod(null, arrayOf(*boundReceiverComponents, *args))
}
val receiverComponentsCount: Int get() = boundReceiverComponents.size
}
class BoundInstance(method: ReflectMethod, private val boundReceiver: Any?) : BoundCaller,
Method(method, requiresInstance = false) {
override fun call(args: Array<*>): Any? {
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -7,29 +7,50 @@ package kotlin.reflect.jvm.internal.calls
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.runtime.structure.desc
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.metadata.jvm.deserialization.ClassMapperLite
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.multiFieldValueClassRepresentation
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 java.lang.reflect.Member
import java.lang.reflect.Method
import java.lang.reflect.Type
import kotlin.reflect.jvm.internal.KDeclarationContainerImpl
import kotlin.reflect.jvm.internal.KotlinReflectionInternalError
import kotlin.reflect.jvm.internal.defaultPrimitiveValue
import kotlin.reflect.jvm.internal.toJavaClass
/**
* A caller that is used whenever the declaration has inline classes in its parameter types or return type.
* Each argument of an inline class type is unboxed, and the return value (if it's of an inline class type) is boxed.
* 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.
*/
internal class InlineClassAwareCaller<out M : Member?>(
internal class ValueClassAwareCaller<out M : Member?>(
descriptor: CallableMemberDescriptor,
private val caller: Caller<M>,
oldCaller: Caller<M>,
private val isDefault: Boolean
) : Caller<M> {
override val member: M
get() = caller.member
private val caller: Caller<M> = if (oldCaller is CallerImpl.Method.BoundStatic) {
val receiverType = (descriptor.extensionReceiverParameter ?: descriptor.dispatchReceiverParameter)?.type
if (receiverType != null && receiverType.needsMfvcFlattening()) {
val unboxMethods = getMfvcUnboxMethods(receiverType.asSimpleType())!!
val boundReceiverComponents = unboxMethods.map { it.invoke(oldCaller.boundReceiver) }.toTypedArray()
@Suppress("UNCHECKED_CAST")
CallerImpl.Method.BoundStaticMultiFieldValueClass(oldCaller.member, boundReceiverComponents) as Caller<M>
} else {
oldCaller
}
} else {
oldCaller
}
override val member: M = caller.member
override val returnType: Type
get() = caller.returnType
@@ -37,24 +58,18 @@ internal class InlineClassAwareCaller<out M : Member?>(
override val parameterTypes: List<Type>
get() = caller.parameterTypes
private class BoxUnboxData(val argumentRange: IntRange, val unbox: Array<Method?>, val box: Method?) {
operator fun component1(): IntRange = argumentRange
operator fun component2(): Array<Method?> = unbox
operator fun component3(): Method? = box
}
private class BoxUnboxData(val argumentRange: IntRange, val unboxParameters: Array<List<Method>?>, val box: Method?)
private val data: BoxUnboxData = run {
val box = descriptor.returnType!!.toInlineClass()?.getBoxMethod(descriptor)
if (descriptor.isGetterOfUnderlyingPropertyOfInlineClass()) {
// Getter of the underlying val of an inline class is always called on a boxed receiver,
// no argument boxing/unboxing is required.
// However, its result might require boxing if it is an inline class type.
if (descriptor.isGetterOfUnderlyingPropertyOfValueClass()) {
// Getter of the underlying val of a value class is always called on a boxed receiver,
// no argument unboxing is required.
return@run BoxUnboxData(IntRange.EMPTY, emptyArray(), box)
}
val shift = when {
caller is CallerImpl.Method.BoundStatic -> {
caller is CallerImpl.Method.BoundStatic || caller is CallerImpl.Method.BoundStaticMultiFieldValueClass -> {
// Bound reference to a static method is only possible for a top level extension function/property,
// and in that case the number of expected arguments is one less than usual, hence -1
-1
@@ -64,9 +79,9 @@ internal class InlineClassAwareCaller<out M : Member?>(
if (caller is BoundCaller) -1 else 0
descriptor.dispatchReceiverParameter != null && caller !is BoundCaller -> {
// If we have an unbound reference to the inline class member,
// If we have an unbound reference to the value class member,
// its receiver (which is passed as argument 0) should also be unboxed.
if (descriptor.containingDeclaration.isInlineClass())
if (descriptor.containingDeclaration.isValueClass())
0
else
1
@@ -75,117 +90,229 @@ internal class InlineClassAwareCaller<out M : Member?>(
else -> 0
}
val kotlinParameterTypes: List<KotlinType> = ArrayList<KotlinType>().also { kotlinParameterTypes ->
val extensionReceiverType = descriptor.extensionReceiverParameter?.type
if (extensionReceiverType != null) {
kotlinParameterTypes.add(extensionReceiverType)
} else if (descriptor is ConstructorDescriptor) {
val constructedClass = descriptor.constructedClass
if (constructedClass.isInner) {
kotlinParameterTypes.add((constructedClass.containingDeclaration as ClassDescriptor).defaultType)
}
} else {
val containingDeclaration = descriptor.containingDeclaration
if (containingDeclaration is ClassDescriptor && containingDeclaration.isInlineClass()) {
kotlinParameterTypes.add(containingDeclaration.defaultType)
}
}
val flattenedShift = if (caller is CallerImpl.Method.BoundStaticMultiFieldValueClass) -caller.receiverComponentsCount else shift
descriptor.valueParameters.mapTo(kotlinParameterTypes, ValueParameterDescriptor::getType)
}
val kotlinParameterTypes: List<KotlinType> = makeKotlinParameterTypes(descriptor) { isValueClass() }
fun typeSize(type: KotlinType): Int = getMfvcUnboxMethods(type.asSimpleType())?.size ?: 1
// If the default argument is set,
// (kotlinParameterTypes.size + Integer.SIZE - 1) / Integer.SIZE masks and one marker are added to the end of the argument.
val extraArgumentsTail = (if (isDefault) ((kotlinParameterTypes.size + Integer.SIZE - 1) / Integer.SIZE) + 1 else 0) +
(if (descriptor is FunctionDescriptor && descriptor.isSuspend) 1 else 0)
val expectedArgsSize = kotlinParameterTypes.size + shift + extraArgumentsTail
if (arity != expectedArgsSize) {
throw KotlinReflectionInternalError(
"Inconsistent number of parameters in the descriptor and Java reflection object: $arity != $expectedArgsSize\n" +
"Calling: $descriptor\n" +
"Parameter types: ${this.parameterTypes})\n" +
"Default: $isDefault"
)
}
// (kotlinParameterTypes.size + Int.SIZE_BITS - 1) / Int.SIZE_BITS masks and one marker are added to the end of the argument.
val extraArgumentsTail =
(if (isDefault) ((kotlinParameterTypes.sumOf(::typeSize) + Int.SIZE_BITS - 1) / Int.SIZE_BITS) + 1 else 0) +
(if (descriptor is FunctionDescriptor && descriptor.isSuspend) 1 else 0)
val expectedArgsSize = kotlinParameterTypes.sumOf(::typeSize) + flattenedShift + extraArgumentsTail
checkParametersSize(expectedArgsSize, descriptor, isDefault)
// maxOf is needed because in case of a bound top level extension, shift can be -1 (see above). But in that case, we need not unbox
// the extension receiver argument, since it has already been unboxed at compile time and generated into the reference
val argumentRange = maxOf(shift, 0) until (kotlinParameterTypes.size + shift)
val unbox = Array(expectedArgsSize) { i ->
if (i in argumentRange) {
kotlinParameterTypes[i - shift].toInlineClass()?.getUnboxMethod(descriptor)
} else null
if (i in argumentRange)
getValueClassUnboxMethods(kotlinParameterTypes[i - shift].asSimpleType(), descriptor)
else null
}
BoxUnboxData(argumentRange, unbox, box)
}
private val slices = buildList {
var currentOffset = when (caller) {
is CallerImpl.Method.BoundStaticMultiFieldValueClass -> caller.boundReceiverComponents.size
is CallerImpl.Method.BoundStatic -> 1
else -> 0
}
if (currentOffset > 0) {
add(0 until currentOffset)
}
for (parameterUnboxMethods in data.unboxParameters) {
val length = parameterUnboxMethods?.size ?: 1
add(currentOffset until (currentOffset + length))
currentOffset += length
}
}.toTypedArray()
fun getRealSlicesOfParameters(index: Int): IntRange = when {
index in slices.indices -> slices[index]
slices.isEmpty() -> index..index
else -> {
val start = (index - slices.size) + (slices.last().last + 1)
start..start
}
}
private val hasMfvcParameters = data.argumentRange.any { (data.unboxParameters[it] ?: return@any false).size > 1 }
override fun call(args: Array<*>): Any? {
val (range, unbox, box) = data
val range = data.argumentRange
val unbox = data.unboxParameters
val box = data.box
@Suppress("UNCHECKED_CAST")
val unboxed = Array(args.size) { index ->
val arg = args[index]
if (index in range) {
// 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
val method = unbox[index]
if (method != null) {
if (arg != null) {
method.invoke(arg)
val unboxedArguments = when {
range.isEmpty() -> args
hasMfvcParameters -> buildList(args.size) {
for (index in 0 until range.first) {
add(args[index])
}
for (index in range) {
val methods = unbox[index]
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 value class type
if (methods != null) {
methods.mapTo(this) { if (arg != null) it.invoke(arg) else defaultPrimitiveValue(it.returnType) }
} else {
defaultPrimitiveValue(method.returnType)
add(arg)
}
}
for (index in (range.last + 1)..args.lastIndex) {
add(args[index])
}
}.toTypedArray()
else -> Array(args.size) { index ->
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
when {
method == null -> arg
arg != null -> method.invoke(arg)
else -> defaultPrimitiveValue(method.returnType)
}
} else {
arg
args[index]
}
} else {
arg
}
}
val result = caller.call(unboxed)
val result = caller.call(unboxedArguments)
// box is not null only for inline classes
return box?.invoke(null, result) ?: result
}
class MultiFieldValueClassPrimaryConstructorCaller(
descriptor: FunctionDescriptor,
container: KDeclarationContainerImpl,
constructorDesc: String,
originalParameters: List<ParameterDescriptor>
) : Caller<Nothing?> {
private val constructorImpl = container.findMethodBySignature("constructor-impl", constructorDesc)!!
private val boxMethod = container.findMethodBySignature("box-impl", constructorDesc.removeSuffix("V") + container.jClass.desc)!!
private val parameterUnboxMethods: List<List<Method>?> = originalParameters.map { parameter ->
getValueClassUnboxMethods(parameter.type.asSimpleType(), descriptor)
}
override val member: Nothing?
get() = null
override val returnType: Type
get() = boxMethod.returnType
val originalParametersGroups: List<List<Class<*>>> = originalParameters.mapIndexed { index, it ->
val classDescriptor = it.type.constructor.declarationDescriptor as ClassDescriptor
parameterUnboxMethods[index]?.map { it.returnType } ?: listOf(classDescriptor.toJavaClass()!!)
}
override val parameterTypes: List<Type> = originalParametersGroups.flatten()
override fun call(args: Array<*>): Any? {
val newArgs = (args zip parameterUnboxMethods)
.flatMap { (arg, unboxMethods) -> unboxMethods?.map { it.invoke(arg) } ?: listOf(arg) }.toTypedArray()
constructorImpl.invoke(null, *newArgs)
return boxMethod.invoke(null, *newArgs)
}
}
}
internal fun <M : Member?> Caller<M>.createInlineClassAwareCallerIfNeeded(
internal fun ClassifierDescriptor.toJvmDescriptor(): String = ClassMapperLite.mapClass(classId!!.asString())
private fun getValueClassUnboxMethods(type: SimpleType, descriptor: CallableMemberDescriptor): List<Method>? =
getMfvcUnboxMethods(type) ?: type.toInlineClass()?.getInlineClassUnboxMethod(descriptor)?.let(::listOf)
internal fun getMfvcUnboxMethods(type: SimpleType): List<Method>? {
fun getUnboxMethodNameSuffixes(type: SimpleType): List<String>? =
if (type.needsMfvcFlattening()) (type.constructor.declarationDescriptor as ClassDescriptor)
.multiFieldValueClassRepresentation!!.underlyingPropertyNamesToTypes.flatMap { (name, innerType) ->
getUnboxMethodNameSuffixes(innerType)?.map { "${name.identifier}-${it}" } ?: listOf(name.identifier)
}
else null
val unboxMethodsNames = getUnboxMethodNameSuffixes(type.asSimpleType())?.map { "unbox-impl-$it" } ?: return null
val javaClass = (type.constructor.declarationDescriptor as ClassDescriptor).toJavaClass()!!
return unboxMethodsNames.map { javaClass.getDeclaredMethod(it) }
}
private fun Caller<*>.checkParametersSize(
expectedArgsSize: Int,
descriptor: CallableMemberDescriptor,
isDefault: Boolean,
) {
if (arity != expectedArgsSize) {
throw KotlinReflectionInternalError(
"Inconsistent number of parameters in the descriptor and Java reflection object: $arity != $expectedArgsSize\n" +
"Calling: $descriptor\n" +
"Parameter types: ${this.parameterTypes})\n" +
"Default: $isDefault"
)
}
}
private fun makeKotlinParameterTypes(
descriptor: CallableMemberDescriptor, isSpecificClass: ClassDescriptor.() -> Boolean
): List<KotlinType> = ArrayList<KotlinType>().also { kotlinParameterTypes ->
val extensionReceiverType = descriptor.extensionReceiverParameter?.type
if (extensionReceiverType != null) {
kotlinParameterTypes.add(extensionReceiverType)
} else if (descriptor is ConstructorDescriptor) {
val constructedClass = descriptor.constructedClass
if (constructedClass.isInner) {
kotlinParameterTypes.add((constructedClass.containingDeclaration as ClassDescriptor).defaultType)
}
} else {
val containingDeclaration = descriptor.containingDeclaration
if (containingDeclaration is ClassDescriptor && containingDeclaration.isSpecificClass()) {
kotlinParameterTypes.add(containingDeclaration.defaultType)
}
}
descriptor.valueParameters.mapTo(kotlinParameterTypes, ValueParameterDescriptor::getType)
}
internal fun <M : Member?> Caller<M>.createValueClassAwareCallerIfNeeded(
descriptor: CallableMemberDescriptor,
isDefault: Boolean = false
): Caller<M> {
val needsInlineAwareCaller: Boolean =
descriptor.isGetterOfUnderlyingPropertyOfInlineClass() ||
descriptor.valueParameters.any { it.type.isInlineClassType() } ||
val needsValueClassAwareCaller: Boolean =
descriptor.isGetterOfUnderlyingPropertyOfValueClass() ||
descriptor.contextReceiverParameters.any { it.type.isValueClassType() } ||
descriptor.valueParameters.any { it.type.isValueClassType() } ||
descriptor.returnType?.isInlineClassType() == true ||
this !is BoundCaller && descriptor.hasInlineClassReceiver()
descriptor.hasValueClassReceiver()
return if (needsInlineAwareCaller) InlineClassAwareCaller(descriptor, this, isDefault) else this
return if (needsValueClassAwareCaller) ValueClassAwareCaller(descriptor, this, isDefault) else this
}
private fun CallableMemberDescriptor.hasInlineClassReceiver() =
expectedReceiverType?.isInlineClassType() == true
private fun CallableMemberDescriptor.hasValueClassReceiver() =
expectedReceiverType?.isValueClassType() == true
internal fun Class<*>.getUnboxMethod(descriptor: CallableMemberDescriptor): Method =
internal fun Class<*>.getInlineClassUnboxMethod(descriptor: CallableMemberDescriptor): Method =
try {
getDeclaredMethod("unbox" + JvmAbi.IMPL_SUFFIX_FOR_INLINE_CLASS_MEMBERS)
} catch (e: NoSuchMethodException) {
throw KotlinReflectionInternalError("No unbox method found in inline class: $this (calling $descriptor)")
}
internal fun Class<*>.getBoxMethod(descriptor: CallableMemberDescriptor): Method =
private fun Class<*>.getBoxMethod(descriptor: CallableMemberDescriptor): Method =
try {
getDeclaredMethod("box" + JvmAbi.IMPL_SUFFIX_FOR_INLINE_CLASS_MEMBERS, getUnboxMethod(descriptor).returnType)
getDeclaredMethod("box" + JvmAbi.IMPL_SUFFIX_FOR_INLINE_CLASS_MEMBERS, getInlineClassUnboxMethod(descriptor).returnType)
} catch (e: NoSuchMethodException) {
throw KotlinReflectionInternalError("No box method found in inline class: $this (calling $descriptor)")
}
internal fun KotlinType.toInlineClass(): Class<*>? {
private fun KotlinType.toInlineClass(): Class<*>? {
// See computeExpandedTypeForInlineClass.
// TODO: add tests on type parameters with inline class bounds.
// TODO: add tests on usages of inline classes in Java.
// TODO: add tests on type parameters with value class bounds.
// TODO: add tests on usages of value classes in Java.
val klass = constructor.declarationDescriptor.toInlineClass() ?: return null
if (!TypeUtils.isNullableType(this)) return klass
@@ -217,7 +344,7 @@ internal fun Any?.coerceToExpectedReceiverType(descriptor: CallableMemberDescrip
if (descriptor is PropertyDescriptor && descriptor.isUnderlyingPropertyOfInlineClass()) return this
val expectedReceiverType = descriptor.expectedReceiverType
val unboxMethod = expectedReceiverType?.toInlineClass()?.getUnboxMethod(descriptor) ?: return this
val unboxMethod = expectedReceiverType?.toInlineClass()?.getInlineClassUnboxMethod(descriptor) ?: return this
return unboxMethod.invoke(this)
}
@@ -44,6 +44,7 @@ import org.jetbrains.kotlin.resolve.constants.*
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.isInlineClassType
import org.jetbrains.kotlin.resolve.needsMfvcFlattening
import org.jetbrains.kotlin.serialization.deserialization.DeserializationContext
import org.jetbrains.kotlin.serialization.deserialization.MemberDeserializer
import java.lang.reflect.Field
@@ -268,6 +269,8 @@ internal fun <M : MessageLite, D : CallableDescriptor> deserializeToDescriptor(
internal val KType.isInlineClassType: Boolean
get() = (this as? KTypeImpl)?.type?.isInlineClassType() == true
internal val KType.needsMultiFieldValueClassFlattening: Boolean
get() = (this as? KTypeImpl)?.type?.needsMfvcFlattening() == true
internal fun defaultPrimitiveValue(type: Type): Any? =
if (type is Class<*> && type.isPrimitive) {