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.ir.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsName
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
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.types.isLong
|
||||
import org.jetbrains.kotlin.ir.util.constructors
|
||||
@@ -105,7 +107,6 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
|
||||
|
||||
|
||||
// RTTI:
|
||||
|
||||
val isInterfaceSymbol = getInternalFunction("isInterface")
|
||||
val isArraySymbol = getInternalFunction("isArray")
|
||||
// val isCharSymbol = getInternalFunction("isChar")
|
||||
@@ -178,10 +179,15 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
|
||||
val jsNumberRangeToLong = getInternalFunction("numberRangeToLong")
|
||||
|
||||
val longClassSymbol = getInternalClassWithoutPackage("kotlin.Long")
|
||||
|
||||
val promiseClassSymbol: IrClassSymbol by context.lazy2 {
|
||||
getInternalClassWithoutPackage("kotlin.js.Promise")
|
||||
}
|
||||
|
||||
val metadataInterfaceConstructorSymbol = getInternalFunction("interfaceMeta")
|
||||
val metadataObjectConstructorSymbol = getInternalFunction("objectMeta")
|
||||
val metadataClassConstructorSymbol = getInternalFunction("classMeta")
|
||||
|
||||
val longToDouble = context.symbolTable.referenceSimpleFunction(
|
||||
context.getClass(FqName("kotlin.Long")).unsubstitutedMemberScope.findSingleFunction(
|
||||
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.JsStatementOrigins
|
||||
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.getJsName
|
||||
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.classifierOrNull
|
||||
import org.jetbrains.kotlin.ir.types.getClass
|
||||
import org.jetbrains.kotlin.ir.util.constructedClass
|
||||
import org.jetbrains.kotlin.ir.util.constructors
|
||||
import org.jetbrains.kotlin.ir.util.primaryConstructor
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
|
||||
internal class JsUsefulDeclarationProcessor(
|
||||
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) {
|
||||
if (declaration in result) return
|
||||
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ private val BODILESS_BUILTIN_CLASSES = listOf(
|
||||
"kotlin.Function"
|
||||
).map { FqName(it) }.toSet()
|
||||
|
||||
private fun isBuiltInClass(declaration: IrDeclaration): Boolean =
|
||||
fun isBuiltInClass(declaration: IrDeclaration): Boolean =
|
||||
declaration is IrClass && declaration.fqNameWhenAvailable in BODILESS_BUILTIN_CLASSES
|
||||
|
||||
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)
|
||||
|
||||
if (baseClass != null && !baseClass.isAny()) {
|
||||
jsClass.baseClass = baseClassRef
|
||||
}
|
||||
|
||||
if (es6mode) classModel.preDeclarationBlock.statements += jsClass.makeStmt()
|
||||
|
||||
for (declaration in irClass.declarations) {
|
||||
@@ -337,81 +341,76 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
||||
}
|
||||
|
||||
private fun generateClassMetadata(): JsStatement {
|
||||
val metadataLiteral = JsObjectLiteral(true)
|
||||
val simpleName = irClass.name
|
||||
|
||||
if (!simpleName.isSpecial) {
|
||||
val simpleNameProp = JsPropertyInitializer(JsNameRef(Namer.METADATA_SIMPLE_NAME), JsStringLiteral(simpleName.identifier))
|
||||
metadataLiteral.propertyInitializers += simpleNameProp
|
||||
}
|
||||
|
||||
val classKind = JsStringLiteral(
|
||||
val metadataConstructor = with(context.staticContext.backendContext.intrinsics) {
|
||||
when {
|
||||
irClass.isInterface -> "interface"
|
||||
irClass.isObject -> "object"
|
||||
else -> "class"
|
||||
irClass.isInterface -> metadataInterfaceConstructorSymbol
|
||||
irClass.isObject -> metadataObjectConstructorSymbol
|
||||
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 generateSuspendArity(): JsPropertyInitializer {
|
||||
private fun generateSuspendArity(): JsArrayLiteral? {
|
||||
if (!isCoroutineClass()) return null
|
||||
|
||||
val arity = context.staticContext.backendContext.mapping.suspendArityStore[irClass]!!
|
||||
.map { it.valueParameters.size }
|
||||
.distinct()
|
||||
.map { JsIntLiteral(it) }
|
||||
|
||||
return JsPropertyInitializer(JsNameRef(Namer.METADATA_SUSPEND_ARITY), JsArrayLiteral(arity))
|
||||
return JsArrayLiteral(arity)
|
||||
}
|
||||
|
||||
private fun generateSuperClasses(): JsPropertyInitializer {
|
||||
return JsPropertyInitializer(
|
||||
JsNameRef(Namer.METADATA_INTERFACES),
|
||||
JsArrayLiteral(
|
||||
irClass.superTypes.mapNotNull {
|
||||
val symbol = it.classifierOrFail as IrClassSymbol
|
||||
val isFunctionType = it.isFunctionType()
|
||||
// TODO: make sure that there is a test which breaks when isExternal is used here instead of isEffectivelyExternal
|
||||
val requireInMetadata = if (context.staticContext.backendContext.baseClassIntoMetadata)
|
||||
!it.isAny()
|
||||
else
|
||||
symbol.isInterface
|
||||
private fun generateSuperClasses(): JsArrayLiteral? {
|
||||
val parentSymbols = irClass.superTypes.mapNotNull {
|
||||
val symbol = it.classifierOrFail as IrClassSymbol
|
||||
val isFunctionType = it.isFunctionType()
|
||||
// TODO: make sure that there is a test which breaks when isExternal is used here instead of isEffectivelyExternal
|
||||
val requireInMetadata = if (context.staticContext.backendContext.baseClassIntoMetadata)
|
||||
!it.isAny()
|
||||
else
|
||||
symbol.isInterface
|
||||
|
||||
if (requireInMetadata && !isFunctionType && !symbol.isEffectivelyExternal) {
|
||||
JsNameRef(context.getNameForClass(symbol.owner))
|
||||
} else null
|
||||
}
|
||||
)
|
||||
)
|
||||
if (requireInMetadata && !isFunctionType && !symbol.isEffectivelyExternal) {
|
||||
symbol
|
||||
} else null
|
||||
}
|
||||
|
||||
return parentSymbols
|
||||
.takeIf { it.isNotEmpty() }
|
||||
?.run { JsArrayLiteral(map { JsNameRef(context.getNameForClass(it.owner)) }) }
|
||||
}
|
||||
|
||||
private fun generateFastPrototype() = baseClassRef?.let {
|
||||
JsPropertyInitializer(JsNameRef(Namer.METADATA_FAST_PROTOTYPE), prototypeOf(it))
|
||||
}
|
||||
private fun generateFastPrototype() = baseClassRef?.let { prototypeOf(it) }
|
||||
|
||||
private fun IrType.isFunctionType() = isFunctionOrKFunction() || isSuspendFunctionOrKFunction()
|
||||
|
||||
private fun generateAssociatedKeyProperties(): List<JsPropertyInitializer> {
|
||||
var result = emptyList<JsPropertyInitializer>()
|
||||
|
||||
context.getAssociatedObjectKey(irClass)?.let { key ->
|
||||
result = result + JsPropertyInitializer(JsStringLiteral("associatedObjectKey"), JsIntLiteral(key))
|
||||
}
|
||||
private fun generateAssociatedObjectKey(): JsIntLiteral? {
|
||||
return context.getAssociatedObjectKey(irClass)?.let { JsIntLiteral(it) }
|
||||
}
|
||||
|
||||
private fun generateAssociatedObjects(): JsObjectLiteral? {
|
||||
val associatedObjects = irClass.annotations.mapNotNull { annotation ->
|
||||
val annotationClass = annotation.symbol.owner.constructedClass
|
||||
context.getAssociatedObjectKey(annotationClass)?.let { key ->
|
||||
@@ -423,11 +422,9 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
||||
}
|
||||
}
|
||||
|
||||
if (associatedObjects.isNotEmpty()) {
|
||||
result = result + JsPropertyInitializer(JsStringLiteral("associatedObjects"), JsObjectLiteral(associatedObjects))
|
||||
}
|
||||
|
||||
return result
|
||||
return associatedObjects
|
||||
.takeIf { it.isNotEmpty() }
|
||||
?.let { JsObjectLiteral(it) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,14 +28,11 @@ object Namer {
|
||||
val JS_ERROR = JsNameRef("Error")
|
||||
|
||||
val JS_OBJECT = JsNameRef("Object")
|
||||
val JS_UNDEFINED = JsNameRef("undefined")
|
||||
val JS_OBJECT_CREATE_FUNCTION = JsNameRef("create", JS_OBJECT)
|
||||
|
||||
val METADATA = "\$metadata\$"
|
||||
val METADATA_INTERFACES = "interfaces"
|
||||
val METADATA_SIMPLE_NAME = "simpleName"
|
||||
val METADATA_CLASS_KIND = "kind"
|
||||
val METADATA_FAST_PROTOTYPE = "fastPrototype"
|
||||
val METADATA_SUSPEND_ARITY = "suspendArity"
|
||||
val METADATA_INTERFACE_ID = "interfaceId"
|
||||
|
||||
val KCALLABLE_GET_NAME = "<get-name>"
|
||||
val KCALLABLE_NAME = "callableName"
|
||||
|
||||
Reference in New Issue
Block a user