Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce overhead by integrating JmClass and ReducedKmClass #146

Merged
merged 2 commits into from
Sep 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import kotlinx.metadata.Flag
import kotlinx.metadata.KmClassifier
import kotlinx.metadata.KmType
import kotlinx.metadata.KmValueParameter
import kotlinx.metadata.internal.accept
import kotlinx.metadata.internal.metadata.jvm.deserialization.JvmProtoBufUtil
import kotlinx.metadata.jvm.JvmMethodSignature
import java.lang.reflect.AnnotatedElement
import java.lang.reflect.Constructor
Expand All @@ -17,14 +15,6 @@ internal typealias KotlinDuration = kotlin.time.Duration

internal fun Class<*>.isUnboxableValueClass() = this.getAnnotation(JvmInline::class.java) != null

internal fun Metadata.accept(visitor: ReducedKmClassVisitor) {
val (strings, proto) = JvmProtoBufUtil.readClassDataFrom(data1.takeIf(Array<*>::isNotEmpty)!!, data2)
proto.accept(visitor, strings)
}

internal fun Class<*>.toReducedKmClass(): ReducedKmClass? = this.getAnnotation(Metadata::class.java)
?.let { ReducedKmClass().apply { it.accept(this) } }

private val primitiveClassToDesc by lazy {
mapOf(
Byte::class.javaPrimitiveType to 'B',
Expand Down
161 changes: 92 additions & 69 deletions src/main/kotlin/io/github/projectmapk/jackson/module/kogera/JmClass.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import kotlinx.metadata.KmTypeVisitor
import kotlinx.metadata.KmVariance
import kotlinx.metadata.KmVersionRequirementVisitor
import kotlinx.metadata.flagsOf
import kotlinx.metadata.internal.accept
import kotlinx.metadata.internal.metadata.jvm.deserialization.JvmProtoBufUtil
import kotlinx.metadata.jvm.getterSignature
import kotlinx.metadata.jvm.signature
import java.lang.reflect.Constructor
Expand Down Expand Up @@ -63,71 +65,88 @@ internal sealed class ReducedKmClassVisitor : KmClassVisitor() {
override fun visitEnd() {}
}

internal class ReducedKmClass : ReducedKmClassVisitor() {
var flags: Flags = flagsOf()
val properties: MutableList<KmProperty> = ArrayList()
val constructors: MutableList<KmConstructor> = ArrayList(1)
var companionObject: String? = null
val sealedSubclasses: MutableList<ClassName> = ArrayList(0)
var inlineClassUnderlyingType: KmType? = null

override fun visit(flags: Flags, name: ClassName) {
this.flags = flags
}
// Jackson Metadata Class
internal sealed interface JmClass {
class CompanionObject(declaringClass: Class<*>, companionObject: String) {
private class ReducedCompanionVisitor(companionClass: Class<*>) : ReducedKmClassVisitor() {
val functions: MutableList<KmFunction> = arrayListOf()

override fun visitProperty(flags: Flags, name: String, getterFlags: Flags, setterFlags: Flags): KmPropertyVisitor =
KmProperty(flags, name, getterFlags, setterFlags).apply { properties.add(this) }
init {
companionClass.getAnnotation(Metadata::class.java)!!.accept(this)
}

override fun visitConstructor(flags: Flags): KmConstructorVisitor =
KmConstructor(flags).apply { constructors.add(this) }
override fun visitFunction(flags: Flags, name: String): KmFunctionVisitor? = KmFunction(flags, name)
.apply { functions.add(this) }
}

override fun visitCompanionObject(name: String) {
this.companionObject = name
}
private val companionField: Field = declaringClass.getDeclaredField(companionObject)
val type: Class<*> = companionField.type
val isAccessible: Boolean = companionField.isAccessible
private val functions by lazy { ReducedCompanionVisitor(type).functions }
val instance: Any by lazy {
// To prevent the call from failing, save the initial value and then rewrite the flag.
if (!companionField.isAccessible) companionField.isAccessible = true
companionField.get(null)
}

override fun visitSealedSubclass(name: ClassName) {
sealedSubclasses.add(name)
fun findFunctionByMethod(method: Method): KmFunction? {
val signature = method.toSignature()
return functions.find { it.signature == signature }
}
}

override fun visitInlineClassUnderlyingType(flags: Flags): KmTypeVisitor =
KmType(flags).also { inlineClassUnderlyingType = it }
val flags: Flags
val constructors: List<KmConstructor>
val sealedSubclasses: List<ClassName>
val inlineClassUnderlyingType: KmType?
val propertyNameSet: Set<String>
val properties: List<KmProperty>
val companion: CompanionObject?

fun findKmConstructor(constructor: Constructor<*>): KmConstructor?
fun findPropertyByField(field: Field): KmProperty?
fun findPropertyByGetter(getter: Method): KmProperty?
}

// Jackson Metadata Class
internal class JmClass(
private class JmClassImpl(
clazz: Class<*>,
kmClass: ReducedKmClass,
metadata: Metadata,
superJmClass: JmClass?,
interfaceJmClasses: List<JmClass>
) {
private val allPropsMap: Map<String, KmProperty> = mutableMapOf<String, KmProperty>().apply {
kmClass.properties.forEach {
this[it.name] = it
}
) : ReducedKmClassVisitor(), JmClass {
private val allPropsMap: MutableMap<String, KmProperty> = mutableMapOf()
private var companionPropName: String? = null

override var flags: Flags = flagsOf()
override val constructors: MutableList<KmConstructor> = mutableListOf()
override val sealedSubclasses: MutableList<ClassName> = mutableListOf()
override var inlineClassUnderlyingType: KmType? = null

init {
metadata.accept(this)

// Add properties of inherited classes and interfaces
// If an `interface` is implicitly implemented by an abstract class,
// it is necessary to obtain a more specific type, so always add it from the abstract class first.
superJmClass?.allPropsMap?.forEach {
this.putIfAbsent(it.key, it.value)
(superJmClass as JmClassImpl?)?.allPropsMap?.forEach {
this.allPropsMap.putIfAbsent(it.key, it.value)
}
interfaceJmClasses.forEach { i ->
@Suppress("UNCHECKED_CAST")
(interfaceJmClasses as List<JmClassImpl>).forEach { i ->
i.allPropsMap.forEach {
this.putIfAbsent(it.key, it.value)
this.allPropsMap.putIfAbsent(it.key, it.value)
}
}
}

val flags: Flags = kmClass.flags
val constructors: List<KmConstructor> = kmClass.constructors
val propertyNameSet: Set<String> get() = allPropsMap.keys
val properties: List<KmProperty> = allPropsMap.values.toList()
val sealedSubclasses: List<ClassName> = kmClass.sealedSubclasses
private val companionPropName: String? = kmClass.companionObject
val companion: CompanionObject? by lazy { companionPropName?.let { CompanionObject(clazz, it) } }
val inlineClassUnderlyingType: KmType? = kmClass.inlineClassUnderlyingType
// computed props
override val propertyNameSet: Set<String> get() = allPropsMap.keys
override val properties: List<KmProperty> by lazy { allPropsMap.values.toList() }
override val companion: JmClass.CompanionObject? by lazy {
companionPropName?.let { JmClass.CompanionObject(clazz, it) }
}

fun findKmConstructor(constructor: Constructor<*>): KmConstructor? {
override fun findKmConstructor(constructor: Constructor<*>): KmConstructor? {
val descHead = constructor.parameterTypes.toDescBuilder()
val len = descHead.length
val desc = CharArray(len + 1).apply {
Expand All @@ -148,40 +167,44 @@ internal class JmClass(
}

// Field name always matches property name
fun findPropertyByField(field: Field): KmProperty? = allPropsMap[field.name]
override fun findPropertyByField(field: Field): KmProperty? = allPropsMap[field.name]

fun findPropertyByGetter(getter: Method): KmProperty? {
override fun findPropertyByGetter(getter: Method): KmProperty? {
val getterName = getter.name
return properties.find { it.getterSignature?.name == getterName }
}

internal class CompanionObject(declaringClass: Class<*>, companionObject: String) {
private class ReducedCompanionVisitor : ReducedKmClassVisitor() {
val functions: MutableList<KmFunction> = arrayListOf()
// KmClassVisitor
override fun visit(flags: Flags, name: ClassName) {
this.flags = flags
}

override fun visitFunction(flags: Flags, name: String): KmFunctionVisitor? = KmFunction(flags, name)
.apply { functions.add(this) }
override fun visitProperty(flags: Flags, name: String, getterFlags: Flags, setterFlags: Flags): KmPropertyVisitor =
KmProperty(flags, name, getterFlags, setterFlags).apply { allPropsMap[name] = this }

companion object {
fun from(companionClass: Class<*>): ReducedCompanionVisitor = ReducedCompanionVisitor().apply {
companionClass.getAnnotation(Metadata::class.java)!!.accept(this)
}
}
}
override fun visitConstructor(flags: Flags): KmConstructorVisitor =
KmConstructor(flags).apply { constructors.add(this) }

private val companionField: Field = declaringClass.getDeclaredField(companionObject)
val type: Class<*> = companionField.type
val isAccessible: Boolean = companionField.isAccessible
private val functions by lazy { ReducedCompanionVisitor.from(type).functions }
val instance: Any by lazy {
// To prevent the call from failing, save the initial value and then rewrite the flag.
if (!companionField.isAccessible) companionField.isAccessible = true
companionField.get(null)
}
override fun visitCompanionObject(name: String) {
this.companionPropName = name
}

fun findFunctionByMethod(method: Method): KmFunction? {
val signature = method.toSignature()
return functions.find { it.signature == signature }
}
override fun visitSealedSubclass(name: ClassName) {
sealedSubclasses.add(name)
}

override fun visitInlineClassUnderlyingType(flags: Flags): KmTypeVisitor =
KmType(flags).also { inlineClassUnderlyingType = it }
}

private fun Metadata.accept(visitor: ReducedKmClassVisitor) {
val (strings, proto) = JvmProtoBufUtil.readClassDataFrom(data1.takeIf(Array<*>::isNotEmpty)!!, data2)
proto.accept(visitor, strings)
}

internal fun JmClass(
clazz: Class<*>,
metadata: Metadata,
superJmClass: JmClass?,
interfaceJmClasses: List<JmClass>
): JmClass = JmClassImpl(clazz, metadata, superJmClass, interfaceJmClasses)
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable {

fun getJmClass(clazz: Class<*>): JmClass? {
return classCache[clazz] ?: run {
val kmClass = clazz.toReducedKmClass() ?: return null
val metadata = clazz.getAnnotation(Metadata::class.java) ?: return null

// Do not parse super class for interfaces.
val superJmClass = if (!clazz.isInterface) {
Expand All @@ -41,7 +41,7 @@ internal class ReflectionCache(reflectionCacheSize: Int) : Serializable {
}
val interfaceJmClasses = clazz.interfaces.mapNotNull { getJmClass(it) }

val value = JmClass(clazz, kmClass, superJmClass, interfaceJmClasses)
val value = JmClass(clazz, metadata, superJmClass, interfaceJmClasses)
(classCache.putIfAbsent(clazz, value) ?: value)
}
}
Expand Down