feat(JS/IR isInterfaceImpl perf): improve isInterfaceImpl performance by memoize an result.
This commit is contained in:
@@ -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")
|
||||||
|
|||||||
+17
-3
@@ -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
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -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")
|
||||||
|
|||||||
+55
-58
@@ -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
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user