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
@@ -112,8 +112,12 @@ extend TypeParameter {
extend Class {
// If absent, assumed to be JvmAbi.DEFAULT_MODULE_NAME
optional int32 class_module_name = 101 [(string_id_in_table) = true];
repeated Property class_local_variable = 102;
}
extend Package {
optional int32 package_module_name = 101 [(string_id_in_table) = true];
repeated Property package_local_variable = 102;
}
@@ -29,7 +29,7 @@ class JvmMetadataVersion(vararg numbers: Int) : BinaryVersion(*numbers) {
companion object {
@JvmField
val INSTANCE = JvmMetadataVersion(1, 1, 6)
val INSTANCE = JvmMetadataVersion(1, 1, 7)
@JvmField
val INVALID_VERSION = JvmMetadataVersion()
@@ -14,7 +14,9 @@ public final class JvmProtoBuf {
registry.add(org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.isRaw);
registry.add(org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.typeParameterAnnotation);
registry.add(org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.classModuleName);
registry.add(org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.classLocalVariable);
registry.add(org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.packageModuleName);
registry.add(org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf.packageLocalVariable);
}
public interface StringTableTypesOrBuilder extends
// @@protoc_insertion_point(interface_extends:org.jetbrains.kotlin.serialization.jvm.StringTableTypes)
@@ -3901,6 +3903,22 @@ public final class JvmProtoBuf {
101,
org.jetbrains.kotlin.protobuf.WireFormat.FieldType.INT32,
java.lang.Integer.class);
public static final int CLASS_LOCAL_VARIABLE_FIELD_NUMBER = 102;
/**
* <code>extend .org.jetbrains.kotlin.serialization.Class { ... }</code>
*/
public static final
org.jetbrains.kotlin.protobuf.GeneratedMessageLite.GeneratedExtension<
org.jetbrains.kotlin.serialization.ProtoBuf.Class,
java.util.List<org.jetbrains.kotlin.serialization.ProtoBuf.Property>> classLocalVariable = org.jetbrains.kotlin.protobuf.GeneratedMessageLite
.newRepeatedGeneratedExtension(
org.jetbrains.kotlin.serialization.ProtoBuf.Class.getDefaultInstance(),
org.jetbrains.kotlin.serialization.ProtoBuf.Property.getDefaultInstance(),
null,
102,
org.jetbrains.kotlin.protobuf.WireFormat.FieldType.MESSAGE,
false,
org.jetbrains.kotlin.serialization.ProtoBuf.Property.class);
public static final int PACKAGE_MODULE_NAME_FIELD_NUMBER = 101;
/**
* <code>extend .org.jetbrains.kotlin.serialization.Package { ... }</code>
@@ -3917,6 +3935,22 @@ public final class JvmProtoBuf {
101,
org.jetbrains.kotlin.protobuf.WireFormat.FieldType.INT32,
java.lang.Integer.class);
public static final int PACKAGE_LOCAL_VARIABLE_FIELD_NUMBER = 102;
/**
* <code>extend .org.jetbrains.kotlin.serialization.Package { ... }</code>
*/
public static final
org.jetbrains.kotlin.protobuf.GeneratedMessageLite.GeneratedExtension<
org.jetbrains.kotlin.serialization.ProtoBuf.Package,
java.util.List<org.jetbrains.kotlin.serialization.ProtoBuf.Property>> packageLocalVariable = org.jetbrains.kotlin.protobuf.GeneratedMessageLite
.newRepeatedGeneratedExtension(
org.jetbrains.kotlin.serialization.ProtoBuf.Package.getDefaultInstance(),
org.jetbrains.kotlin.serialization.ProtoBuf.Property.getDefaultInstance(),
null,
102,
org.jetbrains.kotlin.protobuf.WireFormat.FieldType.MESSAGE,
false,
org.jetbrains.kotlin.serialization.ProtoBuf.Property.class);
static {
}
@@ -21,6 +21,7 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.types.TypeSubstitution;
import java.util.Collection;
import java.util.List;
public interface CallableMemberDescriptor extends CallableDescriptor, MemberDescriptor {
@NotNull
@@ -70,6 +71,9 @@ public interface CallableMemberDescriptor extends CallableDescriptor, MemberDesc
@NotNull
CopyBuilder<D> setKind(@NotNull Kind kind);
@NotNull
CopyBuilder<D> setTypeParameters(@NotNull List<TypeParameterDescriptor> parameters);
@NotNull
CopyBuilder<D> setDispatchReceiverParameter(@Nullable ReceiverParameterDescriptor dispatchReceiverParameter);
@@ -117,6 +117,7 @@ public interface FunctionDescriptor extends CallableMemberDescriptor {
CopyBuilder<D> setValueParameters(@NotNull List<ValueParameterDescriptor> parameters);
@NotNull
@Override
CopyBuilder<D> setTypeParameters(@NotNull List<TypeParameterDescriptor> parameters);
@NotNull
@@ -237,24 +237,20 @@ public class PropertyDescriptorImpl extends VariableDescriptorWithInitializerImp
return newCopyBuilder()
.setSubstitution(originalSubstitutor.getSubstitution())
.setOwner(getContainingDeclaration())
.setModality(modality)
.setVisibility(visibility)
.setOriginal(getOriginal())
.setCopyOverrides(true)
.setKind(getKind())
.build();
}
public class CopyConfiguration implements PropertyDescriptor.CopyBuilder<PropertyDescriptor> {
private DeclarationDescriptor owner;
private Modality modality;
private Visibility visibility;
private DeclarationDescriptor owner = getContainingDeclaration();
private Modality modality = getModality();
private Visibility visibility = getVisibility();
private PropertyDescriptor original = null;
private Kind kind;
private Kind kind = getKind();
private TypeSubstitution substitution = TypeSubstitution.EMPTY;
private boolean copyOverrides = true;
private ReceiverParameterDescriptor dispatchReceiverParameter = PropertyDescriptorImpl.this.dispatchReceiverParameter;
private List<TypeParameterDescriptor> newTypeParameters = null;
@NotNull
@Override
@@ -290,6 +286,13 @@ public class PropertyDescriptorImpl extends VariableDescriptorWithInitializerImp
return this;
}
@NotNull
@Override
public CopyBuilder<PropertyDescriptor> setTypeParameters(@NotNull List<TypeParameterDescriptor> typeParameters) {
this.newTypeParameters = typeParameters;
return this;
}
@NotNull
@Override
public CopyConfiguration setDispatchReceiverParameter(@Nullable ReceiverParameterDescriptor dispatchReceiverParameter) {
@@ -330,7 +333,8 @@ public class PropertyDescriptorImpl extends VariableDescriptorWithInitializerImp
copyConfiguration.owner, copyConfiguration.modality, copyConfiguration.visibility,
copyConfiguration.original, copyConfiguration.kind);
List<TypeParameterDescriptor> originalTypeParameters = getTypeParameters();
List<TypeParameterDescriptor> originalTypeParameters =
copyConfiguration.newTypeParameters == null ? getTypeParameters() : copyConfiguration.newTypeParameters;
List<TypeParameterDescriptor> substitutedTypeParameters = new ArrayList<TypeParameterDescriptor>(originalTypeParameters.size());
TypeSubstitutor substitutor = DescriptorSubstitutor.substituteTypeParameters(
originalTypeParameters, copyConfiguration.substitution, substitutedDescriptor, substitutedTypeParameters
@@ -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)
}