Support reflection for local delegated properties
#KT-15222 Fixed
This commit is contained in:
@@ -36,6 +36,10 @@ internal object EmptyContainerForLocal : KDeclarationContainerImpl() {
|
||||
|
||||
override fun getFunctions(name: Name): Collection<FunctionDescriptor> = fail()
|
||||
|
||||
private fun fail(): Nothing = throw KotlinReflectionInternalError("Introspecting local functions, lambdas and anonymous functions " +
|
||||
"is not yet fully supported in Kotlin reflection")
|
||||
}
|
||||
override fun getLocalProperty(index: Int): PropertyDescriptor? = null
|
||||
|
||||
private fun fail(): Nothing = throw KotlinReflectionInternalError(
|
||||
"Introspecting local functions, lambdas, anonymous functions and local variables " +
|
||||
"is not yet fully supported in Kotlin reflection"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -268,6 +268,11 @@ internal abstract class FunctionCaller<out M : Member?>(
|
||||
}
|
||||
}
|
||||
|
||||
object ThrowingCaller : FunctionCaller<Nothing?>(null, Void.TYPE, null, emptyArray()) {
|
||||
override fun call(args: Array<*>): Any? {
|
||||
throw UnsupportedOperationException("call/callBy are not supported for this declaration.")
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
// TODO lazily allocate array at bound callers?
|
||||
|
||||
@@ -30,9 +30,13 @@ import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.serialization.deserialization.MemberDeserializer
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
|
||||
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
|
||||
import org.jetbrains.kotlin.utils.compactIfPossible
|
||||
import kotlin.jvm.internal.TypeIntrinsics
|
||||
import kotlin.reflect.*
|
||||
import kotlin.reflect.jvm.deserializeToDescriptor
|
||||
import kotlin.reflect.jvm.internal.KDeclarationContainerImpl.MemberBelonginess.DECLARED
|
||||
import kotlin.reflect.jvm.internal.KDeclarationContainerImpl.MemberBelonginess.INHERITED
|
||||
|
||||
@@ -195,6 +199,14 @@ internal class KClassImpl<T : Any>(override val jClass: Class<T>) : KDeclaration
|
||||
memberScope.getContributedFunctions(name, NoLookupLocation.FROM_REFLECTION) +
|
||||
staticScope.getContributedFunctions(name, NoLookupLocation.FROM_REFLECTION)
|
||||
|
||||
override fun getLocalProperty(index: Int): PropertyDescriptor? {
|
||||
return (descriptor as? DeserializedClassDescriptor)?.let { descriptor ->
|
||||
val proto = descriptor.classProto.getExtension(JvmProtoBuf.classLocalVariable, index)
|
||||
val nameResolver = descriptor.c.nameResolver
|
||||
deserializeToDescriptor(jClass, proto, nameResolver, descriptor.c.typeTable, MemberDeserializer::loadProperty)
|
||||
}
|
||||
}
|
||||
|
||||
override val simpleName: String? get() = data().simpleName
|
||||
|
||||
override val qualifiedName: String? get() = data().qualifiedName
|
||||
|
||||
@@ -45,6 +45,8 @@ internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContain
|
||||
|
||||
abstract fun getFunctions(name: Name): Collection<FunctionDescriptor>
|
||||
|
||||
abstract fun getLocalProperty(index: Int): PropertyDescriptor?
|
||||
|
||||
protected fun getMembers(scope: MemberScope, belonginess: MemberBelonginess): Collection<KCallableImpl<*>> {
|
||||
val visitor = object : DeclarationDescriptorVisitorEmptyBodies<KCallableImpl<*>, Unit>() {
|
||||
override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, data: Unit): KCallableImpl<*> =
|
||||
@@ -95,9 +97,15 @@ internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContain
|
||||
}
|
||||
|
||||
fun findPropertyDescriptor(name: String, signature: String): PropertyDescriptor {
|
||||
val match = LOCAL_PROPERTY_SIGNATURE.matchEntire(signature)
|
||||
if (match != null) {
|
||||
val (number) = match.destructured
|
||||
return getLocalProperty(number.toInt())
|
||||
?: throw KotlinReflectionInternalError("Local property #$number not found in $jClass")
|
||||
}
|
||||
|
||||
val properties = getProperties(Name.identifier(name))
|
||||
.filter { descriptor ->
|
||||
descriptor is PropertyDescriptor &&
|
||||
RuntimeTypeMapper.mapPropertySignature(descriptor).asString() == signature
|
||||
}
|
||||
|
||||
@@ -279,5 +287,7 @@ internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContain
|
||||
|
||||
companion object {
|
||||
private val DEFAULT_CONSTRUCTOR_MARKER = Class.forName("kotlin.jvm.internal.DefaultConstructorMarker")
|
||||
|
||||
internal val LOCAL_PROPERTY_SIGNATURE = "<v#(\\d+)>".toRegex()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,14 @@ import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryPackageSourceElement
|
||||
import org.jetbrains.kotlin.load.kotlin.reflect.ReflectKotlinClass
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.serialization.PackageData
|
||||
import org.jetbrains.kotlin.serialization.deserialization.MemberDeserializer
|
||||
import org.jetbrains.kotlin.serialization.deserialization.TypeTable
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
|
||||
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBufUtil
|
||||
import kotlin.reflect.KCallable
|
||||
import kotlin.reflect.jvm.deserializeToDescriptor
|
||||
import kotlin.reflect.jvm.internal.KDeclarationContainerImpl.MemberBelonginess.DECLARED
|
||||
|
||||
internal class KPackageImpl(
|
||||
@@ -33,7 +39,7 @@ internal class KPackageImpl(
|
||||
@Suppress("unused") val usageModuleName: String? = null // may be useful for debug
|
||||
) : KDeclarationContainerImpl() {
|
||||
private inner class Data : KDeclarationContainerImpl.Data() {
|
||||
val kotlinClass: ReflectKotlinClass? by ReflectProperties.lazySoft {
|
||||
private val kotlinClass: ReflectKotlinClass? by ReflectProperties.lazySoft {
|
||||
// TODO: do not read ReflectKotlinClass multiple times
|
||||
ReflectKotlinClass.create(jClass)
|
||||
}
|
||||
@@ -57,6 +63,17 @@ internal class KPackageImpl(
|
||||
}
|
||||
}
|
||||
|
||||
val metadata: PackageData? by ReflectProperties.lazy {
|
||||
kotlinClass?.classHeader?.let { header ->
|
||||
val data = header.data
|
||||
val strings = header.strings
|
||||
if (data != null && strings != null) {
|
||||
JvmProtoBufUtil.readPackageDataFrom(data, strings)
|
||||
}
|
||||
else null
|
||||
}
|
||||
}
|
||||
|
||||
val members: Collection<KCallableImpl<*>> by ReflectProperties.lazySoft {
|
||||
getMembers(scope, DECLARED).filter { member ->
|
||||
val callableDescriptor = member.descriptor as DeserializedCallableMemberDescriptor
|
||||
@@ -84,6 +101,13 @@ internal class KPackageImpl(
|
||||
override fun getFunctions(name: Name): Collection<FunctionDescriptor> =
|
||||
scope.getContributedFunctions(name, NoLookupLocation.FROM_REFLECTION)
|
||||
|
||||
override fun getLocalProperty(index: Int): PropertyDescriptor? {
|
||||
return data().metadata?.let { (nameResolver, packageProto) ->
|
||||
val proto = packageProto.getExtension(JvmProtoBuf.packageLocalVariable, index)
|
||||
deserializeToDescriptor(jClass, proto, nameResolver, TypeTable(packageProto.typeTable), MemberDeserializer::loadProperty)
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
other is KPackageImpl && jClass == other.jClass
|
||||
|
||||
|
||||
@@ -178,12 +178,14 @@ internal abstract class KPropertyImpl<out R> private constructor(
|
||||
|
||||
|
||||
private fun KPropertyImpl.Accessor<*, *>.computeCallerForAccessor(isGetter: Boolean): FunctionCaller<*> {
|
||||
if (KDeclarationContainerImpl.LOCAL_PROPERTY_SIGNATURE.matches(property.signature)) {
|
||||
return FunctionCaller.ThrowingCaller
|
||||
}
|
||||
|
||||
fun isInsideClassCompanionObject(): Boolean {
|
||||
val possibleCompanionObject = property.descriptor.containingDeclaration
|
||||
if (DescriptorUtils.isCompanionObject(possibleCompanionObject) && !DescriptorUtils.isInterface(possibleCompanionObject.containingDeclaration)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return DescriptorUtils.isCompanionObject(possibleCompanionObject) &&
|
||||
!DescriptorUtils.isInterface(possibleCompanionObject.containingDeclaration)
|
||||
}
|
||||
|
||||
fun isJvmStaticProperty() =
|
||||
|
||||
@@ -18,10 +18,13 @@
|
||||
|
||||
package kotlin.reflect.jvm
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.load.kotlin.JvmNameResolver
|
||||
import org.jetbrains.kotlin.protobuf.MessageLite
|
||||
import org.jetbrains.kotlin.serialization.ProtoBuf
|
||||
import org.jetbrains.kotlin.serialization.deserialization.DeserializationContext
|
||||
import org.jetbrains.kotlin.serialization.deserialization.MemberDeserializer
|
||||
import org.jetbrains.kotlin.serialization.deserialization.NameResolver
|
||||
import org.jetbrains.kotlin.serialization.deserialization.TypeTable
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.SinceKotlinInfoTable
|
||||
import org.jetbrains.kotlin.serialization.jvm.BitEncoding
|
||||
@@ -44,12 +47,32 @@ fun <R> Function<R>.reflect(): KFunction<R>? {
|
||||
val stringTableTypes = JvmProtoBuf.StringTableTypes.parseDelimitedFrom(input, JvmProtoBufUtil.EXTENSION_REGISTRY)
|
||||
val nameResolver = JvmNameResolver(stringTableTypes, annotation.d2)
|
||||
val proto = ProtoBuf.Function.parseFrom(input, JvmProtoBufUtil.EXTENSION_REGISTRY)
|
||||
val moduleData = javaClass.getOrCreateModule()
|
||||
val context = DeserializationContext(
|
||||
moduleData.deserialization, nameResolver, moduleData.module, TypeTable(proto.typeTable), SinceKotlinInfoTable.EMPTY,
|
||||
containerSource = null, parentTypeDeserializer = null, typeParameters = proto.typeParameterList
|
||||
)
|
||||
val descriptor = MemberDeserializer(context).loadFunction(proto)
|
||||
|
||||
val descriptor = deserializeToDescriptor(javaClass, proto, nameResolver, TypeTable(proto.typeTable), MemberDeserializer::loadFunction)
|
||||
?: return null
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return KFunctionImpl(EmptyContainerForLocal, descriptor) as KFunction<R>
|
||||
}
|
||||
|
||||
internal fun <M : MessageLite, D : CallableDescriptor> deserializeToDescriptor(
|
||||
moduleAnchor: Class<*>,
|
||||
proto: M,
|
||||
nameResolver: NameResolver,
|
||||
typeTable: TypeTable,
|
||||
createDescriptor: MemberDeserializer.(M) -> D
|
||||
): D? {
|
||||
val moduleData = moduleAnchor.getOrCreateModule()
|
||||
|
||||
val typeParameters = when (proto) {
|
||||
is ProtoBuf.Function -> proto.typeParameterList
|
||||
is ProtoBuf.Property -> proto.typeParameterList
|
||||
else -> error("Unsupported message: $proto")
|
||||
}
|
||||
|
||||
val context = DeserializationContext(
|
||||
moduleData.deserialization, nameResolver, moduleData.module, typeTable, SinceKotlinInfoTable.EMPTY,
|
||||
containerSource = null, parentTypeDeserializer = null, typeParameters = typeParameters
|
||||
)
|
||||
return MemberDeserializer(context).createDescriptor(proto)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user