[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 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?
|
||||
|
||||
+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 {
|
||||
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>()
|
||||
|
||||
|
||||
+2
-1
@@ -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 =
|
||||
|
||||
Reference in New Issue
Block a user