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 import kotlin.reflect.KotlinReflectionInternalError
internal class KClassImpl<T : Any>(override val jClass: Class<T>) : KDeclarationContainerImpl(), KClass<T>, KAnnotatedElementImpl { 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 classId = classId
val descriptor = val descriptor =
@@ -48,6 +48,9 @@ internal class KClassImpl<T : Any>(override val jClass: Class<T>) : KDeclaration
descriptor ?: throw KotlinReflectionInternalError("Class not resolved: $jClass") descriptor ?: throw KotlinReflectionInternalError("Class not resolved: $jClass")
} }
val descriptor: ClassDescriptor
get() = descriptor_()
override val annotated: Annotated get() = descriptor override val annotated: Annotated get() = descriptor
private val classId: ClassId get() = RuntimeTypeMapper.mapJvmClassToKotlinClassId(jClass) 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) } }.filterNotNull().map { KClassImpl(it) }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override val objectInstance: T? by ReflectProperties.lazy { private val objectInstance_ = ReflectProperties.lazy {
val descriptor = descriptor val descriptor = descriptor
if (descriptor.kind != ClassKind.OBJECT) return@lazy null 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 field.get(null) as T
} }
override val objectInstance: T?
get() = objectInstance_()
override fun equals(other: Any?): Boolean = override fun equals(other: Any?): Boolean =
other is KClassImpl<*> && jClass == other.jClass 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.classId
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.safeClassLoader 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.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
@@ -36,11 +37,16 @@ import kotlin.reflect.KCallable
import kotlin.reflect.KotlinReflectionInternalError import kotlin.reflect.KotlinReflectionInternalError
internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContainer { 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 // 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() jClass.getOrCreateModule()
} }
val moduleData: RuntimeModuleData
get() = moduleData_()
abstract val constructorDescriptors: Collection<ConstructorDescriptor> abstract val constructorDescriptors: Collection<ConstructorDescriptor>
abstract fun getProperties(name: Name): Collection<PropertyDescriptor> abstract fun getProperties(name: Name): Collection<PropertyDescriptor>
@@ -28,13 +28,14 @@ import kotlin.reflect.KCallable
import kotlin.reflect.KPackage import kotlin.reflect.KPackage
internal class KPackageImpl(override val jClass: Class<*>, val moduleName: String) : KDeclarationContainerImpl(), KPackage { internal class KPackageImpl(override val jClass: Class<*>, val moduleName: String) : KDeclarationContainerImpl(), KPackage {
val descriptor by ReflectProperties.lazySoft { private val descriptor = ReflectProperties.lazySoft {
val moduleData = moduleData with(moduleData) {
moduleData.packageFacadeProvider.registerModule(moduleName) packageFacadeProvider.registerModule(moduleName)
moduleData.module.getPackage(jClass.classId.packageFqName) module.getPackage(jClass.classId.packageFqName)
}
} }
internal val scope: JetScope get() = descriptor.memberScope internal val scope: JetScope get() = descriptor().memberScope
override val members: Collection<KCallable<*>> override val members: Collection<KCallable<*>>
get() = getMembers(scope, declaredOnly = false, nonExtensions = true, extensions = true).toList() 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.SoftReference;
import java.lang.ref.WeakReference; 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 { /* package */ class ReflectProperties {
public static abstract class Val<T> { public static abstract class Val<T> {
private static final Object NULL_VALUE = new Object() {}; 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) { protected Object escape(T value) {
return value == null ? NULL_VALUE : value; return value == null ? NULL_VALUE : value;
@@ -53,7 +54,7 @@ import java.lang.ref.WeakReference;
} }
@Override @Override
public T get(Object instance, Object metadata) { public T invoke() {
Object cached = value; Object cached = value;
if (cached != null) { if (cached != null) {
return unescape(cached); return unescape(cached);
@@ -80,7 +81,7 @@ import java.lang.ref.WeakReference;
} }
@Override @Override
public T get(Object instance, Object metadata) { public T invoke() {
SoftReference<Object> cached = value; SoftReference<Object> cached = value;
if (cached != null) { if (cached != null) {
Object result = cached.get(); Object result = cached.get();
@@ -107,7 +108,7 @@ import java.lang.ref.WeakReference;
} }
@Override @Override
public T get(Object instance, Object metadata) { public T invoke() {
WeakReference<Object> cached = value; WeakReference<Object> cached = value;
if (cached != null) { if (cached != null) {
Object result = cached.get(); Object result = cached.get();