Introduce reflection cache in KClassImpl and KPackageImpl
Most of KClassImpl operations should be cached, but creating a lazy value for each operation would significantly increase memory footprint of a KClassImpl instance. Therefore we decide to store all lazy values under a single lazy value named 'data' which is stored in KClassImpl. There's a minor overhead of indirection on any operation now, however it'll allow us to substantially increase reflection performance by caching everything we can
This commit is contained in:
@@ -35,18 +35,36 @@ import kotlin.reflect.*
|
||||
|
||||
internal class KClassImpl<T : Any>(override val jClass: Class<T>) :
|
||||
KDeclarationContainerImpl(), KClass<T>, KClassifierImpl, KAnnotatedElementImpl {
|
||||
private val descriptor_ = ReflectProperties.lazySoft {
|
||||
val classId = classId
|
||||
private inner class Data : KDeclarationContainerImpl.Data() {
|
||||
val descriptor: ClassDescriptor by ReflectProperties.lazySoft {
|
||||
val classId = classId
|
||||
val moduleData = data().moduleData
|
||||
|
||||
val descriptor =
|
||||
if (classId.isLocal) moduleData.deserialization.deserializeClass(classId)
|
||||
else moduleData.module.findClassAcrossModuleDependencies(classId)
|
||||
val descriptor =
|
||||
if (classId.isLocal) moduleData.deserialization.deserializeClass(classId)
|
||||
else moduleData.module.findClassAcrossModuleDependencies(classId)
|
||||
|
||||
descriptor ?: reportUnresolvedClass()
|
||||
descriptor ?: reportUnresolvedClass()
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val objectInstance: T? by ReflectProperties.lazy {
|
||||
val descriptor = descriptor
|
||||
if (descriptor.kind != ClassKind.OBJECT) return@lazy null
|
||||
|
||||
val field = if (descriptor.isCompanionObject && !CompanionObjectMapping.isMappedIntrinsicCompanionObject(descriptor)) {
|
||||
jClass.enclosingClass.getDeclaredField(descriptor.name.asString())
|
||||
}
|
||||
else {
|
||||
jClass.getDeclaredField(JvmAbi.INSTANCE_FIELD)
|
||||
}
|
||||
field.get(null) as T
|
||||
}
|
||||
}
|
||||
|
||||
override val descriptor: ClassDescriptor
|
||||
get() = descriptor_()
|
||||
private val data = ReflectProperties.lazy { Data() }
|
||||
|
||||
override val descriptor: ClassDescriptor get() = data().descriptor
|
||||
|
||||
override val annotated: Annotated get() = descriptor
|
||||
|
||||
@@ -70,7 +88,6 @@ internal class KClassImpl<T : Any>(override val jClass: Class<T>) :
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun getProperties(name: Name): Collection<PropertyDescriptor> =
|
||||
(memberScope.getContributedVariables(name, NoLookupLocation.FROM_REFLECTION) +
|
||||
staticScope.getContributedVariables(name, NoLookupLocation.FROM_REFLECTION))
|
||||
@@ -123,22 +140,7 @@ internal class KClassImpl<T : Any>(override val jClass: Class<T>) :
|
||||
jClass?.let { KClassImpl(it) }
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private val objectInstance_ = ReflectProperties.lazy {
|
||||
val descriptor = descriptor
|
||||
if (descriptor.kind != ClassKind.OBJECT) return@lazy null
|
||||
|
||||
val field = if (descriptor.isCompanionObject && !CompanionObjectMapping.isMappedIntrinsicCompanionObject(descriptor)) {
|
||||
jClass.enclosingClass.getDeclaredField(descriptor.name.asString())
|
||||
}
|
||||
else {
|
||||
jClass.getDeclaredField(JvmAbi.INSTANCE_FIELD)
|
||||
}
|
||||
field.get(null) as T
|
||||
}
|
||||
|
||||
override val objectInstance: T?
|
||||
get() = objectInstance_()
|
||||
override val objectInstance: T? get() = data().objectInstance
|
||||
|
||||
override fun isInstance(value: Any?): Boolean {
|
||||
// TODO: use Kotlin semantics for mutable/read-only collections once KT-11754 is supported (see TypeIntrinsics)
|
||||
|
||||
@@ -31,14 +31,13 @@ import kotlin.jvm.internal.ClassBasedDeclarationContainer
|
||||
import kotlin.reflect.KotlinReflectionInternalError
|
||||
|
||||
internal abstract class KDeclarationContainerImpl : ClassBasedDeclarationContainer {
|
||||
// Note: this is stored here on a soft reference to prevent GC from destroying the weak reference to it in the moduleByClassLoader cache
|
||||
private val moduleData_ = ReflectProperties.lazySoft {
|
||||
jClass.getOrCreateModule()
|
||||
protected abstract inner class Data {
|
||||
// This is stored here on a soft reference to prevent GC from destroying the weak reference to it in the moduleByClassLoader cache
|
||||
val moduleData: RuntimeModuleData by ReflectProperties.lazySoft {
|
||||
jClass.getOrCreateModule()
|
||||
}
|
||||
}
|
||||
|
||||
val moduleData: RuntimeModuleData
|
||||
get() = moduleData_()
|
||||
|
||||
protected open val methodOwner: Class<*>
|
||||
get() = jClass
|
||||
|
||||
|
||||
@@ -16,10 +16,7 @@
|
||||
|
||||
package kotlin.reflect.jvm.internal
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaPackageFragment
|
||||
import org.jetbrains.kotlin.load.java.structure.reflect.classId
|
||||
@@ -31,27 +28,33 @@ import org.jetbrains.kotlin.serialization.deserialization.descriptors.Deserializ
|
||||
import kotlin.reflect.KCallable
|
||||
|
||||
internal class KPackageImpl(override val jClass: Class<*>, val moduleName: String) : KDeclarationContainerImpl() {
|
||||
private val descriptor = ReflectProperties.lazySoft {
|
||||
with(moduleData) {
|
||||
packageFacadeProvider.registerModule(moduleName)
|
||||
module.getPackage(jClass.classId.packageFqName)
|
||||
private inner class Data : KDeclarationContainerImpl.Data() {
|
||||
val descriptor: PackageViewDescriptor by ReflectProperties.lazySoft {
|
||||
with(moduleData) {
|
||||
packageFacadeProvider.registerModule(moduleName)
|
||||
module.getPackage(jClass.classId.packageFqName)
|
||||
}
|
||||
}
|
||||
|
||||
val methodOwner: Class<*> by ReflectProperties.lazy {
|
||||
val facadeName = ReflectKotlinClass.create(jClass)?.classHeader?.multifileClassName
|
||||
// We need to check isNotEmpty because this is the value read from the annotation which cannot be null.
|
||||
// The default value for 'xs' is empty string, as declared in kotlin.Metadata
|
||||
// TODO: do not read ReflectKotlinClass multiple times, obtain facade name from descriptor
|
||||
if (facadeName != null && facadeName.isNotEmpty()) {
|
||||
jClass.classLoader.loadClass(facadeName.replace('/', '.'))
|
||||
}
|
||||
else {
|
||||
jClass
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val methodOwner: Class<*> by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||
val facadeName = ReflectKotlinClass.create(jClass)?.classHeader?.multifileClassName
|
||||
// We need to check isNotEmpty because this is the value read from the annotation which cannot be null.
|
||||
// The default value for 'xs' is empty string, as declared in kotlin.Metadata
|
||||
// TODO: do not read ReflectKotlinClass multiple times, obtain facade name from descriptor
|
||||
if (facadeName != null && facadeName.isNotEmpty()) {
|
||||
jClass.classLoader.loadClass(facadeName.replace('/', '.'))
|
||||
}
|
||||
else {
|
||||
jClass
|
||||
}
|
||||
}
|
||||
private val data = ReflectProperties.lazy { Data() }
|
||||
|
||||
internal val scope: MemberScope get() = descriptor().memberScope
|
||||
override val methodOwner: Class<*> get() = data().methodOwner
|
||||
|
||||
private val scope: MemberScope get() = data().descriptor.memberScope
|
||||
|
||||
override val members: Collection<KCallable<*>>
|
||||
get() = getMembers(scope, declaredOnly = false, nonExtensions = true, extensions = true).filter { member ->
|
||||
|
||||
Reference in New Issue
Block a user