From a501878d60405ab7a8f6314984f16e69ae35be0d Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Sun, 18 Oct 2015 22:19:04 +0300 Subject: [PATCH] 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 --- .../kotlin/reflect/jvm/internal/KClassImpl.kt | 10 ++++++++-- .../jvm/internal/KDeclarationContainerImpl.kt | 8 +++++++- .../kotlin/reflect/jvm/internal/KPackageImpl.kt | 11 ++++++----- .../reflect/jvm/internal/ReflectProperties.java | 17 +++++++++-------- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt index 67b206cb15a..f91e9c805e3 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt @@ -38,7 +38,7 @@ import kotlin.reflect.KFunction import kotlin.reflect.KotlinReflectionInternalError internal class KClassImpl(override val jClass: Class) : KDeclarationContainerImpl(), KClass, KAnnotatedElementImpl { - val descriptor by ReflectProperties.lazySoft { + private val descriptor_ = ReflectProperties.lazySoft { val classId = classId val descriptor = @@ -48,6 +48,9 @@ internal class KClassImpl(override val jClass: Class) : 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(override val jClass: Class) : 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(override val jClass: Class) : KDeclaration field.get(null) as T } + override val objectInstance: T? + get() = objectInstance_() + override fun equals(other: Any?): Boolean = other is KClassImpl<*> && jClass == other.jClass diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KDeclarationContainerImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KDeclarationContainerImpl.kt index bf05b74c2e7..0d6b00b99cf 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KDeclarationContainerImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KDeclarationContainerImpl.kt @@ -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 abstract fun getProperties(name: Name): Collection diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPackageImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPackageImpl.kt index d927d8b576f..3088d471672 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPackageImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KPackageImpl.kt @@ -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> get() = getMembers(scope, declaredOnly = false, nonExtensions = true, extensions = true).toList() diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectProperties.java b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectProperties.java index 926a87d2874..09ebcd7b04e 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectProperties.java +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectProperties.java @@ -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 { 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 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 cached = value; if (cached != null) { Object result = cached.get();