Workaround circular dependency of KProperty1 and KClassImpl

This appeared after introduction of KProperty in delegated property convention
methods. If a KProperty1 instance is created while constructing a KClassImpl
instance before assigning the 'jClass' field, an NPE is thrown because the
constructor of that KProperty1 instance tries to lookup the declaring class of
the property, which in some case happens to be the exact KClassImpl we're
constructing. No test added because I was not able to reproduce the behavior in
an isolated environment
This commit is contained in:
Alexander Udalov
2015-10-18 22:19:04 +03:00
parent d0f318d7db
commit a501878d60
4 changed files with 30 additions and 16 deletions
@@ -38,7 +38,7 @@ import kotlin.reflect.KFunction
import kotlin.reflect.KotlinReflectionInternalError
internal class KClassImpl<T : Any>(override val jClass: Class<T>) : KDeclarationContainerImpl(), KClass<T>, KAnnotatedElementImpl {
val descriptor by ReflectProperties.lazySoft {
private val descriptor_ = ReflectProperties.lazySoft {
val classId = classId
val descriptor =
@@ -48,6 +48,9 @@ internal class KClassImpl<T : Any>(override val jClass: Class<T>) : KDeclaration
descriptor ?: throw KotlinReflectionInternalError("Class not resolved: $jClass")
}
val descriptor: ClassDescriptor
get() = descriptor_()
override val annotated: Annotated get() = descriptor
private val classId: ClassId get() = RuntimeTypeMapper.mapJvmClassToKotlinClassId(jClass)
@@ -138,7 +141,7 @@ internal class KClassImpl<T : Any>(override val jClass: Class<T>) : KDeclaration
}.filterNotNull().map { KClassImpl(it) }
@Suppress("UNCHECKED_CAST")
override val objectInstance: T? by ReflectProperties.lazy {
private val objectInstance_ = ReflectProperties.lazy {
val descriptor = descriptor
if (descriptor.kind != ClassKind.OBJECT) return@lazy null
@@ -151,6 +154,9 @@ internal class KClassImpl<T : Any>(override val jClass: Class<T>) : KDeclaration
field.get(null) as T
}
override val objectInstance: T?
get() = objectInstance_()
override fun equals(other: Any?): Boolean =
other is KClassImpl<*> && jClass == other.jClass
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.structure.reflect.classId
import org.jetbrains.kotlin.load.java.structure.reflect.createArrayType
import org.jetbrains.kotlin.load.java.structure.reflect.safeClassLoader
import org.jetbrains.kotlin.load.kotlin.reflect.RuntimeModuleData
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.scopes.JetScope
@@ -36,11 +37,16 @@ import kotlin.reflect.KCallable
import kotlin.reflect.KotlinReflectionInternalError
internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContainer {
// NB: be careful not to introduce delegated properties in this class and subclasses, there are problems with circular dependencies
// Note: this is stored here on a soft reference to prevent GC from destroying the weak reference to it in the moduleByClassLoader cache
val moduleData by ReflectProperties.lazySoft {
private val moduleData_ = ReflectProperties.lazySoft {
jClass.getOrCreateModule()
}
val moduleData: RuntimeModuleData
get() = moduleData_()
abstract val constructorDescriptors: Collection<ConstructorDescriptor>
abstract fun getProperties(name: Name): Collection<PropertyDescriptor>
@@ -28,13 +28,14 @@ import kotlin.reflect.KCallable
import kotlin.reflect.KPackage
internal class KPackageImpl(override val jClass: Class<*>, val moduleName: String) : KDeclarationContainerImpl(), KPackage {
val descriptor by ReflectProperties.lazySoft {
val moduleData = moduleData
moduleData.packageFacadeProvider.registerModule(moduleName)
moduleData.module.getPackage(jClass.classId.packageFqName)
private val descriptor = ReflectProperties.lazySoft {
with(moduleData) {
packageFacadeProvider.registerModule(moduleName)
module.getPackage(jClass.classId.packageFqName)
}
}
internal val scope: JetScope get() = descriptor.memberScope
internal val scope: JetScope get() = descriptor().memberScope
override val members: Collection<KCallable<*>>
get() = getMembers(scope, declaredOnly = false, nonExtensions = true, extensions = true).toList()
@@ -23,15 +23,16 @@ import org.jetbrains.annotations.Nullable;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
/**
* The fact that this is a Java class is used in reflection implementation classes: otherwise if it were a Kotlin class, KClassImpl()
* instance would have been invoked in its static initializer (to create and cache $kotlinClass field), resulting in infinite recursion.
*/
/* package */ class ReflectProperties {
public static abstract class Val<T> {
private static final Object NULL_VALUE = new Object() {};
public abstract T get(Object instance, Object metadata);
@SuppressWarnings("UnusedParameters")
public T get(Object instance, Object metadata) {
return invoke();
}
public abstract T invoke();
protected Object escape(T value) {
return value == null ? NULL_VALUE : value;
@@ -53,7 +54,7 @@ import java.lang.ref.WeakReference;
}
@Override
public T get(Object instance, Object metadata) {
public T invoke() {
Object cached = value;
if (cached != null) {
return unescape(cached);
@@ -80,7 +81,7 @@ import java.lang.ref.WeakReference;
}
@Override
public T get(Object instance, Object metadata) {
public T invoke() {
SoftReference<Object> cached = value;
if (cached != null) {
Object result = cached.get();
@@ -107,7 +108,7 @@ import java.lang.ref.WeakReference;
}
@Override
public T get(Object instance, Object metadata) {
public T invoke() {
WeakReference<Object> cached = value;
if (cached != null) {
Object result = cached.get();