From 3c9dcdbbeef72c29b04d6881bc7db47438331b43 Mon Sep 17 00:00:00 2001 From: Ilya Goncharov Date: Mon, 26 Jul 2021 15:00:24 +0300 Subject: [PATCH] [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 --- .../ir/backend/js/JsIrBackendContext.kt | 2 ++ .../kotlin/ir/backend/js/JsLoweringPhases.kt | 10 +++++- .../coroutines/JsSuspendArityStoreLowering.kt | 29 +++++++++++++++++ .../transformers/irToJs/JsClassGenerator.kt | 15 ++++++--- .../js/transformers/irToJs/jsAstUtils.kt | 3 +- .../stdlib/js-ir/runtime/typeCheckUtils.kt | 31 +++++++++++++++---- 6 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/JsSuspendArityStoreLowering.kt diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt index ad528e344ee..373dd71c921 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt @@ -65,6 +65,8 @@ class JsIrBackendContext( val fileToInitializationFuns: MutableMap = mutableMapOf() val fileToInitializerPureness: MutableMap = mutableMapOf() + val suspendArityStore: MutableMap> = mutableMapOf() + val extractedLocalClasses: MutableSet = hashSetOf() override val builtIns = module.builtIns diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt index f0e46ddcbb3..fbe1a0ee832 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt @@ -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( scriptRemoveReceiverLowering, validateIrBeforeLowering, @@ -818,7 +825,8 @@ private val loweringList = listOf( captureStackTraceInThrowablesPhase, callsLoweringPhase, cleanupLoweringPhase, - validateIrAfterLowering + validateIrAfterLowering, + jsSuspendArityStorePhase ) // TODO comment? Eliminate ModuleLowering's? Don't filter them here? diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/JsSuspendArityStoreLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/JsSuspendArityStoreLowering.kt new file mode 100644 index 00000000000..256659a46ce --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/coroutines/JsSuspendArityStoreLowering.kt @@ -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? { + if (declaration !is IrClass) return null + + declaration.declarations + .filterIsInstance() + .filter { it.isSuspend } + .map { it.valueParameters.size } + .let { + context.suspendArityStore[declaration] = it.toSet() + } + + return null + } +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt index 1df2d0b5b2d..d3de84ab5a0 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/JsClassGenerator.kt @@ -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().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 { var result = emptyList() diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt index ac7df5a6eaf..6aee5018202 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/transformers/irToJs/jsAstUtils.kt @@ -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( diff --git a/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt b/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt index af031531ce5..e4b9b0ed093 100644 --- a/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt +++ b/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt @@ -7,7 +7,7 @@ package kotlin.js private external interface Metadata { val interfaces: Array - val suspendArity: Int? + val suspendArity: Array? } private external interface Ctor { @@ -80,6 +80,25 @@ internal fun isSuspendFunction(obj: dynamic, arity: Int): Boolean { return obj.`$arity`.unsafeCast() === arity } + if (jsTypeOf(obj) == "object" && jsIn("${'$'}metadata${'$'}", obj.constructor)) { + @Suppress("IMPLICIT_BOXING_IN_IDENTITY_EQUALS") + return obj.constructor.unsafeCast().`$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() } -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() @@ -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 =