[JS IR] Improve performance for runtime typecheck

This is a part of KT-42743
This commit is contained in:
Alexander Korepanov
2021-12-16 18:07:00 +03:00
committed by Space
parent fa951f8f9c
commit 769fbcf3d0
3 changed files with 45 additions and 19 deletions
@@ -31,7 +31,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
private val baseClass: IrType? = irClass.superTypes.firstOrNull { !it.classifierOrFail.isInterface } private val baseClass: IrType? = irClass.superTypes.firstOrNull { !it.classifierOrFail.isInterface }
private val baseClassRef by lazy { // Lazy in case was not collected by namer during JsClassGenerator construction private val baseClassRef by lazy { // Lazy in case was not collected by namer during JsClassGenerator construction
baseClass?.getClassRef(context) if (baseClass != null && !baseClass.isAny()) baseClass.getClassRef(context) else null
} }
private val classPrototypeRef = prototypeOf(classNameRef) private val classPrototypeRef = prototypeOf(classNameRef)
private val classBlock = JsGlobalBlock() private val classBlock = JsGlobalBlock()
@@ -49,11 +49,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
// We'll use IrSimpleFunction::correspondingProperty to collect them into set // We'll use IrSimpleFunction::correspondingProperty to collect them into set
val properties = mutableSetOf<IrProperty>() val properties = mutableSetOf<IrProperty>()
val jsClass = JsClass(name = className) 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()
@@ -329,12 +325,10 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
} }
private fun generateInheritanceCode(): List<JsStatement> { private fun generateInheritanceCode(): List<JsStatement> {
if (baseClass == null || baseClass.isAny()) { val baseClassPrototype = baseClassRef ?: return emptyList()
return emptyList()
}
val createCall = jsAssignment( val createCall = jsAssignment(
classPrototypeRef, JsInvocation(Namer.JS_OBJECT_CREATE_FUNCTION, prototypeOf(baseClassRef!!)) classPrototypeRef, JsInvocation(Namer.JS_OBJECT_CREATE_FUNCTION, prototypeOf(baseClassPrototype))
).makeStmt() ).makeStmt()
val ctorAssign = jsAssignment(JsNameRef(Namer.CONSTRUCTOR_NAME, classPrototypeRef), classNameRef).makeStmt() val ctorAssign = jsAssignment(JsNameRef(Namer.CONSTRUCTOR_NAME, classPrototypeRef), classNameRef).makeStmt()
@@ -364,6 +358,8 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
metadataLiteral.propertyInitializers += generateAssociatedKeyProperties() metadataLiteral.propertyInitializers += generateAssociatedKeyProperties()
generateFastPrototype()?.let { metadataLiteral.propertyInitializers += it }
if (isCoroutineClass()) { if (isCoroutineClass()) {
metadataLiteral.propertyInitializers += generateSuspendArity() metadataLiteral.propertyInitializers += generateSuspendArity()
} }
@@ -403,6 +399,10 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
) )
} }
private fun generateFastPrototype() = baseClassRef?.let {
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 generateAssociatedKeyProperties(): List<JsPropertyInitializer> {
@@ -34,6 +34,7 @@ object Namer {
val METADATA_INTERFACES = "interfaces" val METADATA_INTERFACES = "interfaces"
val METADATA_SIMPLE_NAME = "simpleName" val METADATA_SIMPLE_NAME = "simpleName"
val METADATA_CLASS_KIND = "kind" val METADATA_CLASS_KIND = "kind"
val METADATA_FAST_PROTOTYPE = "fastPrototype"
val METADATA_SUSPEND_ARITY = "suspendArity" val METADATA_SUSPEND_ARITY = "suspendArity"
val KCALLABLE_GET_NAME = "<get-name>" val KCALLABLE_GET_NAME = "<get-name>"
@@ -8,29 +8,54 @@ package kotlin.js
private external interface Metadata { private external interface Metadata {
val interfaces: Array<Ctor> val interfaces: Array<Ctor>
val suspendArity: Array<Int>? val suspendArity: Array<Int>?
// 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
var fastPrototype: Prototype?
// 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 { private external interface Ctor {
val `$metadata$`: Metadata? val `$metadata$`: Metadata?
val prototype: Ctor? val prototype: Prototype?
} }
private fun isInterfaceImpl(ctor: Ctor, iface: dynamic): Boolean { private external interface Prototype {
if (ctor === iface) return true val constructor: Ctor?
}
val metadata = ctor.`$metadata$` private fun Ctor.getPrototype() = prototype?.let { js("Object").getPrototypeOf(it).unsafeCast<Prototype>() }
if (metadata != null) {
val interfaces = metadata.interfaces private fun isInterfaceImpl(ctor: Ctor, iface: dynamic): Boolean {
if (ctor === iface) {
return true
}
val superPrototype = ctor.`$metadata$`?.run {
if (nonParentHint != null && nonParentHint === iface) {
return false
}
for (i in interfaces) { for (i in interfaces) {
if (isInterfaceImpl(i, iface)) { if (isInterfaceImpl(i, iface)) {
return true return true
} }
} }
if (fastPrototype == null) {
fastPrototype = ctor.getPrototype()
}
fastPrototype
} }
val superPrototype = if (ctor.prototype != null) js("Object").getPrototypeOf(ctor.prototype) else null (superPrototype ?: ctor.getPrototype())?.constructor?.let {
val superConstructor: Ctor? = if (superPrototype != null) superPrototype.constructor else null if (isInterfaceImpl(it, iface)) {
return superConstructor != null && isInterfaceImpl(superConstructor, iface) return true
}
ctor.`$metadata$`?.run { nonParentHint = iface }
}
return false
} }
internal fun isInterface(obj: dynamic, iface: dynamic): Boolean { internal fun isInterface(obj: dynamic, iface: dynamic): Boolean {