From 9c7d47789c1923103a58f35902e3fdd31a178002 Mon Sep 17 00:00:00 2001 From: Roman Artemev Date: Thu, 16 May 2019 16:43:44 +0300 Subject: [PATCH] [JS IR BE] Implement type check for `SuspendFunctionN` --- .../src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt | 1 + .../jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt | 1 + .../ir/backend/js/lower/TypeOperatorLowering.kt | 12 ++++++++++++ .../js/transformers/irToJs/JsClassGenerator.kt | 12 ++++++++++++ .../jetbrains/kotlin/ir/backend/js/utils/Namer.kt | 1 + .../featureIntersection/destructuringInLambdas.kt | 1 - .../suspendDestructuringInLambdas.kt | 1 - libraries/stdlib/js-ir/runtime/typeCheckUtils.kt | 12 +++++++++--- 8 files changed, 36 insertions(+), 5 deletions(-) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt index 813be6c5650..b8864b68807 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt @@ -54,6 +54,7 @@ fun IrType.typeParameterSuperTypes(): List { } fun IrType.isFunctionTypeOrSubtype(): Boolean = DFS.ifAny(listOf(this), { it.superTypes() }, { it.isFunction() }) +fun IrType.isSuspendFunctionTypeOrSubtype(): Boolean = DFS.ifAny(listOf(this), { it.superTypes() }, { it.isSuspendFunction() }) fun IrType.isTypeParameter() = classifierOrNull is IrTypeParameterSymbol diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt index 3b4e709686a..76ad4b97a56 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt @@ -118,6 +118,7 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC val isArraySymbol = getInternalFunction("isArray") // val isCharSymbol = getInternalFunction("isChar") val isObjectSymbol = getInternalFunction("isObject") + val isSuspendFunctionSymbol = getInternalFunction("isSuspendFunction") val isNumberSymbol = getInternalFunction("isNumber") val isComparableSymbol = getInternalFunction("isComparable") diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt index c07e9e014c9..4eb4d64f0e7 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt @@ -42,6 +42,7 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass { private val isInterfaceSymbol get() = context.intrinsics.isInterfaceSymbol private val isArraySymbol get() = context.intrinsics.isArraySymbol + private val isSuspenfFunctionSymbol = context.intrinsics.isSuspendFunctionSymbol // private val isCharSymbol get() = context.intrinsics.isCharSymbol private val isObjectSymbol get() = context.intrinsics.isObjectSymbol @@ -209,6 +210,7 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass { return when { toType.isAny() -> generateIsObjectCheck(argument) toType.isNothing() -> JsIrBuilder.buildComposite(context.irBuiltIns.booleanType, listOf(argument, litFalse)) + toType.isSuspendFunctionTypeOrSubtype() -> generateSuspendFunctionCheck(argument, toType) isTypeOfCheckingType(toType) -> generateTypeOfCheck(argument, toType) // toType.isChar() -> generateCheckForChar(argument) toType.isNumber() -> generateNumberCheck(argument) @@ -254,6 +256,16 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass { // private fun generateCheckForChar(argument: IrExpression) = // JsIrBuilder.buildCall(isCharSymbol).apply { dispatchReceiver = argument } + private fun generateSuspendFunctionCheck(argument: IrExpression, toType: IrType): IrExpression { + val arity = (toType.classifierOrFail.owner as IrClass).typeParameters.size - 1 // drop return type + + val irBuiltIns = context.irBuiltIns + return JsIrBuilder.buildCall(isSuspenfFunctionSymbol, irBuiltIns.booleanType).apply { + putValueArgument(0, argument) + putValueArgument(1, JsIrBuilder.buildInt(irBuiltIns.intType, arity)) + } + } + private fun generateTypeOfCheck(argument: IrExpression, toType: IrType): IrExpression { val marker = when { toType.isFunctionOrKFunction() -> functionMarker 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 ee92f4378d6..c53444d2458 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 @@ -240,9 +240,21 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo metadataLiteral.propertyInitializers += JsPropertyInitializer(JsNameRef(Namer.METADATA_CLASS_KIND), classKind) metadataLiteral.propertyInitializers += generateSuperClasses() + + if (isCoroutineClass()) { + metadataLiteral.propertyInitializers += generateSuspendArity() + } + return jsAssignment(JsNameRef(Namer.METADATA, classNameRef), metadataLiteral).makeStmt() } + 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 generateSuperClasses(): JsPropertyInitializer { val functionTypeOrSubtype = irClass.defaultType.isFunctionTypeOrSubtype() return JsPropertyInitializer( diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt index bbe81bf5f01..9b48338276a 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/Namer.kt @@ -35,6 +35,7 @@ object Namer { val METADATA_INTERFACES = "interfaces" val METADATA_SIMPLE_NAME = "simpleName" val METADATA_CLASS_KIND = "kind" + val METADATA_SUSPEND_ARITY = "suspendArity" val KCALLABLE_GET_NAME = "" val KCALLABLE_NAME = "callableName" diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt index 6a7975d281d..c4a4f126565 100644 --- a/compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME // WITH_COROUTINES // COMMON_COROUTINES_TEST diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/suspendDestructuringInLambdas.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/suspendDestructuringInLambdas.kt index 8074777c57e..46cc6d4e0fe 100644 --- a/compiler/testData/codegen/box/coroutines/featureIntersection/suspendDestructuringInLambdas.kt +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/suspendDestructuringInLambdas.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES diff --git a/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt b/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt index f1708583a74..43e41341237 100644 --- a/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt +++ b/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt @@ -7,6 +7,7 @@ package kotlin.js private external interface Metadata { val interfaces: Array + val suspendArity: Int? } private external interface Ctor { @@ -33,9 +34,7 @@ private fun isInterfaceImpl(ctor: Ctor, iface: dynamic): Boolean { } public fun isInterface(obj: dynamic, iface: dynamic): Boolean { - val ctor = obj.constructor - - if (ctor == null) return false + val ctor = obj.constructor ?: return false return isInterfaceImpl(ctor, iface) } @@ -75,6 +74,13 @@ public fun isInterface(ctor: dynamic, IType: dynamic): Boolean { } */ +internal fun isSuspendFunction(obj: dynamic, arity: Int): Boolean { + val ctor = obj.constructor?.unsafeCast() ?: return false + + val metadata = ctor.`$metadata$`?.unsafeCast() ?: return false + + return metadata.suspendArity === arity +} fun isObject(obj: dynamic): Boolean { val objTypeOf = jsTypeOf(obj)