Support reflection for local delegated properties

#KT-15222 Fixed
This commit is contained in:
Alexander Udalov
2017-06-01 19:42:59 +03:00
parent c444c4d10b
commit 616d575fb6
27 changed files with 559 additions and 48 deletions
@@ -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)
}