Generate separate anonymous class for each property reference

Each property reference obtained by the '::' operator now causes back-end to
generate an anonymous subclass of the corresponding KProperty class, with the
customized behavior. This fixes a number of issues:

- get/set/name of property references now works without kotlin-reflect.jar in
  the classpath
- get/set/name methods are now overridden with statically-generated property
  access instead of the default KPropertyImpl's behavior of using Java
  reflection, which should be a lot faster
- references to private/protected properties now work without the need to set
  'accessible' flag, because corresponding synthetic accessors are generated at
  compile-time near the target property

 #KT-6870 Fixed
 #KT-6873 Fixed
 #KT-7033 Fixed
This commit is contained in:
Alexander Udalov
2015-06-30 16:07:51 +03:00
parent 30794060a9
commit 048a9b686e
40 changed files with 833 additions and 304 deletions
@@ -30,17 +30,17 @@ import java.lang.reflect.Method
abstract class DescriptorBasedProperty protected constructor(
container: KCallableContainerImpl,
name: String,
receiverParameterDesc: String?,
signature: String,
descriptorInitialValue: PropertyDescriptor?
) {
constructor(container: KCallableContainerImpl, name: String, receiverParameterClass: Class<*>?) : this(
container, name, receiverParameterClass?.desc, null
constructor(container: KCallableContainerImpl, name: String, signature: String) : this(
container, name, signature, null
)
constructor(container: KCallableContainerImpl, descriptor: PropertyDescriptor) : this(
container,
descriptor.getName().asString(),
descriptor.getExtensionReceiverParameter()?.getType()?.let { type -> RuntimeTypeMapper.mapTypeToJvmDesc(type) },
RuntimeTypeMapper.mapPropertySignature(descriptor),
descriptor
)
@@ -51,7 +51,7 @@ abstract class DescriptorBasedProperty protected constructor(
)
protected val descriptor: PropertyDescriptor by ReflectProperties.lazySoft<PropertyDescriptor>(descriptorInitialValue) {
container.findPropertyDescriptor(name, receiverParameterDesc)
container.findPropertyDescriptor(name, signature)
}
// null if this is a property declared in a foreign (Java) class
@@ -43,20 +43,16 @@ abstract class KCallableContainerImpl : KDeclarationContainer {
abstract val scope: JetScope
fun findPropertyDescriptor(name: String, receiverDesc: String? = null): PropertyDescriptor {
fun findPropertyDescriptor(name: String, signature: String): PropertyDescriptor {
val properties = scope
.getProperties(Name.guess(name))
.filter { descriptor ->
descriptor is PropertyDescriptor &&
descriptor.getName().asString() == name &&
with(descriptor.getExtensionReceiverParameter()) {
(this == null && receiverDesc == null) ||
(this != null && RuntimeTypeMapper.mapTypeToJvmDesc(getType()) == receiverDesc)
}
RuntimeTypeMapper.mapPropertySignature(descriptor) == signature
}
if (properties.size() != 1) {
val debugText = if (receiverDesc == null) name else "'$receiverDesc.$name'"
val debugText = "'$name' (JVM signature: $signature)"
throw KotlinReflectionInternalError(
if (properties.isEmpty()) "Property $debugText not resolved in $this"
else "${properties.size()} properties $debugText resolved in $this: $properties"
@@ -104,12 +104,6 @@ class KClassImpl<T>(override val jClass: Class<T>) : KCallableContainerImpl(), K
.map(create)
.toList()
fun memberProperty(name: String): KProperty1<T, *> =
KProperty1Impl<T, Any>(this, name, null)
fun mutableMemberProperty(name: String): KMutableProperty1<T, *> =
KMutableProperty1Impl<T, Any>(this, name, null)
override fun equals(other: Any?): Boolean =
other is KClassImpl<*> && jClass == other.jClass
@@ -31,18 +31,6 @@ class KPackageImpl(override val jClass: Class<*>) : KCallableContainerImpl(), KP
override val scope: JetScope get() = descriptor.memberScope
fun topLevelVariable(name: String): KProperty0<*> =
KProperty0Impl<Any?>(this, name)
fun mutableTopLevelVariable(name: String): KMutableProperty0<*> =
KMutableProperty0Impl<Any?>(this, name)
fun <T> topLevelExtensionProperty(name: String, receiver: Class<T>): KProperty1<T, *> =
KProperty1Impl<T, Any?>(this, name, receiver)
fun <T> mutableTopLevelExtensionProperty(name: String, receiver: Class<T>): KMutableProperty1<T, *> =
KMutableProperty1Impl<T, Any?>(this, name, receiver)
override fun equals(other: Any?): Boolean =
other is KPackageImpl && jClass == other.jClass
@@ -17,12 +17,14 @@
package kotlin.reflect.jvm.internal
import java.lang.reflect.Method
import kotlin.jvm.internal.MutablePropertyReference0
import kotlin.jvm.internal.PropertyReference0
import kotlin.reflect.IllegalPropertyAccessException
import kotlin.reflect.KMutableProperty0
import kotlin.reflect.KProperty0
open class KProperty0Impl<out R> : DescriptorBasedProperty, KProperty0<R>, KPropertyImpl<R> {
constructor(container: KPackageImpl, name: String) : super(container, name, null)
constructor(container: KCallableContainerImpl, name: String, signature: String) : super(container, name, signature)
override val name: String get() = descriptor.getName().asString()
@@ -39,8 +41,8 @@ open class KProperty0Impl<out R> : DescriptorBasedProperty, KProperty0<R>, KProp
}
}
class KMutableProperty0Impl<R> : KProperty0Impl<R>, KMutableProperty0<R>, KMutablePropertyImpl<R> {
constructor(container: KPackageImpl, name: String) : super(container, name)
open class KMutableProperty0Impl<R> : KProperty0Impl<R>, KMutableProperty0<R>, KMutablePropertyImpl<R> {
constructor(container: KCallableContainerImpl, name: String, signature: String) : super(container, name, signature)
override val setter: Method get() = super<KProperty0Impl>.setter!!
@@ -53,3 +55,33 @@ class KMutableProperty0Impl<R> : KProperty0Impl<R>, KMutableProperty0<R>, KMutab
}
}
}
class KProperty0FromReferenceImpl(
val reference: PropertyReference0
) : KProperty0Impl<Any?>(
reference.getOwner() as KCallableContainerImpl,
reference.getName(),
reference.getSignature()
) {
override val name: String get() = reference.getName()
override fun get(): Any? = reference.get()
}
class KMutableProperty0FromReferenceImpl(
val reference: MutablePropertyReference0
) : KMutableProperty0Impl<Any?>(
reference.getOwner() as KCallableContainerImpl,
reference.getName(),
reference.getSignature()
) {
override val name: String get() = reference.getName()
override fun get(): Any? = reference.get()
override fun set(value: Any?) {
reference.set(value)
}
}
@@ -21,11 +21,11 @@ import java.lang.reflect.Modifier
import kotlin.reflect.IllegalPropertyAccessException
import kotlin.reflect.KMutableProperty1
import kotlin.reflect.KProperty1
import kotlin.jvm.internal.MutablePropertyReference1
import kotlin.jvm.internal.PropertyReference1
open class KProperty1Impl<T, out R> : DescriptorBasedProperty, KProperty1<T, R>, KPropertyImpl<R> {
constructor(container: KCallableContainerImpl, name: String, receiverParameterClass: Class<*>?) : super(
container, name, receiverParameterClass
)
constructor(container: KCallableContainerImpl, name: String, signature: String) : super(container, name, signature)
constructor(container: KCallableContainerImpl, descriptor: PropertyDescriptor) : super(container, descriptor)
@@ -56,10 +56,8 @@ open class KProperty1Impl<T, out R> : DescriptorBasedProperty, KProperty1<T, R>,
}
class KMutableProperty1Impl<T, R> : KProperty1Impl<T, R>, KMutableProperty1<T, R>, KMutablePropertyImpl<R> {
constructor(container: KCallableContainerImpl, name: String, receiverParameterClass: Class<*>?) : super(
container, name, receiverParameterClass
)
open class KMutableProperty1Impl<T, R> : KProperty1Impl<T, R>, KMutableProperty1<T, R>, KMutablePropertyImpl<R> {
constructor(container: KCallableContainerImpl, name: String, signature: String) : super(container, name, signature)
constructor(container: KCallableContainerImpl, descriptor: PropertyDescriptor) : super(container, descriptor)
@@ -86,3 +84,33 @@ class KMutableProperty1Impl<T, R> : KProperty1Impl<T, R>, KMutableProperty1<T, R
}
}
}
class KProperty1FromReferenceImpl(
val reference: PropertyReference1
) : KProperty1Impl<Any?, Any?>(
reference.getOwner() as KCallableContainerImpl,
reference.getName(),
reference.getSignature()
) {
override val name: String get() = reference.getName()
override fun get(receiver: Any?): Any? = reference.get(receiver)
}
class KMutableProperty1FromReferenceImpl(
val reference: MutablePropertyReference1
) : KMutableProperty1Impl<Any?, Any?>(
reference.getOwner() as KCallableContainerImpl,
reference.getName(),
reference.getSignature()
) {
override val name: String get() = reference.getName()
override fun get(receiver: Any?): Any? = reference.get(receiver)
override fun set(receiver: Any?, value: Any?) {
reference.set(receiver, value)
}
}
@@ -24,7 +24,7 @@ import kotlin.reflect.KMutableProperty2
import kotlin.reflect.KProperty2
open class KProperty2Impl<D, E, out R> : DescriptorBasedProperty, KProperty2<D, E, R>, KPropertyImpl<R> {
constructor(container: KClassImpl<D>, name: String, receiverParameterClass: Class<E>) : super(container, name, receiverParameterClass)
constructor(container: KClassImpl<D>, name: String, signature: String) : super(container, name, signature)
constructor(container: KClassImpl<D>, descriptor: PropertyDescriptor) : super(container, descriptor)
@@ -47,7 +47,7 @@ open class KProperty2Impl<D, E, out R> : DescriptorBasedProperty, KProperty2<D,
class KMutableProperty2Impl<D, E, R> : KProperty2Impl<D, E, R>, KMutableProperty2<D, E, R>, KMutablePropertyImpl<R> {
constructor(container: KClassImpl<D>, name: String, receiverParameterClass: Class<E>) : super(container, name, receiverParameterClass)
constructor(container: KClassImpl<D>, name: String, signature: String) : super(container, name, signature)
constructor(container: KClassImpl<D>, descriptor: PropertyDescriptor) : super(container, descriptor)
@@ -16,8 +16,7 @@
package kotlin.reflect.jvm.internal;
import kotlin.jvm.internal.FunctionReference;
import kotlin.jvm.internal.ReflectionFactory;
import kotlin.jvm.internal.*;
import kotlin.reflect.*;
/**
@@ -50,32 +49,34 @@ public class ReflectionFactoryImpl extends ReflectionFactory {
// Properties
@Override
public KProperty1 memberProperty(String name, KClass owner) {
return ((KClassImpl) owner).memberProperty(name);
public KProperty0 property0(PropertyReference0 p) {
return new KProperty0FromReferenceImpl(p);
}
@Override
public KMutableProperty1 mutableMemberProperty(String name, KClass owner) {
return ((KClassImpl) owner).mutableMemberProperty(name);
public KMutableProperty0 mutableProperty0(MutablePropertyReference0 p) {
return new KMutableProperty0FromReferenceImpl(p);
}
@Override
public KProperty0 topLevelVariable(String name, KPackage owner) {
return ((KPackageImpl) owner).topLevelVariable(name);
public KProperty1 property1(PropertyReference1 p) {
return new KProperty1FromReferenceImpl(p);
}
@Override
public KMutableProperty0 mutableTopLevelVariable(String name, KPackage owner) {
return ((KPackageImpl) owner).mutableTopLevelVariable(name);
public KMutableProperty1 mutableProperty1(MutablePropertyReference1 p) {
return new KMutableProperty1FromReferenceImpl(p);
}
@Override
public KProperty1 topLevelExtensionProperty(String name, KPackage owner, Class receiver) {
return ((KPackageImpl) owner).topLevelExtensionProperty(name, receiver);
public KProperty2 property2(PropertyReference2 p) {
// TODO: support member extension property references
return p;
}
@Override
public KMutableProperty1 mutableTopLevelExtensionProperty(String name, KPackage owner, Class receiver) {
return ((KPackageImpl) owner).mutableTopLevelExtensionProperty(name, receiver);
public KMutableProperty2 mutableProperty2(MutablePropertyReference2 p) {
// TODO: support member extension property references
return p;
}
}
@@ -18,58 +18,25 @@ package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
import org.jetbrains.kotlin.load.java.descriptors.JavaPropertyDescriptor
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
import org.jetbrains.kotlin.load.java.structure.*
import org.jetbrains.kotlin.load.java.structure.reflect.*
import org.jetbrains.kotlin.load.kotlin.SignatureDeserializer
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedSimpleFunctionDescriptor
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeUtils
import kotlin.reflect.KotlinReflectionInternalError
object RuntimeTypeMapper {
// TODO: this logic must be shared with JetTypeMapper
fun mapTypeToJvmDesc(type: JetType): String {
val classifier = type.getConstructor().getDeclarationDescriptor()
if (classifier is TypeParameterDescriptor) {
return mapTypeToJvmDesc(classifier.getUpperBounds().first())
}
if (KotlinBuiltIns.isArray(type)) {
val elementType = KotlinBuiltIns.getInstance().getArrayElementType(type)
// makeNullable is called here to map primitive types to the corresponding wrappers,
// because the given type is Array<Something>, not SomethingArray
return "[" + mapTypeToJvmDesc(TypeUtils.makeNullable(elementType))
}
val classDescriptor = classifier as ClassDescriptor
val fqName = DescriptorUtils.getFqName(classDescriptor)
KotlinBuiltIns.getPrimitiveTypeByFqName(fqName)?.let { primitiveType ->
val jvmType = JvmPrimitiveType.get(primitiveType)
return if (TypeUtils.isNullableType(type)) ClassId.topLevel(jvmType.getWrapperFqName()).desc else jvmType.getDesc()
}
KotlinBuiltIns.getPrimitiveTypeByArrayClassFqName(fqName)?.let { primitiveType ->
return "[" + JvmPrimitiveType.get(primitiveType).getDesc()
}
JavaToKotlinClassMap.INSTANCE.mapKotlinToJava(fqName)?.let { return it.desc }
return classDescriptor.classId.desc
}
fun mapSignature(function: FunctionDescriptor): String {
if (function is DeserializedSimpleFunctionDescriptor) {
val proto = function.getProto()
@@ -125,6 +92,50 @@ object RuntimeTypeMapper {
}
}
fun mapPropertySignature(property: PropertyDescriptor): String {
if (property is DeserializedPropertyDescriptor) {
val proto = property.proto
val nameResolver = property.nameResolver
if (!proto.hasExtension(JvmProtoBuf.propertySignature)) {
throw KotlinReflectionInternalError("No metadata found for $property")
}
val signature = proto.getExtension(JvmProtoBuf.propertySignature)
val deserializer = SignatureDeserializer(nameResolver)
if (signature.hasGetter()) {
return deserializer.methodSignatureString(signature.getGetter())
}
// In case the property doesn't have a getter, construct the signature of its imaginary default getter.
// See PropertyReference#getSignature
val field = signature.getField()
// TODO: some kind of test on the Java Bean convention?
return getterName(nameResolver.getString(field.getName())) +
"()" +
deserializer.typeDescriptor(field.getType())
}
else if (property is JavaPropertyDescriptor) {
val method = (property.getSource() as? JavaSourceElement)?.javaElement as? JavaField ?:
throw KotlinReflectionInternalError("Incorrect resolution sequence for Java field $property")
return StringBuilder {
append(getterName(method.getName().asString()))
append("()")
appendJavaType(method.getType())
}.toString()
}
else throw KotlinReflectionInternalError("Unknown origin of $property (${property.javaClass})")
}
private fun getterName(propertyName: String): String {
return JvmAbi.GETTER_PREFIX + propertyName.capitalizeWithJavaBeanConvention()
}
private fun String.capitalizeWithJavaBeanConvention(): String {
return if (length() > 1 && this[1].isUpperCase()) this else capitalize()
}
fun mapJvmClassToKotlinClassId(klass: Class<*>): ClassId {
if (klass.isArray()) {
klass.getComponentType().primitiveType?.let {
@@ -147,7 +158,4 @@ object RuntimeTypeMapper {
private val Class<*>.primitiveType: PrimitiveType?
get() = if (isPrimitive()) JvmPrimitiveType.get(getSimpleName()).getPrimitiveType() else null
private val ClassId.desc: String
get() = "L${JvmClassName.byClassId(this).getInternalName()};"
}
@@ -95,17 +95,10 @@ public val Field.kotlin: KProperty<*>?
get() {
if (isSynthetic()) return null
val clazz = getDeclaringClass()
val name = getName()
val modifiers = getModifiers()
val static = Modifier.isStatic(modifiers)
val final = Modifier.isFinal(modifiers)
if (static) {
val kPackage = clazz.kotlinPackage
return if (final) Reflection.topLevelVariable(name, kPackage) else Reflection.mutableTopLevelVariable(name, kPackage)
}
else {
val kClass = clazz.kotlin
return if (final) Reflection.memberProperty(name, kClass) else Reflection.mutableMemberProperty(name, kClass)
val clazz = getDeclaringClass().kotlin as KClassImpl
// TODO: optimize (search by name)
return clazz.properties.firstOrNull { p: KProperty<*> ->
(p as KPropertyImpl<*>).field == this
}
}