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