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:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user