[JS IR BE] Implement type check for SuspendFunctionN

This commit is contained in:
Roman Artemev
2019-05-16 16:43:44 +03:00
committed by romanart
parent 9894c216c1
commit 9c7d47789c
8 changed files with 36 additions and 5 deletions
@@ -54,6 +54,7 @@ fun IrType.typeParameterSuperTypes(): List<IrType> {
} }
fun IrType.isFunctionTypeOrSubtype(): Boolean = DFS.ifAny(listOf(this), { it.superTypes() }, { it.isFunction() }) 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 fun IrType.isTypeParameter() = classifierOrNull is IrTypeParameterSymbol
@@ -118,6 +118,7 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
val isArraySymbol = getInternalFunction("isArray") val isArraySymbol = getInternalFunction("isArray")
// val isCharSymbol = getInternalFunction("isChar") // val isCharSymbol = getInternalFunction("isChar")
val isObjectSymbol = getInternalFunction("isObject") val isObjectSymbol = getInternalFunction("isObject")
val isSuspendFunctionSymbol = getInternalFunction("isSuspendFunction")
val isNumberSymbol = getInternalFunction("isNumber") val isNumberSymbol = getInternalFunction("isNumber")
val isComparableSymbol = getInternalFunction("isComparable") val isComparableSymbol = getInternalFunction("isComparable")
@@ -42,6 +42,7 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
private val isInterfaceSymbol get() = context.intrinsics.isInterfaceSymbol private val isInterfaceSymbol get() = context.intrinsics.isInterfaceSymbol
private val isArraySymbol get() = context.intrinsics.isArraySymbol private val isArraySymbol get() = context.intrinsics.isArraySymbol
private val isSuspenfFunctionSymbol = context.intrinsics.isSuspendFunctionSymbol
// private val isCharSymbol get() = context.intrinsics.isCharSymbol // private val isCharSymbol get() = context.intrinsics.isCharSymbol
private val isObjectSymbol get() = context.intrinsics.isObjectSymbol private val isObjectSymbol get() = context.intrinsics.isObjectSymbol
@@ -209,6 +210,7 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
return when { return when {
toType.isAny() -> generateIsObjectCheck(argument) toType.isAny() -> generateIsObjectCheck(argument)
toType.isNothing() -> JsIrBuilder.buildComposite(context.irBuiltIns.booleanType, listOf(argument, litFalse)) toType.isNothing() -> JsIrBuilder.buildComposite(context.irBuiltIns.booleanType, listOf(argument, litFalse))
toType.isSuspendFunctionTypeOrSubtype() -> generateSuspendFunctionCheck(argument, toType)
isTypeOfCheckingType(toType) -> generateTypeOfCheck(argument, toType) isTypeOfCheckingType(toType) -> generateTypeOfCheck(argument, toType)
// toType.isChar() -> generateCheckForChar(argument) // toType.isChar() -> generateCheckForChar(argument)
toType.isNumber() -> generateNumberCheck(argument) toType.isNumber() -> generateNumberCheck(argument)
@@ -254,6 +256,16 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : FileLoweringPass {
// private fun generateCheckForChar(argument: IrExpression) = // private fun generateCheckForChar(argument: IrExpression) =
// JsIrBuilder.buildCall(isCharSymbol).apply { dispatchReceiver = argument } // 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 { private fun generateTypeOfCheck(argument: IrExpression, toType: IrType): IrExpression {
val marker = when { val marker = when {
toType.isFunctionOrKFunction() -> functionMarker toType.isFunctionOrKFunction() -> functionMarker
@@ -240,9 +240,21 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
metadataLiteral.propertyInitializers += JsPropertyInitializer(JsNameRef(Namer.METADATA_CLASS_KIND), classKind) metadataLiteral.propertyInitializers += JsPropertyInitializer(JsNameRef(Namer.METADATA_CLASS_KIND), classKind)
metadataLiteral.propertyInitializers += generateSuperClasses() metadataLiteral.propertyInitializers += generateSuperClasses()
if (isCoroutineClass()) {
metadataLiteral.propertyInitializers += generateSuspendArity()
}
return jsAssignment(JsNameRef(Namer.METADATA, classNameRef), metadataLiteral).makeStmt() 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<IrSimpleFunction>().first { it.isSuspend }.valueParameters.size
return JsPropertyInitializer(JsNameRef(Namer.METADATA_SUSPEND_ARITY), JsIntLiteral(arity))
}
private fun generateSuperClasses(): JsPropertyInitializer { private fun generateSuperClasses(): JsPropertyInitializer {
val functionTypeOrSubtype = irClass.defaultType.isFunctionTypeOrSubtype() val functionTypeOrSubtype = irClass.defaultType.isFunctionTypeOrSubtype()
return JsPropertyInitializer( return JsPropertyInitializer(
@@ -35,6 +35,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_SUSPEND_ARITY = "suspendArity"
val KCALLABLE_GET_NAME = "<get-name>" val KCALLABLE_GET_NAME = "<get-name>"
val KCALLABLE_NAME = "callableName" val KCALLABLE_NAME = "callableName"
@@ -1,5 +1,4 @@
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
// COMMON_COROUTINES_TEST // COMMON_COROUTINES_TEST
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JVM_IR // IGNORE_BACKEND: JVM_IR
// WITH_RUNTIME // WITH_RUNTIME
// WITH_COROUTINES // WITH_COROUTINES
@@ -7,6 +7,7 @@ package kotlin.js
private external interface Metadata { private external interface Metadata {
val interfaces: Array<Ctor> val interfaces: Array<Ctor>
val suspendArity: Int?
} }
private external interface Ctor { private external interface Ctor {
@@ -33,9 +34,7 @@ private fun isInterfaceImpl(ctor: Ctor, iface: dynamic): Boolean {
} }
public fun isInterface(obj: dynamic, iface: dynamic): Boolean { public fun isInterface(obj: dynamic, iface: dynamic): Boolean {
val ctor = obj.constructor val ctor = obj.constructor ?: return false
if (ctor == null) return false
return isInterfaceImpl(ctor, iface) 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<Ctor>() ?: return false
val metadata = ctor.`$metadata$`?.unsafeCast<Metadata>() ?: return false
return metadata.suspendArity === arity
}
fun isObject(obj: dynamic): Boolean { fun isObject(obj: dynamic): Boolean {
val objTypeOf = jsTypeOf(obj) val objTypeOf = jsTypeOf(obj)