[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:
@@ -65,6 +65,8 @@ class JsIrBackendContext(
|
|||||||
val fileToInitializationFuns: MutableMap<IrFile, IrSimpleFunction?> = mutableMapOf()
|
val fileToInitializationFuns: MutableMap<IrFile, IrSimpleFunction?> = mutableMapOf()
|
||||||
val fileToInitializerPureness: MutableMap<IrFile, Boolean> = mutableMapOf()
|
val fileToInitializerPureness: MutableMap<IrFile, Boolean> = mutableMapOf()
|
||||||
|
|
||||||
|
val suspendArityStore: MutableMap<IrDeclaration, Set<Int>> = mutableMapOf()
|
||||||
|
|
||||||
val extractedLocalClasses: MutableSet<IrClass> = hashSetOf()
|
val extractedLocalClasses: MutableSet<IrClass> = hashSetOf()
|
||||||
|
|
||||||
override val builtIns = module.builtIns
|
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.*
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.calls.CallsLowering
|
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.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.coroutines.JsSuspendFunctionsLowering
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.inline.CopyInlineFunctionBodyLowering
|
import org.jetbrains.kotlin.ir.backend.js.lower.inline.CopyInlineFunctionBodyLowering
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.inline.RemoveInlineDeclarationsWithReifiedTypeParametersLowering
|
import org.jetbrains.kotlin.ir.backend.js.lower.inline.RemoveInlineDeclarationsWithReifiedTypeParametersLowering
|
||||||
@@ -728,6 +729,12 @@ private val cleanupLoweringPhase = makeBodyLoweringPhase(
|
|||||||
description = "Clean up IR before codegen"
|
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>(
|
private val loweringList = listOf<Lowering>(
|
||||||
scriptRemoveReceiverLowering,
|
scriptRemoveReceiverLowering,
|
||||||
validateIrBeforeLowering,
|
validateIrBeforeLowering,
|
||||||
@@ -818,7 +825,8 @@ private val loweringList = listOf<Lowering>(
|
|||||||
captureStackTraceInThrowablesPhase,
|
captureStackTraceInThrowablesPhase,
|
||||||
callsLoweringPhase,
|
callsLoweringPhase,
|
||||||
cleanupLoweringPhase,
|
cleanupLoweringPhase,
|
||||||
validateIrAfterLowering
|
validateIrAfterLowering,
|
||||||
|
jsSuspendArityStorePhase
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO comment? Eliminate ModuleLowering's? Don't filter them here?
|
// TODO comment? Eliminate ModuleLowering's? Don't filter them here?
|
||||||
|
|||||||
+29
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
-5
@@ -165,7 +165,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
|||||||
declaration.realOverrideTarget.let {
|
declaration.realOverrideTarget.let {
|
||||||
val implClassDeclaration = it.parent as IrClass
|
val implClassDeclaration = it.parent as IrClass
|
||||||
|
|
||||||
if (implClassDeclaration.shouldCopyFrom()) {
|
if (implClassDeclaration.shouldCopyFrom() && !implClassDeclaration.defaultType.isFunctionType()) {
|
||||||
val implMethodName = context.getNameForMemberFunction(it)
|
val implMethodName = context.getNameForMemberFunction(it)
|
||||||
val implClassName = context.getNameForClass(implClassDeclaration)
|
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 isCoroutineClass(): Boolean = irClass.superTypes.any { it.isSuspendFunctionTypeOrSubtype() }
|
||||||
|
|
||||||
private fun generateSuspendArity(): JsPropertyInitializer {
|
private fun generateSuspendArity(): JsPropertyInitializer? {
|
||||||
val arity = irClass.declarations.filterIsInstance<IrSimpleFunction>().first { it.isSuspend }.valueParameters.size
|
val arity = context.staticContext.backendContext.suspendArityStore
|
||||||
return JsPropertyInitializer(JsNameRef(Namer.METADATA_SUSPEND_ARITY), JsIntLiteral(arity))
|
.getValue(irClass)
|
||||||
|
.map { JsIntLiteral(it) }
|
||||||
|
|
||||||
|
return JsPropertyInitializer(JsNameRef(Namer.METADATA_SUSPEND_ARITY), JsArrayLiteral(arity))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateSuperClasses(): JsPropertyInitializer {
|
private fun generateSuperClasses(): JsPropertyInitializer {
|
||||||
@@ -245,7 +248,7 @@ class JsClassGenerator(private val irClass: IrClass, val context: JsGenerationCo
|
|||||||
JsArrayLiteral(
|
JsArrayLiteral(
|
||||||
irClass.superTypes.mapNotNull {
|
irClass.superTypes.mapNotNull {
|
||||||
val symbol = it.classifierOrFail as IrClassSymbol
|
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
|
// 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)
|
val requireInMetadata = if (context.staticContext.backendContext.baseClassIntoMetadata)
|
||||||
!it.isAny()
|
!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> {
|
private fun generateAssociatedKeyProperties(): List<JsPropertyInitializer> {
|
||||||
var result = emptyList<JsPropertyInitializer>()
|
var result = emptyList<JsPropertyInitializer>()
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -90,7 +90,8 @@ private fun isFunctionTypeInvoke(receiver: JsExpression?, call: IrCall): Boolean
|
|||||||
|
|
||||||
if (call.origin === JsStatementOrigins.EXPLICIT_INVOKE) return false
|
if (call.origin === JsStatementOrigins.EXPLICIT_INVOKE) return false
|
||||||
|
|
||||||
return simpleFunction.name == OperatorNameConventions.INVOKE && receiverType.isFunctionTypeOrSubtype()
|
return simpleFunction.name == OperatorNameConventions.INVOKE
|
||||||
|
&& receiverType.isFunctionTypeOrSubtype()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun translateCall(
|
fun translateCall(
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ package kotlin.js
|
|||||||
|
|
||||||
private external interface Metadata {
|
private external interface Metadata {
|
||||||
val interfaces: Array<Ctor>
|
val interfaces: Array<Ctor>
|
||||||
val suspendArity: Int?
|
val suspendArity: Array<Int>?
|
||||||
}
|
}
|
||||||
|
|
||||||
private external interface Ctor {
|
private external interface Ctor {
|
||||||
@@ -80,6 +80,25 @@ internal fun isSuspendFunction(obj: dynamic, arity: Int): Boolean {
|
|||||||
return obj.`$arity`.unsafeCast<Int>() === arity
|
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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,11 +118,11 @@ private fun isJsArray(obj: Any): Boolean {
|
|||||||
return js("Array").isArray(obj).unsafeCast<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$`)
|
return isJsArray(obj) && !(obj.asDynamic().`$type$`)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun isArrayish(o: dynamic) =
|
internal fun isArrayish(o: dynamic) =
|
||||||
isJsArray(o) || js("ArrayBuffer").isView(o).unsafeCast<Boolean>()
|
isJsArray(o) || js("ArrayBuffer").isView(o).unsafeCast<Boolean>()
|
||||||
|
|
||||||
|
|
||||||
@@ -166,9 +185,9 @@ internal fun isComparable(value: dynamic): Boolean {
|
|||||||
var type = jsTypeOf(value)
|
var type = jsTypeOf(value)
|
||||||
|
|
||||||
return type == "string" ||
|
return type == "string" ||
|
||||||
type == "boolean" ||
|
type == "boolean" ||
|
||||||
isNumber(value) ||
|
isNumber(value) ||
|
||||||
isInterface(value, Comparable::class.js)
|
isInterface(value, Comparable::class.js)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun isCharSequence(value: dynamic): Boolean =
|
internal fun isCharSequence(value: dynamic): Boolean =
|
||||||
|
|||||||
Reference in New Issue
Block a user