Reflection: refactor property construction and fix memory leak

Properties obtained by KClass.properties were having strong references to
descriptors (captured by closures, which are strongly retained by
DescriptorBasedProperty). Support initial value in lazy soft properties
This commit is contained in:
Alexander Udalov
2015-04-01 21:20:35 +03:00
parent 715641fb0d
commit 9c1e7f93d2
9 changed files with 85 additions and 64 deletions
@@ -18,6 +18,7 @@ package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.load.java.structure.reflect.desc
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.serialization.ProtoBuf import org.jetbrains.kotlin.serialization.ProtoBuf
import org.jetbrains.kotlin.serialization.deserialization.NameResolver import org.jetbrains.kotlin.serialization.deserialization.NameResolver
@@ -26,10 +27,22 @@ import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf
import java.lang.reflect.Field import java.lang.reflect.Field
import java.lang.reflect.Method import java.lang.reflect.Method
abstract class DescriptorBasedProperty(computeDescriptor: () -> PropertyDescriptor) { abstract class DescriptorBasedProperty private(
protected abstract val container: KCallableContainerImpl container: KCallableContainerImpl,
name: String,
receiverParameterDesc: String?,
descriptorInitialValue: PropertyDescriptor?
) {
constructor(container: KCallableContainerImpl, name: String, receiverParameterClass: Class<*>?) : this(
container, name, receiverParameterClass?.desc, null
)
protected abstract val name: String constructor(container: KCallableContainerImpl, descriptor: PropertyDescriptor) : this(
container,
descriptor.getName().asString(),
descriptor.getExtensionReceiverParameter()?.getType()?.let { type -> RuntimeTypeMapper.mapTypeToJvmDesc(type) },
descriptor
)
private data class PropertyProtoData( private data class PropertyProtoData(
val proto: ProtoBuf.Callable, val proto: ProtoBuf.Callable,
@@ -37,7 +50,9 @@ abstract class DescriptorBasedProperty(computeDescriptor: () -> PropertyDescript
val signature: JvmProtoBuf.JvmPropertySignature val signature: JvmProtoBuf.JvmPropertySignature
) )
protected val descriptor: PropertyDescriptor by ReflectProperties.lazySoft(computeDescriptor) protected val descriptor: PropertyDescriptor by ReflectProperties.lazySoft<PropertyDescriptor>(descriptorInitialValue) {
container.findPropertyDescriptor(name, receiverParameterDesc)
}
// null if this is a property declared in a foreign (Java) class // null if this is a property declared in a foreign (Java) class
private val protoData: PropertyProtoData? by ReflectProperties.lazyWeak { private val protoData: PropertyProtoData? by ReflectProperties.lazyWeak {
@@ -20,7 +20,6 @@ import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.load.java.structure.reflect.classId import org.jetbrains.kotlin.load.java.structure.reflect.classId
import org.jetbrains.kotlin.load.java.structure.reflect.classLoader import org.jetbrains.kotlin.load.java.structure.reflect.classLoader
import org.jetbrains.kotlin.load.java.structure.reflect.createArrayType import org.jetbrains.kotlin.load.java.structure.reflect.createArrayType
import org.jetbrains.kotlin.load.java.structure.reflect.desc
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.scopes.JetScope import org.jetbrains.kotlin.resolve.scopes.JetScope
@@ -42,9 +41,7 @@ abstract class KCallableContainerImpl {
abstract val scope: JetScope abstract val scope: JetScope
protected fun findPropertyDescriptor(name: String, receiverParameterClass: Class<*>? = null): () -> PropertyDescriptor = { fun findPropertyDescriptor(name: String, receiverDesc: String? = null): PropertyDescriptor {
val receiverDesc = receiverParameterClass?.desc
val properties = scope val properties = scope
.getProperties(Name.identifier(name)) .getProperties(Name.identifier(name))
.filter { descriptor -> .filter { descriptor ->
@@ -57,14 +54,14 @@ abstract class KCallableContainerImpl {
} }
if (properties.size() != 1) { if (properties.size() != 1) {
val debugText = if (receiverParameterClass == null) name else "${receiverParameterClass.getSimpleName()}.$name" val debugText = if (receiverDesc == null) name else "$receiverDesc.$name"
throw KotlinReflectionInternalError( throw KotlinReflectionInternalError(
if (properties.isEmpty()) "Property '$debugText' not resolved in $this" if (properties.isEmpty()) "Property '$debugText' not resolved in $this"
else "${properties.size()} properties '$debugText' resolved in $this" else "${properties.size()} properties '$debugText' resolved in $this"
) )
} }
properties.single() as PropertyDescriptor return properties.single() as PropertyDescriptor
} }
// TODO: check resulting method's return type // TODO: check resulting method's return type
@@ -69,14 +69,14 @@ class KClassImpl<T>(override val jClass: Class<T>) : KCallableContainerImpl(), K
fun getProperties(declared: Boolean): Collection<KMemberProperty<T, *>> = fun getProperties(declared: Boolean): Collection<KMemberProperty<T, *>> =
getProperties(extension = false, declared = declared) { descriptor -> getProperties(extension = false, declared = declared) { descriptor ->
if (descriptor.isVar()) KMutableMemberPropertyImpl<T, Any?>(this) { descriptor } if (descriptor.isVar()) KMutableMemberPropertyImpl<T, Any?>(this, descriptor)
else KMemberPropertyImpl<T, Any?>(this) { descriptor } else KMemberPropertyImpl<T, Any?>(this, descriptor)
} }
fun getExtensionProperties(declared: Boolean): Collection<KMemberExtensionProperty<T, *, *>> = fun getExtensionProperties(declared: Boolean): Collection<KMemberExtensionProperty<T, *, *>> =
getProperties(extension = true, declared = declared) { descriptor -> getProperties(extension = true, declared = declared) { descriptor ->
if (descriptor.isVar()) KMutableMemberExtensionPropertyImpl<T, Any?, Any?>(this) { descriptor } if (descriptor.isVar()) KMutableMemberExtensionPropertyImpl<T, Any?, Any?>(this, descriptor)
else KMemberExtensionPropertyImpl<T, Any?, Any?>(this) { descriptor } else KMemberExtensionPropertyImpl<T, Any?, Any?>(this, descriptor)
} }
private fun <P : KProperty<*>> getProperties(extension: Boolean, declared: Boolean, create: (PropertyDescriptor) -> P): Collection<P> = private fun <P : KProperty<*>> getProperties(extension: Boolean, declared: Boolean, create: (PropertyDescriptor) -> P): Collection<P> =
@@ -91,10 +91,10 @@ class KClassImpl<T>(override val jClass: Class<T>) : KCallableContainerImpl(), K
.toList() .toList()
fun memberProperty(name: String): KMemberProperty<T, *> = fun memberProperty(name: String): KMemberProperty<T, *> =
KMemberPropertyImpl<T, Any>(this, findPropertyDescriptor(name)) KMemberPropertyImpl<T, Any>(this, name)
fun mutableMemberProperty(name: String): KMutableMemberProperty<T, *> = fun mutableMemberProperty(name: String): KMutableMemberProperty<T, *> =
KMutableMemberPropertyImpl<T, Any>(this, findPropertyDescriptor(name)) KMutableMemberPropertyImpl<T, Any>(this, name)
override fun equals(other: Any?): Boolean = override fun equals(other: Any?): Boolean =
other is KClassImpl<*> && jClass == other.jClass other is KClassImpl<*> && jClass == other.jClass
@@ -23,10 +23,11 @@ import kotlin.reflect.IllegalPropertyAccessException
import kotlin.reflect.KMemberExtensionProperty import kotlin.reflect.KMemberExtensionProperty
import kotlin.reflect.KMutableMemberExtensionProperty import kotlin.reflect.KMutableMemberExtensionProperty
open class KMemberExtensionPropertyImpl<D : Any, E, out R>( open class KMemberExtensionPropertyImpl<D : Any, E, out R> : DescriptorBasedProperty, KMemberExtensionProperty<D, E, R>, KPropertyImpl<R> {
override val container: KClassImpl<D>, constructor(container: KClassImpl<D>, name: String, receiverParameterClass: Class<E>) : super(container, name, receiverParameterClass)
computeDescriptor: () -> PropertyDescriptor
) : DescriptorBasedProperty(computeDescriptor), KMemberExtensionProperty<D, E, R>, KPropertyImpl<R> { constructor(container: KClassImpl<D>, descriptor: PropertyDescriptor) : super(container, descriptor)
override val name: String get() = descriptor.getName().asString() override val name: String get() = descriptor.getName().asString()
override val getter: Method get() = super<DescriptorBasedProperty>.getter!! override val getter: Method get() = super<DescriptorBasedProperty>.getter!!
@@ -45,10 +46,14 @@ open class KMemberExtensionPropertyImpl<D : Any, E, out R>(
} }
class KMutableMemberExtensionPropertyImpl<D : Any, E, R>( class KMutableMemberExtensionPropertyImpl<D : Any, E, R> :
container: KClassImpl<D>, KMemberExtensionPropertyImpl<D, E, R>,
computeDescriptor: () -> PropertyDescriptor KMutableMemberExtensionProperty<D, E, R>,
) : KMemberExtensionPropertyImpl<D, E, R>(container, computeDescriptor), KMutableMemberExtensionProperty<D, E, R>, KMutablePropertyImpl<R> { KMutablePropertyImpl<R> {
constructor(container: KClassImpl<D>, name: String, receiverParameterClass: Class<E>) : super(container, name, receiverParameterClass)
constructor(container: KClassImpl<D>, descriptor: PropertyDescriptor) : super(container, descriptor)
override val setter: Method get() = super<KMemberExtensionPropertyImpl>.setter!! override val setter: Method get() = super<KMemberExtensionPropertyImpl>.setter!!
override fun set(instance: D, extensionReceiver: E, value: R) { override fun set(instance: D, extensionReceiver: E, value: R) {
@@ -21,10 +21,11 @@ import kotlin.reflect.IllegalPropertyAccessException
import kotlin.reflect.KMemberProperty import kotlin.reflect.KMemberProperty
import kotlin.reflect.KMutableMemberProperty import kotlin.reflect.KMutableMemberProperty
open class KMemberPropertyImpl<T : Any, out R>( open class KMemberPropertyImpl<T : Any, out R> : DescriptorBasedProperty, KMemberProperty<T, R>, KPropertyImpl<R> {
override val container: KClassImpl<T>, constructor(container: KClassImpl<T>, name: String) : super(container, name, null)
computeDescriptor: () -> PropertyDescriptor
) : DescriptorBasedProperty(computeDescriptor), KMemberProperty<T, R>, KPropertyImpl<R> { constructor(container: KClassImpl<T>, descriptor: PropertyDescriptor) : super(container, descriptor)
override val name: String get() = descriptor.getName().asString() override val name: String get() = descriptor.getName().asString()
override fun get(instance: T): R { override fun get(instance: T): R {
@@ -40,10 +41,11 @@ open class KMemberPropertyImpl<T : Any, out R>(
} }
class KMutableMemberPropertyImpl<T : Any, R>( class KMutableMemberPropertyImpl<T : Any, R> : KMemberPropertyImpl<T, R>, KMutableMemberProperty<T, R>, KMutablePropertyImpl<R> {
container: KClassImpl<T>, constructor(container: KClassImpl<T>, name: String) : super(container, name)
computeDescriptor: () -> PropertyDescriptor
) : KMemberPropertyImpl<T, R>(container, computeDescriptor), KMutableMemberProperty<T, R>, KMutablePropertyImpl<R> { constructor(container: KClassImpl<T>, descriptor: PropertyDescriptor) : super(container, descriptor)
override fun set(instance: T, value: R) { override fun set(instance: T, value: R) {
try { try {
val setter = setter val setter = setter
@@ -31,21 +31,17 @@ class KPackageImpl(override val jClass: Class<*>) : KCallableContainerImpl(), KP
override val scope: JetScope get() = descriptor.getMemberScope() override val scope: JetScope get() = descriptor.getMemberScope()
fun topLevelVariable(name: String): KTopLevelVariable<*> { fun topLevelVariable(name: String): KTopLevelVariable<*> =
return KTopLevelVariableImpl<Any?>(this, findPropertyDescriptor(name)) KTopLevelVariableImpl<Any?>(this, name)
}
fun mutableTopLevelVariable(name: String): KMutableTopLevelVariable<*> { fun mutableTopLevelVariable(name: String): KMutableTopLevelVariable<*> =
return KMutableTopLevelVariableImpl<Any?>(this, findPropertyDescriptor(name)) KMutableTopLevelVariableImpl<Any?>(this, name)
}
fun <T> topLevelExtensionProperty(name: String, receiver: Class<T>): KTopLevelExtensionProperty<T, *> { fun <T> topLevelExtensionProperty(name: String, receiver: Class<T>): KTopLevelExtensionProperty<T, *> =
return KTopLevelExtensionPropertyImpl<T, Any?>(this, findPropertyDescriptor(name, receiver)) KTopLevelExtensionPropertyImpl<T, Any?>(this, name, receiver)
}
fun <T> mutableTopLevelExtensionProperty(name: String, receiver: Class<T>): KMutableTopLevelExtensionProperty<T, *> { fun <T> mutableTopLevelExtensionProperty(name: String, receiver: Class<T>): KMutableTopLevelExtensionProperty<T, *> =
return KMutableTopLevelExtensionPropertyImpl<T, Any?>(this, findPropertyDescriptor(name, receiver)) KMutableTopLevelExtensionPropertyImpl<T, Any?>(this, name, receiver)
}
override fun equals(other: Any?): Boolean = override fun equals(other: Any?): Boolean =
other is KPackageImpl && jClass == other.jClass other is KPackageImpl && jClass == other.jClass
@@ -16,16 +16,14 @@
package kotlin.reflect.jvm.internal package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import java.lang.reflect.Method import java.lang.reflect.Method
import kotlin.reflect.IllegalPropertyAccessException import kotlin.reflect.IllegalPropertyAccessException
import kotlin.reflect.KMutableTopLevelExtensionProperty import kotlin.reflect.KMutableTopLevelExtensionProperty
import kotlin.reflect.KTopLevelExtensionProperty import kotlin.reflect.KTopLevelExtensionProperty
open class KTopLevelExtensionPropertyImpl<T, out R>( open class KTopLevelExtensionPropertyImpl<T, out R> : DescriptorBasedProperty, KTopLevelExtensionProperty<T, R>, KPropertyImpl<R> {
override val container: KPackageImpl, constructor(container: KPackageImpl, name: String, receiverParameterClass: Class<T>) : super(container, name, receiverParameterClass)
computeDescriptor: () -> PropertyDescriptor
) : DescriptorBasedProperty(computeDescriptor), KTopLevelExtensionProperty<T, R>, KPropertyImpl<R> {
override val name: String get() = descriptor.getName().asString() override val name: String get() = descriptor.getName().asString()
override val getter: Method get() = super<DescriptorBasedProperty>.getter!! override val getter: Method get() = super<DescriptorBasedProperty>.getter!!
@@ -41,10 +39,12 @@ open class KTopLevelExtensionPropertyImpl<T, out R>(
} }
} }
class KMutableTopLevelExtensionPropertyImpl<T, R>( class KMutableTopLevelExtensionPropertyImpl<T, R> :
container: KPackageImpl, KTopLevelExtensionPropertyImpl<T, R>,
computeDescriptor: () -> PropertyDescriptor KMutableTopLevelExtensionProperty<T, R>,
) : KTopLevelExtensionPropertyImpl<T, R>(container, computeDescriptor), KMutableTopLevelExtensionProperty<T, R>, KMutablePropertyImpl<R> { KMutablePropertyImpl<R> {
constructor(container: KPackageImpl, name: String, receiverParameterClass: Class<T>) : super(container, name, receiverParameterClass)
override val setter: Method get() = super<KTopLevelExtensionPropertyImpl>.setter!! override val setter: Method get() = super<KTopLevelExtensionPropertyImpl>.setter!!
override fun set(receiver: T, value: R) { override fun set(receiver: T, value: R) {
@@ -16,16 +16,14 @@
package kotlin.reflect.jvm.internal package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import java.lang.reflect.Method import java.lang.reflect.Method
import kotlin.reflect.IllegalPropertyAccessException import kotlin.reflect.IllegalPropertyAccessException
import kotlin.reflect.KMutableTopLevelVariable import kotlin.reflect.KMutableTopLevelVariable
import kotlin.reflect.KTopLevelVariable import kotlin.reflect.KTopLevelVariable
open class KTopLevelVariableImpl<out R>( open class KTopLevelVariableImpl<out R> : DescriptorBasedProperty, KTopLevelVariable<R>, KVariableImpl<R> {
override val container: KPackageImpl, constructor(container: KPackageImpl, name: String) : super(container, name, null)
computeDescriptor: () -> PropertyDescriptor
) : DescriptorBasedProperty(computeDescriptor), KTopLevelVariable<R>, KVariableImpl<R> {
override val name: String get() = descriptor.getName().asString() override val name: String get() = descriptor.getName().asString()
override val getter: Method get() = super<DescriptorBasedProperty>.getter!! override val getter: Method get() = super<DescriptorBasedProperty>.getter!!
@@ -41,10 +39,9 @@ open class KTopLevelVariableImpl<out R>(
} }
} }
class KMutableTopLevelVariableImpl<R>( class KMutableTopLevelVariableImpl<R> : KTopLevelVariableImpl<R>, KMutableTopLevelVariable<R>, KMutableVariableImpl<R> {
container: KPackageImpl, constructor(container: KPackageImpl, name: String) : super(container, name)
computeDescriptor: () -> PropertyDescriptor
) : KTopLevelVariableImpl<R>(container, computeDescriptor), KMutableTopLevelVariable<R>, KMutableVariableImpl<R> {
override val setter: Method get() = super<KTopLevelVariableImpl>.setter!! override val setter: Method get() = super<KTopLevelVariableImpl>.setter!!
override fun set(value: R) { override fun set(value: R) {
@@ -18,6 +18,7 @@ package kotlin.reflect.jvm.internal;
import kotlin.Function0; import kotlin.Function0;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.SoftReference; import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@@ -67,8 +68,11 @@ import java.lang.ref.WeakReference;
private final Function0<T> initializer; private final Function0<T> initializer;
private SoftReference<Object> value = null; private SoftReference<Object> value = null;
public LazySoftVal(@NotNull Function0<T> initializer) { public LazySoftVal(@Nullable T initialValue, @NotNull Function0<T> initializer) {
this.initializer = initializer; this.initializer = initializer;
if (initialValue != null) {
this.value = new SoftReference<Object>(escape(initialValue));
}
} }
@Override @Override
@@ -120,9 +124,14 @@ import java.lang.ref.WeakReference;
return new LazyVal<T>(initializer); return new LazyVal<T>(initializer);
} }
@NotNull
public static <T> LazySoftVal<T> lazySoft(@Nullable T initialValue, @NotNull Function0<T> initializer) {
return new LazySoftVal<T>(initialValue, initializer);
}
@NotNull @NotNull
public static <T> LazySoftVal<T> lazySoft(@NotNull Function0<T> initializer) { public static <T> LazySoftVal<T> lazySoft(@NotNull Function0<T> initializer) {
return new LazySoftVal<T>(initializer); return lazySoft(null, initializer);
} }
@NotNull @NotNull