
jvmMain.kotlinx.serialization.internal.Caching.kt Maven / Gradle / Ivy
/*
* Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package kotlinx.serialization.internal
import kotlinx.serialization.KSerializer
import java.util.concurrent.ConcurrentHashMap
import kotlin.reflect.KClass
import kotlin.reflect.KType
/*
* By default, we use ClassValue-based caches to avoid classloader leaks,
* but ClassValue is not available on Android, thus we attempt to check it dynamically
* and fallback to ConcurrentHashMap-based cache.
*/
private val useClassValue = runCatching {
Class.forName("java.lang.ClassValue")
}.map { true }.getOrDefault(false)
/**
* Creates a **strongly referenced** cache of values associated with [Class].
* Serializers are computed using provided [factory] function.
*
* `null` values are not supported, though there aren't any technical limitations.
*/
internal actual fun createCache(factory: (KClass<*>) -> KSerializer?): SerializerCache {
return if (useClassValue) ClassValueCache(factory) else ConcurrentHashMapCache(factory)
}
/**
* Creates a **strongly referenced** cache of values associated with [Class].
* Serializers are computed using provided [factory] function.
*
* `null` values are not supported, though there aren't any technical limitations.
*/
internal actual fun createParametrizedCache(factory: (KClass, List) -> KSerializer?): ParametrizedSerializerCache {
return if (useClassValue) ClassValueParametrizedCache(factory) else ConcurrentHashMapParametrizedCache(factory)
}
@SuppressAnimalSniffer
private class ClassValueCache(private val compute: (KClass<*>) -> KSerializer?) : SerializerCache {
private val classValue = initClassValue()
private fun initClassValue() = object : ClassValue>() {
/*
* Since during the computing of the value for the `ClassValue` entry, we do not know whether a nullable
* serializer is needed, so we may need to differentiate nullable/non-null caches by a level higher
*/
override fun computeValue(type: Class<*>): CacheEntry {
return CacheEntry(compute(type.kotlin))
}
}
override fun get(key: KClass): KSerializer? = classValue[key.java].serializer
}
@SuppressAnimalSniffer
private class ClassValueParametrizedCache(private val compute: (KClass, List) -> KSerializer?) : ParametrizedSerializerCache {
private val classValue = initClassValue()
private fun initClassValue() = object : ClassValue>() {
/*
* Since during the computing of the value for the `ClassValue` entry, we do not know whether a nullable
* serializer is needed, so we may need to differentiate nullable/non-null caches by a level higher
*/
override fun computeValue(type: Class<*>): ParametrizedCacheEntry {
return ParametrizedCacheEntry()
}
}
override fun get(key: KClass, types: List): Result?> =
classValue[key.java].computeIfAbsent(types) { compute(key, types) }
}
/**
* We no longer support Java 6, so the only place we use this cache is Android, where there
* are no classloader leaks issue, thus we can safely use strong references and do not bother
* with WeakReference wrapping.
*/
private class ConcurrentHashMapCache(private val compute: (KClass<*>) -> KSerializer?) : SerializerCache {
private val cache = ConcurrentHashMap, CacheEntry>()
override fun get(key: KClass): KSerializer? {
return cache.getOrPut(key.java) {
CacheEntry(compute(key))
}.serializer
}
}
private class ConcurrentHashMapParametrizedCache(private val compute: (KClass, List) -> KSerializer?) : ParametrizedSerializerCache {
private val cache = ConcurrentHashMap, ParametrizedCacheEntry>()
override fun get(key: KClass, types: List): Result?> {
return cache.getOrPut(key.java) { ParametrizedCacheEntry() }
.computeIfAbsent(types) { compute(key, types) }
}
}
private class CacheEntry(@JvmField val serializer: KSerializer?)
private class ParametrizedCacheEntry {
private val serializers: ConcurrentHashMap, Result?>> = ConcurrentHashMap()
inline fun computeIfAbsent(types: List, producer: () -> KSerializer?): Result?> {
return serializers.getOrPut(types) {
kotlin.runCatching { producer() }
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy