[JS IR] Generate suspend function as invoke

[JS IR] Fix type check utils to work with array of arities

[JS IR] Store multiple arities for suspend functional interface implementers

^KT-46204 fixed
This commit is contained in:
Ilya Goncharov
2021-07-26 15:00:24 +03:00
committed by Space
parent 8a812996dd
commit 3c9dcdbbee
6 changed files with 77 additions and 13 deletions
@@ -65,6 +65,8 @@ class JsIrBackendContext(
val fileToInitializationFuns: MutableMap<IrFile, IrSimpleFunction?> = mutableMapOf()
val fileToInitializerPureness: MutableMap<IrFile, Boolean> = mutableMapOf()
val suspendArityStore: MutableMap<IrDeclaration, Set<Int>> = mutableMapOf()
val extractedLocalClasses: MutableSet<IrClass> = hashSetOf()
override val builtIns = module.builtIns
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.backend.js.lower.*
import org.jetbrains.kotlin.ir.backend.js.lower.calls.CallsLowering
import org.jetbrains.kotlin.ir.backend.js.lower.cleanup.CleanupLowering
import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.JsSuspendArityStoreLowering
import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.JsSuspendFunctionsLowering
import org.jetbrains.kotlin.ir.backend.js.lower.inline.CopyInlineFunctionBodyLowering
import org.jetbrains.kotlin.ir.backend.js.lower.inline.RemoveInlineDeclarationsWithReifiedTypeParametersLowering
@@ -728,6 +729,12 @@ private val cleanupLoweringPhase = makeBodyLoweringPhase(
description = "Clean up IR before codegen"
)
private val jsSuspendArityStorePhase = makeDeclarationTransformerPhase(
::JsSuspendArityStoreLowering,
name = "JsSuspendArityStoreLowering",
description = "Store arity for suspend functions to not remove it during DCE"
)
private val loweringList = listOf<Lowering>(
scriptRemoveReceiverLowering,
validateIrBeforeLowering,
@@ -818,7 +825,8 @@ private val loweringList = listOf<Lowering>(
captureStackTraceInThrowablesPhase,
callsLoweringPhase,
cleanupLoweringPhase,
validateIrAfterLowering
validateIrAfterLowering,
jsSuspendArityStorePhase
)
// TODO comment? Eliminate ModuleLowering's? Don't filter them here?
@@ -0,0 +1,29 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.ir.backend.js.lower.coroutines
import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import kotlin.collections.set
class JsSuspendArityStoreLowering(private val context: JsIrBackendContext) : DeclarationTransformer {
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
if (declaration !is IrClass) return null
declaration.declarations
.filterIsInstance<IrSimpleFunction>()
.filter { it.isSuspend }
.map { it.valueParameters.size }
.let {
context.suspendArityStore[declaration] = it.toSet()
}
return null
}
}
@@ -165,7 +165,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
declaration.realOverrideTarget.let {
val implClassDeclaration = it.parent as IrClass
if (implClassDeclaration.shouldCopyFrom()) {
if (implClassDeclaration.shouldCopyFrom() && !implClassDeclaration.defaultType.isFunctionType()) {
val implMethodName = context.getNameForMemberFunction(it)
val implClassName = context.getNameForClass(implClassDeclaration)
@@ -234,9 +234,12 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
private fun isCoroutineClass(): Boolean = irClass.superTypes.any { it.isSuspendFunctionTypeOrSubtype() }
private fun generateSuspendArity(): JsPropertyInitializer {
val arity = irClass.declarations.filterIsInstance<IrSimpleFunction>().first { it.isSuspend }.valueParameters.size
return JsPropertyInitializer(JsNameRef(Namer.METADATA_SUSPEND_ARITY), JsIntLiteral(arity))
private fun generateSuspendArity(): JsPropertyInitializer? {
val arity = context.staticContext.backendContext.suspendArityStore
.getValue(irClass)
.map { JsIntLiteral(it) }
return JsPropertyInitializer(JsNameRef(Namer.METADATA_SUSPEND_ARITY), JsArrayLiteral(arity))
}
private fun generateSuperClasses(): JsPropertyInitializer {
@@ -245,7 +248,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
JsArrayLiteral(
irClass.superTypes.mapNotNull {
val symbol = it.classifierOrFail as IrClassSymbol
val isFunctionType = it.run { isFunctionOrKFunction() || isSuspendFunctionOrKFunction() }
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()
@@ -260,6 +263,8 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
)
}
private fun IrType.isFunctionType() = isFunctionOrKFunction() || isSuspendFunctionOrKFunction()
private fun generateAssociatedKeyProperties(): List<JsPropertyInitializer> {
var result = emptyList<JsPropertyInitializer>()
@@ -90,7 +90,8 @@ private fun isFunctionTypeInvoke(receiver: JsExpression?, call: IrCall): Boolean
if (call.origin === JsStatementOrigins.EXPLICIT_INVOKE) return false
return simpleFunction.name == OperatorNameConventions.INVOKE && receiverType.isFunctionTypeOrSubtype()
return simpleFunction.name == OperatorNameConventions.INVOKE
&& receiverType.isFunctionTypeOrSubtype()
}
fun translateCall(
@@ -7,7 +7,7 @@ package kotlin.js
private external interface Metadata {
val interfaces: Array<Ctor>
val suspendArity: Int?
val suspendArity: Array<Int>?
}
private external interface Ctor {
@@ -80,6 +80,25 @@ internal fun isSuspendFunction(obj: dynamic, arity: Int): Boolean {
return obj.`$arity`.unsafeCast<Int>() === arity
}
if (jsTypeOf(obj) == "object" && jsIn("${'$'}metadata${'$'}", obj.constructor)) {
@Suppress("IMPLICIT_BOXING_IN_IDENTITY_EQUALS")
return obj.constructor.unsafeCast<Ctor>().`$metadata$`?.suspendArity?.let {
val result = js("{result: false}")
js(
"""
for (var i = 0; i < it.length; i++) {
if (arity === it[i]) {
result.result = true;
break;
}
result.result = false;
}
"""
)
return result.result
} ?: false
}
return false
}
@@ -99,11 +118,11 @@ private fun isJsArray(obj: Any): Boolean {
return js("Array").isArray(obj).unsafeCast<Boolean>()
}
internal fun isArray(obj: Any): Boolean {
internal fun isArray(obj: Any): Boolean {
return isJsArray(obj) && !(obj.asDynamic().`$type$`)
}
internal fun isArrayish(o: dynamic) =
internal fun isArrayish(o: dynamic) =
isJsArray(o) || js("ArrayBuffer").isView(o).unsafeCast<Boolean>()
@@ -166,9 +185,9 @@ internal fun isComparable(value: dynamic): Boolean {
var type = jsTypeOf(value)
return type == "string" ||
type == "boolean" ||
isNumber(value) ||
isInterface(value, Comparable::class.js)
type == "boolean" ||
isNumber(value) ||
isInterface(value, Comparable::class.js)
}
internal fun isCharSequence(value: dynamic): Boolean =