feat(JS/IR isInterfaceImpl perf): improve isInterfaceImpl performance by memoize an result.

This commit is contained in:
Artem Kobzar
2022-02-28 20:28:10 +00:00
committed by Space
parent f8f1b3fd41
commit ac542d4c01
8 changed files with 267 additions and 109 deletions
@@ -7,10 +7,12 @@ package org.jetbrains.kotlin.ir.backend.js
import org.jetbrains.kotlin.builtins.PrimitiveType import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.backend.js.utils.getJsName
import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrProperty import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.isLong import org.jetbrains.kotlin.ir.types.isLong
import org.jetbrains.kotlin.ir.util.constructors import org.jetbrains.kotlin.ir.util.constructors
@@ -105,7 +107,6 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
// RTTI: // RTTI:
val isInterfaceSymbol = getInternalFunction("isInterface") val isInterfaceSymbol = getInternalFunction("isInterface")
val isArraySymbol = getInternalFunction("isArray") val isArraySymbol = getInternalFunction("isArray")
// val isCharSymbol = getInternalFunction("isChar") // val isCharSymbol = getInternalFunction("isChar")
@@ -178,10 +179,15 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
val jsNumberRangeToLong = getInternalFunction("numberRangeToLong") val jsNumberRangeToLong = getInternalFunction("numberRangeToLong")
val longClassSymbol = getInternalClassWithoutPackage("kotlin.Long") val longClassSymbol = getInternalClassWithoutPackage("kotlin.Long")
val promiseClassSymbol: IrClassSymbol by context.lazy2 { val promiseClassSymbol: IrClassSymbol by context.lazy2 {
getInternalClassWithoutPackage("kotlin.js.Promise") getInternalClassWithoutPackage("kotlin.js.Promise")
} }
val metadataInterfaceConstructorSymbol = getInternalFunction("interfaceMeta")
val metadataObjectConstructorSymbol = getInternalFunction("objectMeta")
val metadataClassConstructorSymbol = getInternalFunction("classMeta")
val longToDouble = context.symbolTable.referenceSimpleFunction( val longToDouble = context.symbolTable.referenceSimpleFunction(
context.getClass(FqName("kotlin.Long")).unsubstitutedMemberScope.findSingleFunction( context.getClass(FqName("kotlin.Long")).unsubstitutedMemberScope.findSingleFunction(
Name.identifier("toDouble") Name.identifier("toDouble")
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.ir.backend.js.dce
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
import org.jetbrains.kotlin.ir.backend.js.export.isExported import org.jetbrains.kotlin.ir.backend.js.export.isExported
import org.jetbrains.kotlin.ir.backend.js.lower.isBuiltInClass
import org.jetbrains.kotlin.ir.backend.js.utils.associatedObject import org.jetbrains.kotlin.ir.backend.js.utils.associatedObject
import org.jetbrains.kotlin.ir.backend.js.utils.getJsName import org.jetbrains.kotlin.ir.backend.js.utils.getJsName
import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName import org.jetbrains.kotlin.ir.backend.js.utils.getJsNameOrKotlinName
@@ -19,9 +20,7 @@ import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.classifierOrFail import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.classifierOrNull import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.types.getClass import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.util.constructedClass import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.primaryConstructor
internal class JsUsefulDeclarationProcessor( internal class JsUsefulDeclarationProcessor(
override val context: JsIrBackendContext, override val context: JsIrBackendContext,
@@ -112,6 +111,21 @@ internal class JsUsefulDeclarationProcessor(
} }
} }
override fun processClass(irClass: IrClass) {
super.processClass(irClass)
if (irClass.containsMetadata()) {
when {
irClass.isInterface -> context.intrinsics.metadataInterfaceConstructorSymbol.owner.enqueue(irClass, "interface metadata")
irClass.isObject -> context.intrinsics.metadataObjectConstructorSymbol.owner.enqueue(irClass, "object metadata")
else -> context.intrinsics.metadataClassConstructorSymbol.owner.enqueue(irClass, "class metadata")
}
}
}
private fun IrClass.containsMetadata(): Boolean =
!isExternal && !isExpect && !isBuiltInClass(this)
override fun processConstructedClassDeclaration(declaration: IrDeclaration) { override fun processConstructedClassDeclaration(declaration: IrDeclaration) {
if (declaration in result) return if (declaration in result) return
@@ -42,7 +42,7 @@ private val BODILESS_BUILTIN_CLASSES = listOf(
"kotlin.Function" "kotlin.Function"
).map { FqName(it) }.toSet() ).map { FqName(it) }.toSet()
private fun isBuiltInClass(declaration: IrDeclaration): Boolean = fun isBuiltInClass(declaration: IrDeclaration): Boolean =
declaration is IrClass && declaration.fqNameWhenAvailable in BODILESS_BUILTIN_CLASSES declaration is IrClass && declaration.fqNameWhenAvailable in BODILESS_BUILTIN_CLASSES
private val JsPackage = FqName("kotlin.js") private val JsPackage = FqName("kotlin.js")
@@ -51,6 +51,10 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
val jsClass = JsClass(name = className, baseClass = baseClassRef) val jsClass = JsClass(name = className, baseClass = baseClassRef)
if (baseClass != null && !baseClass.isAny()) {
jsClass.baseClass = baseClassRef
}
if (es6mode) classModel.preDeclarationBlock.statements += jsClass.makeStmt() if (es6mode) classModel.preDeclarationBlock.statements += jsClass.makeStmt()
for (declaration in irClass.declarations) { for (declaration in irClass.declarations) {
@@ -337,81 +341,76 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
} }
private fun generateClassMetadata(): JsStatement { private fun generateClassMetadata(): JsStatement {
val metadataLiteral = JsObjectLiteral(true) val metadataConstructor = with(context.staticContext.backendContext.intrinsics) {
val simpleName = irClass.name
if (!simpleName.isSpecial) {
val simpleNameProp = JsPropertyInitializer(JsNameRef(Namer.METADATA_SIMPLE_NAME), JsStringLiteral(simpleName.identifier))
metadataLiteral.propertyInitializers += simpleNameProp
}
val classKind = JsStringLiteral(
when { when {
irClass.isInterface -> "interface" irClass.isInterface -> metadataInterfaceConstructorSymbol
irClass.isObject -> "object" irClass.isObject -> metadataObjectConstructorSymbol
else -> "class" else -> metadataClassConstructorSymbol
} }
)
metadataLiteral.propertyInitializers += JsPropertyInitializer(JsNameRef(Namer.METADATA_CLASS_KIND), classKind)
metadataLiteral.propertyInitializers += generateSuperClasses()
metadataLiteral.propertyInitializers += generateAssociatedKeyProperties()
generateFastPrototype()?.let { metadataLiteral.propertyInitializers += it }
if (isCoroutineClass()) {
metadataLiteral.propertyInitializers += generateSuspendArity()
} }
return jsAssignment(JsNameRef(Namer.METADATA, classNameRef), metadataLiteral).makeStmt() val simpleName = irClass.name
.takeIf { !it.isSpecial }
?.let { JsStringLiteral(it.identifier) }
val interfaces = generateSuperClasses()
val associatedObjectKey = generateAssociatedObjectKey()
val associatedObjects = generateAssociatedObjects()
val fastPrototype = generateFastPrototype()
val suspendArity = generateSuspendArity()
val constructorCall = JsInvocation(
JsNameRef(context.getNameForStaticFunction(metadataConstructor.owner)),
listOf(simpleName, interfaces, associatedObjectKey, associatedObjects, suspendArity, fastPrototype)
.dropLastWhile { it == null }
.map { it ?: Namer.JS_UNDEFINED }
)
return jsAssignment(JsNameRef(Namer.METADATA, classNameRef), constructorCall).makeStmt()
} }
private fun isCoroutineClass(): Boolean = irClass.superTypes.any { it.isSuspendFunctionTypeOrSubtype() } private fun isCoroutineClass(): Boolean = irClass.superTypes.any { it.isSuspendFunctionTypeOrSubtype() }
private fun generateSuspendArity(): JsPropertyInitializer { private fun generateSuspendArity(): JsArrayLiteral? {
if (!isCoroutineClass()) return null
val arity = context.staticContext.backendContext.mapping.suspendArityStore[irClass]!! val arity = context.staticContext.backendContext.mapping.suspendArityStore[irClass]!!
.map { it.valueParameters.size } .map { it.valueParameters.size }
.distinct() .distinct()
.map { JsIntLiteral(it) } .map { JsIntLiteral(it) }
return JsPropertyInitializer(JsNameRef(Namer.METADATA_SUSPEND_ARITY), JsArrayLiteral(arity)) return JsArrayLiteral(arity)
} }
private fun generateSuperClasses(): JsPropertyInitializer { private fun generateSuperClasses(): JsArrayLiteral? {
return JsPropertyInitializer( val parentSymbols = irClass.superTypes.mapNotNull {
JsNameRef(Namer.METADATA_INTERFACES), val symbol = it.classifierOrFail as IrClassSymbol
JsArrayLiteral( val isFunctionType = it.isFunctionType()
irClass.superTypes.mapNotNull { // TODO: make sure that there is a test which breaks when isExternal is used here instead of isEffectivelyExternal
val symbol = it.classifierOrFail as IrClassSymbol val requireInMetadata = if (context.staticContext.backendContext.baseClassIntoMetadata)
val isFunctionType = it.isFunctionType() !it.isAny()
// TODO: make sure that there is a test which breaks when isExternal is used here instead of isEffectivelyExternal else
val requireInMetadata = if (context.staticContext.backendContext.baseClassIntoMetadata) symbol.isInterface
!it.isAny()
else
symbol.isInterface
if (requireInMetadata && !isFunctionType && !symbol.isEffectivelyExternal) { if (requireInMetadata && !isFunctionType && !symbol.isEffectivelyExternal) {
JsNameRef(context.getNameForClass(symbol.owner)) symbol
} else null } else null
} }
)
) return parentSymbols
.takeIf { it.isNotEmpty() }
?.run { JsArrayLiteral(map { JsNameRef(context.getNameForClass(it.owner)) }) }
} }
private fun generateFastPrototype() = baseClassRef?.let { private fun generateFastPrototype() = baseClassRef?.let { prototypeOf(it) }
JsPropertyInitializer(JsNameRef(Namer.METADATA_FAST_PROTOTYPE), prototypeOf(it))
}
private fun IrType.isFunctionType() = isFunctionOrKFunction() || isSuspendFunctionOrKFunction() private fun IrType.isFunctionType() = isFunctionOrKFunction() || isSuspendFunctionOrKFunction()
private fun generateAssociatedKeyProperties(): List<JsPropertyInitializer> { private fun generateAssociatedObjectKey(): JsIntLiteral? {
var result = emptyList<JsPropertyInitializer>() return context.getAssociatedObjectKey(irClass)?.let { JsIntLiteral(it) }
}
context.getAssociatedObjectKey(irClass)?.let { key ->
result = result + JsPropertyInitializer(JsStringLiteral("associatedObjectKey"), JsIntLiteral(key))
}
private fun generateAssociatedObjects(): JsObjectLiteral? {
val associatedObjects = irClass.annotations.mapNotNull { annotation -> val associatedObjects = irClass.annotations.mapNotNull { annotation ->
val annotationClass = annotation.symbol.owner.constructedClass val annotationClass = annotation.symbol.owner.constructedClass
context.getAssociatedObjectKey(annotationClass)?.let { key -> context.getAssociatedObjectKey(annotationClass)?.let { key ->
@@ -423,11 +422,9 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
} }
} }
if (associatedObjects.isNotEmpty()) { return associatedObjects
result = result + JsPropertyInitializer(JsStringLiteral("associatedObjects"), JsObjectLiteral(associatedObjects)) .takeIf { it.isNotEmpty() }
} ?.let { JsObjectLiteral(it) }
return result
} }
} }
@@ -28,14 +28,11 @@ object Namer {
val JS_ERROR = JsNameRef("Error") val JS_ERROR = JsNameRef("Error")
val JS_OBJECT = JsNameRef("Object") val JS_OBJECT = JsNameRef("Object")
val JS_UNDEFINED = JsNameRef("undefined")
val JS_OBJECT_CREATE_FUNCTION = JsNameRef("create", JS_OBJECT) val JS_OBJECT_CREATE_FUNCTION = JsNameRef("create", JS_OBJECT)
val METADATA = "\$metadata\$" val METADATA = "\$metadata\$"
val METADATA_INTERFACES = "interfaces" val METADATA_INTERFACE_ID = "interfaceId"
val METADATA_SIMPLE_NAME = "simpleName"
val METADATA_CLASS_KIND = "kind"
val METADATA_FAST_PROTOTYPE = "fastPrototype"
val METADATA_SUSPEND_ARITY = "suspendArity"
val KCALLABLE_GET_NAME = "<get-name>" val KCALLABLE_GET_NAME = "<get-name>"
val KCALLABLE_NAME = "callableName" val KCALLABLE_NAME = "callableName"
@@ -18,23 +18,34 @@ internal fun getLocalDelegateReference(name: String, type: dynamic, mutable: Boo
return getPropertyCallableRef(name, 0, type, lambda, if (mutable) lambda else null) return getPropertyCallableRef(name, 0, type, lambda, if (mutable) lambda else null)
} }
private fun getPropertyRefClass(obj: dynamic, metadata: dynamic): dynamic { private fun getPropertyRefClass(obj: Ctor, metadata: Metadata): dynamic {
obj.`$metadata$` = metadata; obj.`$metadata$` = metadata;
obj.constructor = obj; obj.constructor = obj;
return obj; return obj;
} }
private fun getKPropMetadata(paramCount: Int, setter: Any?, type: dynamic): dynamic { private fun getKPropMetadata(paramCount: Int, setter: Any?, type: dynamic): dynamic {
val mdata = propertyRefClassMetadataCache[paramCount][if (setter == null) 0 else 1] val mdata: Metadata = propertyRefClassMetadataCache[paramCount][if (setter == null) 0 else 1]
if (mdata.interfaces.length == 0) {
mdata.interfaces.push(type) if (mdata.interfaces.size == 0) {
mdata.interfaces.asDynamic().push(type)
if (mdata.interfacesCache == null) {
mdata.interfacesCache = generateInterfaceCache()
} else {
mdata.interfacesCache!!.isComplete = false
}
mdata.interfacesCache!!.extendCacheWithSingle(type)
} }
return mdata return mdata
} }
@Suppress("NOTHING_TO_INLINE") private fun metadataObject(): Metadata {
private inline fun metadataObject(): dynamic = js("{ kind: 'class', interfaces: [] }") val undef = js("undefined")
return classMeta(undef, undef, undef, undef, undef, undef)
}
private val propertyRefClassMetadataCache: Array<Array<dynamic>> = arrayOf<Array<dynamic>>( private val propertyRefClassMetadataCache: Array<Array<dynamic>> = arrayOf<Array<dynamic>>(
// immutable , mutable // immutable , mutable
+166 -33
View File
@@ -5,62 +5,195 @@
package kotlin.js package kotlin.js
private external interface Metadata { internal fun interfaceMeta(
val interfaces: Array<Ctor> name: String?,
val suspendArity: Array<Int>? interfaces: Array<Ctor>?,
associatedObjectKey: Number?,
// This field gives fast access to the prototype of metadata owner (Object.getPrototypeOf()) associatedObjects: dynamic,
// Can be pre-initialized or lazy initialized and then should be immutable suspendArity: Array<Int>?,
var fastPrototype: Prototype? ): Metadata {
return createMetadata("interface", name, interfaces, associatedObjectKey, associatedObjects, suspendArity, js("undefined"))
// The hint is used as a sort of flag, pointed to the class or the interface, which is not a parent for metadata owner
// Can be mutated quite often
var nonParentHint: dynamic
} }
private external interface Ctor { internal fun objectMeta(
val `$metadata$`: Metadata? name: String?,
interfaces: Array<Ctor>?,
associatedObjectKey: Number?,
associatedObjects: dynamic,
suspendArity: Array<Int>?,
fastPrototype: Prototype?,
): Metadata {
return createMetadata("object", name, interfaces, associatedObjectKey, associatedObjects, suspendArity, fastPrototype)
}
internal fun classMeta(
name: String?,
interfaces: Array<Ctor>?,
associatedObjectKey: Number?,
associatedObjects: dynamic,
suspendArity: Array<Int>?,
fastPrototype: Prototype?,
): Metadata {
return createMetadata("class", name, interfaces, associatedObjectKey, associatedObjects, suspendArity, fastPrototype)
}
// Seems like we need to disable this check if variables are used inside js annotation
@Suppress("UNUSED_PARAMETER")
private fun createMetadata(
kind: String,
name: String?,
interfaces: Array<Ctor>?,
associatedObjectKey: Number?,
associatedObjects: dynamic,
suspendArity: Array<Int>?,
fastPrototype: Prototype?
): Metadata {
return js("""({
kind: kind,
simpleName: name,
interfaceId: kind === "interface" ? -1 : undefined,
interfaces: interfaces || [],
associatedObjectKey: associatedObjectKey,
associatedObjects: associatedObjects,
suspendArity: suspendArity,
fastPrototype: fastPrototype,
${'$'}kClass$: undefined,
interfacesCache: {
isComplete: fastPrototype === undefined && (interfaces === undefined || interfaces.length === 0),
implementInterfaceMemo: {}
}
})""")
}
internal external interface Metadata {
val kind: String
val interfaces: Array<Ctor>
// This field gives fast access to the prototype of metadata owner (Object.getPrototypeOf())
// Can be pre-initialized or lazy initialized and then should be immutable
val simpleName: String?
val associatedObjectKey: Number?
val associatedObjects: dynamic
var interfaceId: Number?
var fastPrototype: Prototype?
val suspendArity: Array<Int>?
var `$kClass$`: dynamic
// This is an object for memoization of a isInterfaceImpl function
// Can be mutated quite often
var interfacesCache: IsImplementsCache?
}
// This is a flag for memoization of a isInterfaceImpl function
internal external interface IsImplementsCache {
var isComplete: Boolean
val implementInterfaceMemo: dynamic
}
internal external interface Ctor {
var `$metadata$`: Metadata?
var constructor: Ctor?
val prototype: Prototype? val prototype: Prototype?
} }
private external interface Prototype { internal external interface Prototype {
val constructor: Ctor? val constructor: Ctor?
} }
private var interfacesCounter = 0
private fun Ctor.getOrDefineInterfaceId(): Number? {
val metadata = `$metadata$`.unsafeCast<Metadata>()
val interfaceId = metadata.interfaceId ?: -1
return if (interfaceId != -1) {
interfaceId
} else {
val result = interfacesCounter++
metadata.interfaceId = result
result
}
}
private fun Ctor.getPrototype() = prototype?.let { js("Object").getPrototypeOf(it).unsafeCast<Prototype>() } private fun Ctor.getPrototype() = prototype?.let { js("Object").getPrototypeOf(it).unsafeCast<Prototype>() }
private fun isInterfaceImpl(ctor: Ctor, iface: dynamic): Boolean { internal fun IsImplementsCache.extendCacheWith(cache: IsImplementsCache?) {
if (ctor === iface) { val anotherInterfaceMemo = cache?.implementInterfaceMemo ?: return
return true js("Object").assign(implementInterfaceMemo, anotherInterfaceMemo)
} }
val superPrototype = ctor.`$metadata$`?.run { internal fun IsImplementsCache.extendCacheWithSingle(intr: Ctor) {
if (nonParentHint != null && nonParentHint === iface) { implementInterfaceMemo[intr.getOrDefineInterfaceId()] = true
return false }
}
for (i in interfaces) { private fun fastGetPrototype(ctor: Ctor): Prototype? {
if (isInterfaceImpl(i, iface)) { return ctor.`$metadata$`?.run {
return true
}
}
if (fastPrototype == null) { if (fastPrototype == null) {
fastPrototype = ctor.getPrototype() fastPrototype = ctor.getPrototype()
} }
fastPrototype fastPrototype
} ?: ctor.getPrototype()
}
private fun completeInterfaceCache(ctor: Ctor): IsImplementsCache? {
val metadata = ctor.`$metadata$`
if (metadata != null && metadata.interfacesCache == null) {
metadata.interfacesCache = generateInterfaceCache()
} }
(superPrototype ?: ctor.getPrototype())?.constructor?.let { val interfacesCache = metadata?.interfacesCache
if (isInterfaceImpl(it, iface)) {
return true if (interfacesCache != null) {
if (interfacesCache.isComplete == true) {
return interfacesCache
}
for (i in metadata.interfaces) {
interfacesCache.extendCacheWithSingle(i)
interfacesCache.extendCacheWith(completeInterfaceCache(i))
} }
ctor.`$metadata$`?.run { nonParentHint = iface }
} }
return false
val constructor = fastGetPrototype(ctor)?.constructor ?: return null
val parentInterfacesCache = completeInterfaceCache(constructor) ?: return interfacesCache
if (interfacesCache == null) return parentInterfacesCache
interfacesCache.extendCacheWith(parentInterfacesCache)
interfacesCache.isComplete = true
return interfacesCache
}
// Old JS Backend
internal fun generateInterfaceCache(): IsImplementsCache {
return js("{ isComplete: false, implementInterfaceMemo: {} }")
}
private fun isInterfaceImpl(ctor: Ctor, iface: Ctor): Boolean {
if (ctor === iface) {
return true
}
val metadata = ctor.`$metadata$`
if (metadata != null && metadata.interfacesCache == null) {
metadata.interfacesCache = generateInterfaceCache()
}
val interfacesCache = metadata?.interfacesCache
return if (interfacesCache != null) {
if (!interfacesCache.isComplete) completeInterfaceCache(ctor)
val interfaceId = iface.`$metadata$`?.interfaceId ?: return false
!!interfacesCache.implementInterfaceMemo[interfaceId]
} else {
val constructor = fastGetPrototype(ctor)?.constructor ?: return false
isInterfaceImpl(constructor, iface)
}
} }
internal fun isInterface(obj: dynamic, iface: dynamic): Boolean { internal fun isInterface(obj: dynamic, iface: dynamic): Boolean {
val ctor = obj.constructor ?: return false val ctor = obj.constructor ?: return false
return isInterfaceImpl(ctor, iface) return isInterfaceImpl(ctor, iface)
} }
@@ -9,8 +9,8 @@ import kotlin.reflect.js.internal.*
@PublishedApi @PublishedApi
internal fun <T : Annotation> KClass<*>.findAssociatedObject(annotationClass: KClass<T>): Any? { internal fun <T : Annotation> KClass<*>.findAssociatedObject(annotationClass: KClass<T>): Any? {
return if (this is KClassImpl<*> && annotationClass is KClassImpl<T>) { return if (this is KClassImpl<*> && annotationClass is KClassImpl<T>) {
val key = annotationClass.jClass.asDynamic().`$metadata$`?.associatedObjectKey?.unsafeCast<Int>() ?: return null val key = annotationClass.jClass.asDynamic().unsafeCast<Ctor>().`$metadata$`?.associatedObjectKey?.unsafeCast<Int>() ?: return null
val map = this.jClass.asDynamic().`$metadata$`?.associatedObjects ?: return null val map = jClass.asDynamic().unsafeCast<Ctor>().`$metadata$`?.associatedObjects ?: return null
val factory = map[key] ?: return null val factory = map[key] ?: return null
return factory() return factory()
} else { } else {