[Wasm] Support coroutines
- Reuse JS IR Suspend function lowering
- Fix types
- Disable reinterpretCast-based optimization for inline
classes
- Add basic support to Wasm stdlib
- Explicitly transform suspend functions into regular functions
with continuations
- Clean suspend function handling from JS IR codegen
This commit is contained in:
+1
@@ -18,6 +18,7 @@
|
|||||||
<w>sqrt</w>
|
<w>sqrt</w>
|
||||||
<w>testsuite</w>
|
<w>testsuite</w>
|
||||||
<w>uninstantiable</w>
|
<w>uninstantiable</w>
|
||||||
|
<w>unintercepted</w>
|
||||||
<w>unlinkable</w>
|
<w>unlinkable</w>
|
||||||
<w>vtable</w>
|
<w>vtable</w>
|
||||||
<w>wabt</w>
|
<w>wabt</w>
|
||||||
|
|||||||
@@ -94,9 +94,10 @@ private fun buildRoots(modules: Iterable<IrModuleFragment>, context: JsIrBackend
|
|||||||
rootDeclarations += dceRuntimeDiagnostic.unreachableDeclarationMethod(context).owner
|
rootDeclarations += dceRuntimeDiagnostic.unreachableDeclarationMethod(context).owner
|
||||||
}
|
}
|
||||||
|
|
||||||
JsMainFunctionDetector.getMainFunctionOrNull(modules.last())?.let { mainFunction ->
|
// TODO: Generate calls to main as IR->IR lowering and reference coroutineEmptyContinuation directly
|
||||||
|
JsMainFunctionDetector(context).getMainFunctionOrNull(modules.last())?.let { mainFunction ->
|
||||||
rootDeclarations += mainFunction
|
rootDeclarations += mainFunction
|
||||||
if (mainFunction.isSuspend) {
|
if (mainFunction.isLoweredSuspendFunction(context)) {
|
||||||
rootDeclarations += context.coroutineEmptyContinuation.owner
|
rootDeclarations += context.coroutineEmptyContinuation.owner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+98
-1
@@ -5,22 +5,119 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.ir.backend.js
|
package org.jetbrains.kotlin.ir.backend.js
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.common.BackendContext
|
||||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||||
|
import org.jetbrains.kotlin.backend.common.atMostOne
|
||||||
|
import org.jetbrains.kotlin.backend.common.ir.Symbols
|
||||||
import org.jetbrains.kotlin.backend.common.ir.isOverridableOrOverrides
|
import org.jetbrains.kotlin.backend.common.ir.isOverridableOrOverrides
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||||
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
import org.jetbrains.kotlin.ir.backend.js.utils.isDispatchReceiver
|
import org.jetbrains.kotlin.ir.backend.js.utils.isDispatchReceiver
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
|
import org.jetbrains.kotlin.ir.util.SymbolTable
|
||||||
|
import org.jetbrains.kotlin.ir.util.getPropertyGetter
|
||||||
|
import org.jetbrains.kotlin.ir.util.getPropertySetter
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||||
|
|
||||||
interface JsCommonBackendContext : CommonBackendContext {
|
interface JsCommonBackendContext : CommonBackendContext {
|
||||||
override val mapping: JsMapping
|
override val mapping: JsMapping
|
||||||
|
|
||||||
val inlineClassesUtils: InlineClassesUtils
|
val inlineClassesUtils: InlineClassesUtils
|
||||||
|
|
||||||
|
val coroutineSymbols: JsCommonCoroutineSymbols
|
||||||
|
|
||||||
|
val catchAllThrowableType: IrType
|
||||||
|
get() = irBuiltIns.throwableType
|
||||||
|
|
||||||
val es6mode: Boolean
|
val es6mode: Boolean
|
||||||
get() = false
|
get() = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: investigate if it could be removed
|
||||||
|
internal fun <T> BackendContext.lazy2(fn: () -> T) = lazy { irFactory.stageController.withInitialIr(fn) }
|
||||||
|
|
||||||
|
class JsCommonCoroutineSymbols(
|
||||||
|
symbolTable: SymbolTable,
|
||||||
|
module: ModuleDescriptor,
|
||||||
|
val context: JsCommonBackendContext
|
||||||
|
) {
|
||||||
|
val coroutinePackage = module.getPackage(COROUTINE_PACKAGE_FQNAME)
|
||||||
|
val coroutineIntrinsicsPackage = module.getPackage(COROUTINE_INTRINSICS_PACKAGE_FQNAME)
|
||||||
|
|
||||||
|
val coroutineImpl =
|
||||||
|
symbolTable.referenceClass(findClass(coroutinePackage.memberScope, COROUTINE_IMPL_NAME))
|
||||||
|
|
||||||
|
val coroutineImplLabelPropertyGetter by context.lazy2 { coroutineImpl.getPropertyGetter("state")!!.owner }
|
||||||
|
val coroutineImplLabelPropertySetter by context.lazy2 { coroutineImpl.getPropertySetter("state")!!.owner }
|
||||||
|
val coroutineImplResultSymbolGetter by context.lazy2 { coroutineImpl.getPropertyGetter("result")!!.owner }
|
||||||
|
val coroutineImplResultSymbolSetter by context.lazy2 { coroutineImpl.getPropertySetter("result")!!.owner }
|
||||||
|
val coroutineImplExceptionPropertyGetter by context.lazy2 { coroutineImpl.getPropertyGetter("exception")!!.owner }
|
||||||
|
val coroutineImplExceptionPropertySetter by context.lazy2 { coroutineImpl.getPropertySetter("exception")!!.owner }
|
||||||
|
val coroutineImplExceptionStatePropertyGetter by context.lazy2 { coroutineImpl.getPropertyGetter("exceptionState")!!.owner }
|
||||||
|
val coroutineImplExceptionStatePropertySetter by context.lazy2 { coroutineImpl.getPropertySetter("exceptionState")!!.owner }
|
||||||
|
|
||||||
|
val continuationClass = symbolTable.referenceClass(
|
||||||
|
coroutinePackage.memberScope.getContributedClassifier(
|
||||||
|
CONTINUATION_NAME,
|
||||||
|
NoLookupLocation.FROM_BACKEND
|
||||||
|
) as ClassDescriptor
|
||||||
|
)
|
||||||
|
|
||||||
|
val coroutineSuspendedGetter = symbolTable.referenceSimpleFunction(
|
||||||
|
coroutineIntrinsicsPackage.memberScope.getContributedVariables(
|
||||||
|
COROUTINE_SUSPENDED_NAME,
|
||||||
|
NoLookupLocation.FROM_BACKEND
|
||||||
|
).filterNot { it.isExpect }.single().getter!!
|
||||||
|
)
|
||||||
|
|
||||||
|
val coroutineGetContext: IrSimpleFunctionSymbol
|
||||||
|
get() {
|
||||||
|
val contextGetter =
|
||||||
|
continuationClass.owner.declarations.filterIsInstance<IrSimpleFunction>()
|
||||||
|
.atMostOne { it.name == CONTINUATION_CONTEXT_GETTER_NAME }
|
||||||
|
?: continuationClass.owner.declarations.filterIsInstance<IrProperty>()
|
||||||
|
.atMostOne { it.name == CONTINUATION_CONTEXT_PROPERTY_NAME }?.getter!!
|
||||||
|
return contextGetter.symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
val coroutineContextProperty: PropertyDescriptor
|
||||||
|
get() {
|
||||||
|
val vars = coroutinePackage.memberScope.getContributedVariables(
|
||||||
|
COROUTINE_CONTEXT_NAME,
|
||||||
|
NoLookupLocation.FROM_BACKEND
|
||||||
|
)
|
||||||
|
return vars.single()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val INTRINSICS_PACKAGE_NAME = Name.identifier("intrinsics")
|
||||||
|
private val COROUTINE_SUSPENDED_NAME = Name.identifier("COROUTINE_SUSPENDED")
|
||||||
|
private val COROUTINE_CONTEXT_NAME = Name.identifier("coroutineContext")
|
||||||
|
private val COROUTINE_IMPL_NAME = Name.identifier("CoroutineImpl")
|
||||||
|
private val CONTINUATION_NAME = Name.identifier("Continuation")
|
||||||
|
private val CONTINUATION_CONTEXT_GETTER_NAME = Name.special("<get-context>")
|
||||||
|
private val CONTINUATION_CONTEXT_PROPERTY_NAME = Name.identifier("context")
|
||||||
|
private val COROUTINE_PACKAGE_FQNAME = FqName.fromSegments(listOf("kotlin", "coroutines"))
|
||||||
|
private val COROUTINE_INTRINSICS_PACKAGE_FQNAME = COROUTINE_PACKAGE_FQNAME.child(INTRINSICS_PACKAGE_NAME)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun findClass(memberScope: MemberScope, name: Name): ClassDescriptor =
|
||||||
|
memberScope.getContributedClassifier(name, NoLookupLocation.FROM_BACKEND) as ClassDescriptor
|
||||||
|
|
||||||
|
internal fun findFunctions(memberScope: MemberScope, name: Name): List<SimpleFunctionDescriptor> =
|
||||||
|
memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND).toList()
|
||||||
|
|
||||||
interface InlineClassesUtils {
|
interface InlineClassesUtils {
|
||||||
fun isTypeInlined(type: IrType): Boolean
|
fun isTypeInlined(type: IrType): Boolean
|
||||||
|
|
||||||
|
|||||||
+9
-69
@@ -144,23 +144,11 @@ class JsIrBackendContext(
|
|||||||
companion object {
|
companion object {
|
||||||
val KOTLIN_PACKAGE_FQN = FqName.fromSegments(listOf("kotlin"))
|
val KOTLIN_PACKAGE_FQN = FqName.fromSegments(listOf("kotlin"))
|
||||||
|
|
||||||
private val INTRINSICS_PACKAGE_NAME = Name.identifier("intrinsics")
|
|
||||||
private val COROUTINE_SUSPENDED_NAME = Name.identifier("COROUTINE_SUSPENDED")
|
|
||||||
private val COROUTINE_CONTEXT_NAME = Name.identifier("coroutineContext")
|
|
||||||
private val COROUTINE_IMPL_NAME = Name.identifier("CoroutineImpl")
|
|
||||||
private val CONTINUATION_NAME = Name.identifier("Continuation")
|
|
||||||
|
|
||||||
// TODO: what is more clear way reference this getter?
|
// TODO: what is more clear way reference this getter?
|
||||||
private val CONTINUATION_CONTEXT_GETTER_NAME = Name.special("<get-context>")
|
|
||||||
|
|
||||||
private val CONTINUATION_CONTEXT_PROPERTY_NAME = Name.identifier("context")
|
|
||||||
private val REFLECT_PACKAGE_FQNAME = KOTLIN_PACKAGE_FQN.child(Name.identifier("reflect"))
|
private val REFLECT_PACKAGE_FQNAME = KOTLIN_PACKAGE_FQN.child(Name.identifier("reflect"))
|
||||||
private val JS_PACKAGE_FQNAME = KOTLIN_PACKAGE_FQN.child(Name.identifier("js"))
|
private val JS_PACKAGE_FQNAME = KOTLIN_PACKAGE_FQN.child(Name.identifier("js"))
|
||||||
private val JS_INTERNAL_PACKAGE_FQNAME = JS_PACKAGE_FQNAME.child(Name.identifier("internal"))
|
private val JS_INTERNAL_PACKAGE_FQNAME = JS_PACKAGE_FQNAME.child(Name.identifier("internal"))
|
||||||
private val COROUTINE_PACKAGE_FQNAME_12 = FqName.fromSegments(listOf("kotlin", "coroutines", "experimental"))
|
|
||||||
private val COROUTINE_PACKAGE_FQNAME_13 = FqName.fromSegments(listOf("kotlin", "coroutines"))
|
|
||||||
private val COROUTINE_PACKAGE_FQNAME = COROUTINE_PACKAGE_FQNAME_13
|
|
||||||
private val COROUTINE_INTRINSICS_PACKAGE_FQNAME = COROUTINE_PACKAGE_FQNAME.child(INTRINSICS_PACKAGE_NAME)
|
|
||||||
|
|
||||||
// TODO: due to name clash those weird suffix is required, remove it once `MemberNameGenerator` is implemented
|
// TODO: due to name clash those weird suffix is required, remove it once `MemberNameGenerator` is implemented
|
||||||
private val COROUTINE_SUSPEND_OR_RETURN_JS_NAME = "suspendCoroutineUninterceptedOrReturnJS"
|
private val COROUTINE_SUSPEND_OR_RETURN_JS_NAME = "suspendCoroutineUninterceptedOrReturnJS"
|
||||||
@@ -171,12 +159,13 @@ class JsIrBackendContext(
|
|||||||
|
|
||||||
private val internalPackage = module.getPackage(JS_PACKAGE_FQNAME)
|
private val internalPackage = module.getPackage(JS_PACKAGE_FQNAME)
|
||||||
|
|
||||||
private val coroutinePackage = module.getPackage(COROUTINE_PACKAGE_FQNAME)
|
|
||||||
private val coroutineIntrinsicsPackage = module.getPackage(COROUTINE_INTRINSICS_PACKAGE_FQNAME)
|
|
||||||
|
|
||||||
val dynamicType: IrDynamicType = IrDynamicTypeImpl(null, emptyList(), Variance.INVARIANT)
|
val dynamicType: IrDynamicType = IrDynamicTypeImpl(null, emptyList(), Variance.INVARIANT)
|
||||||
val intrinsics = JsIntrinsics(irBuiltIns, this)
|
val intrinsics = JsIntrinsics(irBuiltIns, this)
|
||||||
|
|
||||||
|
override val catchAllThrowableType: IrType
|
||||||
|
get() = dynamicType
|
||||||
|
|
||||||
override val sharedVariablesManager = JsSharedVariablesManager(this)
|
override val sharedVariablesManager = JsSharedVariablesManager(this)
|
||||||
|
|
||||||
override val internalPackageFqn = JS_PACKAGE_FQNAME
|
override val internalPackageFqn = JS_PACKAGE_FQNAME
|
||||||
@@ -193,6 +182,9 @@ class JsIrBackendContext(
|
|||||||
|
|
||||||
fun getOperatorByName(name: Name, type: IrSimpleType) = operatorMap[name]?.get(type.classifier)
|
fun getOperatorByName(name: Name, type: IrSimpleType) = operatorMap[name]?.get(type.classifier)
|
||||||
|
|
||||||
|
override val coroutineSymbols =
|
||||||
|
JsCommonCoroutineSymbols(symbolTable, module, this)
|
||||||
|
|
||||||
override val ir = object : Ir<JsIrBackendContext>(this, irModuleFragment) {
|
override val ir = object : Ir<JsIrBackendContext>(this, irModuleFragment) {
|
||||||
override val symbols = object : Symbols<JsIrBackendContext>(this@JsIrBackendContext, irBuiltIns, symbolTable) {
|
override val symbols = object : Symbols<JsIrBackendContext>(this@JsIrBackendContext, irBuiltIns, symbolTable) {
|
||||||
override val throwNullPointerException =
|
override val throwNullPointerException =
|
||||||
@@ -220,14 +212,9 @@ class JsIrBackendContext(
|
|||||||
override val stringBuilder
|
override val stringBuilder
|
||||||
get() = TODO("not implemented")
|
get() = TODO("not implemented")
|
||||||
override val coroutineImpl =
|
override val coroutineImpl =
|
||||||
symbolTable.referenceClass(findClass(coroutinePackage.memberScope, COROUTINE_IMPL_NAME))
|
coroutineSymbols.coroutineImpl
|
||||||
override val coroutineSuspendedGetter =
|
override val coroutineSuspendedGetter =
|
||||||
symbolTable.referenceSimpleFunction(
|
coroutineSymbols.coroutineSuspendedGetter
|
||||||
coroutineIntrinsicsPackage.memberScope.getContributedVariables(
|
|
||||||
COROUTINE_SUSPENDED_NAME,
|
|
||||||
NoLookupLocation.FROM_BACKEND
|
|
||||||
).filterNot { it.isExpect }.single().getter!!
|
|
||||||
)
|
|
||||||
|
|
||||||
private val _arraysContentEquals = getFunctions(FqName("kotlin.collections.contentEquals")).mapNotNull {
|
private val _arraysContentEquals = getFunctions(FqName("kotlin.collections.contentEquals")).mapNotNull {
|
||||||
if (it.extensionReceiverParameter != null && it.extensionReceiverParameter!!.type.isNullable())
|
if (it.extensionReceiverParameter != null && it.extensionReceiverParameter!!.type.isNullable())
|
||||||
@@ -241,7 +228,7 @@ class JsIrBackendContext(
|
|||||||
|
|
||||||
override val getContinuation = symbolTable.referenceSimpleFunction(getJsInternalFunction("getContinuation"))
|
override val getContinuation = symbolTable.referenceSimpleFunction(getJsInternalFunction("getContinuation"))
|
||||||
|
|
||||||
override val coroutineContextGetter = symbolTable.referenceSimpleFunction(context.coroutineContextProperty.getter!!)
|
override val coroutineContextGetter = symbolTable.referenceSimpleFunction(context.coroutineSymbols.coroutineContextProperty.getter!!)
|
||||||
|
|
||||||
override val suspendCoroutineUninterceptedOrReturn =
|
override val suspendCoroutineUninterceptedOrReturn =
|
||||||
symbolTable.referenceSimpleFunction(getJsInternalFunction(COROUTINE_SUSPEND_OR_RETURN_JS_NAME))
|
symbolTable.referenceSimpleFunction(getJsInternalFunction(COROUTINE_SUSPEND_OR_RETURN_JS_NAME))
|
||||||
@@ -308,31 +295,10 @@ class JsIrBackendContext(
|
|||||||
getIrClass(JS_INTERNAL_PACKAGE_FQNAME.child(Name.identifier("${it.identifier}CompanionObject")))
|
getIrClass(JS_INTERNAL_PACKAGE_FQNAME.child(Name.identifier("${it.identifier}CompanionObject")))
|
||||||
}
|
}
|
||||||
|
|
||||||
val coroutineImpl = ir.symbols.coroutineImpl
|
|
||||||
val continuationClass = symbolTable.referenceClass(
|
|
||||||
coroutinePackage.memberScope.getContributedClassifier(
|
|
||||||
CONTINUATION_NAME,
|
|
||||||
NoLookupLocation.FROM_BACKEND
|
|
||||||
) as ClassDescriptor
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
// Top-level functions forced to be loaded
|
// Top-level functions forced to be loaded
|
||||||
|
|
||||||
val coroutineSuspendOrReturn = ir.symbols.suspendCoroutineUninterceptedOrReturn
|
|
||||||
val coroutineSuspendGetter = ir.symbols.coroutineSuspendedGetter
|
|
||||||
val coroutineGetContext: IrSimpleFunctionSymbol
|
|
||||||
get() {
|
|
||||||
val contextGetter =
|
|
||||||
continuationClass.owner.declarations.filterIsInstance<IrSimpleFunction>()
|
|
||||||
.atMostOne { it.name == CONTINUATION_CONTEXT_GETTER_NAME }
|
|
||||||
?: continuationClass.owner.declarations.filterIsInstance<IrProperty>()
|
|
||||||
.atMostOne { it.name == CONTINUATION_CONTEXT_PROPERTY_NAME }?.getter!!
|
|
||||||
return contextGetter.symbol
|
|
||||||
}
|
|
||||||
|
|
||||||
val coroutineGetContextJs
|
|
||||||
get() = ir.symbols.coroutineGetContext
|
|
||||||
|
|
||||||
val coroutineEmptyContinuation = symbolTable.referenceProperty(
|
val coroutineEmptyContinuation = symbolTable.referenceProperty(
|
||||||
getProperty(
|
getProperty(
|
||||||
@@ -348,14 +314,6 @@ class JsIrBackendContext(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val coroutineContextProperty: PropertyDescriptor
|
|
||||||
get() {
|
|
||||||
val vars = coroutinePackage.memberScope.getContributedVariables(
|
|
||||||
COROUTINE_CONTEXT_NAME,
|
|
||||||
NoLookupLocation.FROM_BACKEND
|
|
||||||
)
|
|
||||||
return vars.single()
|
|
||||||
}
|
|
||||||
|
|
||||||
val newThrowableSymbol = symbolTable.referenceSimpleFunction(getJsInternalFunction("newThrowable"))
|
val newThrowableSymbol = symbolTable.referenceSimpleFunction(getJsInternalFunction("newThrowable"))
|
||||||
val extendThrowableSymbol = symbolTable.referenceSimpleFunction(getJsInternalFunction("extendThrowable"))
|
val extendThrowableSymbol = symbolTable.referenceSimpleFunction(getJsInternalFunction("extendThrowable"))
|
||||||
@@ -368,15 +326,6 @@ class JsIrBackendContext(
|
|||||||
val suiteFun = getFunctions(FqName("kotlin.test.suite")).singleOrNull()?.let { symbolTable.referenceSimpleFunction(it) }
|
val suiteFun = getFunctions(FqName("kotlin.test.suite")).singleOrNull()?.let { symbolTable.referenceSimpleFunction(it) }
|
||||||
val testFun = getFunctions(FqName("kotlin.test.test")).singleOrNull()?.let { symbolTable.referenceSimpleFunction(it) }
|
val testFun = getFunctions(FqName("kotlin.test.test")).singleOrNull()?.let { symbolTable.referenceSimpleFunction(it) }
|
||||||
|
|
||||||
val coroutineImplLabelPropertyGetter by lazy2 { ir.symbols.coroutineImpl.getPropertyGetter("state")!!.owner }
|
|
||||||
val coroutineImplLabelPropertySetter by lazy2 { ir.symbols.coroutineImpl.getPropertySetter("state")!!.owner }
|
|
||||||
val coroutineImplResultSymbolGetter by lazy2 { ir.symbols.coroutineImpl.getPropertyGetter("result")!!.owner }
|
|
||||||
val coroutineImplResultSymbolSetter by lazy2 { ir.symbols.coroutineImpl.getPropertySetter("result")!!.owner }
|
|
||||||
val coroutineImplExceptionPropertyGetter by lazy2 { ir.symbols.coroutineImpl.getPropertyGetter("exception")!!.owner }
|
|
||||||
val coroutineImplExceptionPropertySetter by lazy2 { ir.symbols.coroutineImpl.getPropertySetter("exception")!!.owner }
|
|
||||||
val coroutineImplExceptionStatePropertyGetter by lazy2 { ir.symbols.coroutineImpl.getPropertyGetter("exceptionState")!!.owner }
|
|
||||||
val coroutineImplExceptionStatePropertySetter by lazy2 { ir.symbols.coroutineImpl.getPropertySetter("exceptionState")!!.owner }
|
|
||||||
|
|
||||||
val primitiveClassProperties by lazy2 {
|
val primitiveClassProperties by lazy2 {
|
||||||
primitiveClassesObject.owner.declarations.filterIsInstance<IrProperty>()
|
primitiveClassesObject.owner.declarations.filterIsInstance<IrProperty>()
|
||||||
}
|
}
|
||||||
@@ -407,12 +356,6 @@ class JsIrBackendContext(
|
|||||||
}.toMap()
|
}.toMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findClass(memberScope: MemberScope, name: Name): ClassDescriptor =
|
|
||||||
memberScope.getContributedClassifier(name, NoLookupLocation.FROM_BACKEND) as ClassDescriptor
|
|
||||||
|
|
||||||
private fun findFunctions(memberScope: MemberScope, name: Name): List<SimpleFunctionDescriptor> =
|
|
||||||
memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND).toList()
|
|
||||||
|
|
||||||
private fun findProperty(memberScope: MemberScope, name: Name): List<PropertyDescriptor> =
|
private fun findProperty(memberScope: MemberScope, name: Name): List<PropertyDescriptor> =
|
||||||
memberScope.getContributedVariables(name, NoLookupLocation.FROM_BACKEND).toList()
|
memberScope.getContributedVariables(name, NoLookupLocation.FROM_BACKEND).toList()
|
||||||
|
|
||||||
@@ -442,7 +385,4 @@ class JsIrBackendContext(
|
|||||||
/*TODO*/
|
/*TODO*/
|
||||||
print(message)
|
print(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: investigate if it could be removed
|
|
||||||
fun <T> lazy2(fn: () -> T) = lazy { irFactory.stageController.withInitialIr(fn) }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,8 +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.*
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.JsSuspendFunctionsLowering
|
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.inline.*
|
import org.jetbrains.kotlin.ir.backend.js.lower.inline.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||||
@@ -474,6 +473,29 @@ private val suspendFunctionsLoweringPhase = makeBodyLoweringPhase(
|
|||||||
description = "Transform suspend functions into CoroutineImpl instance and build state machine"
|
description = "Transform suspend functions into CoroutineImpl instance and build state machine"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private val addContinuationToNonLocalSuspendFunctionsLoweringPhase = makeDeclarationTransformerPhase(
|
||||||
|
::AddContinuationToNonLocalSuspendFunctionsLowering,
|
||||||
|
name = "AddContinuationToNonLocalSuspendFunctionsLowering",
|
||||||
|
description = "Add explicit continuation as last parameter of non-local suspend functions"
|
||||||
|
)
|
||||||
|
|
||||||
|
private val addContinuationToLocalSuspendFunctionsLoweringPhase = makeBodyLoweringPhase(
|
||||||
|
::AddContinuationToLocalSuspendFunctionsLowering,
|
||||||
|
name = "AddContinuationToLocalSuspendFunctionsLowering",
|
||||||
|
description = "Add explicit continuation as last parameter of local suspend functions"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
private val addContinuationToFunctionCallsLoweringPhase = makeBodyLoweringPhase(
|
||||||
|
::AddContinuationToFunctionCallsLowering,
|
||||||
|
name = "AddContinuationToFunctionCallsLowering",
|
||||||
|
description = "Replace suspend function calls with calls with continuation",
|
||||||
|
prerequisite = setOf(
|
||||||
|
addContinuationToLocalSuspendFunctionsLoweringPhase,
|
||||||
|
addContinuationToNonLocalSuspendFunctionsLoweringPhase,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
private val privateMembersLoweringPhase = makeDeclarationTransformerPhase(
|
private val privateMembersLoweringPhase = makeDeclarationTransformerPhase(
|
||||||
::PrivateMembersLowering,
|
::PrivateMembersLowering,
|
||||||
name = "PrivateMembersLowering",
|
name = "PrivateMembersLowering",
|
||||||
@@ -812,6 +834,10 @@ private val loweringList = listOf<Lowering>(
|
|||||||
suspendFunctionsLoweringPhase,
|
suspendFunctionsLoweringPhase,
|
||||||
propertyReferenceLoweringPhase,
|
propertyReferenceLoweringPhase,
|
||||||
interopCallableReferenceLoweringPhase,
|
interopCallableReferenceLoweringPhase,
|
||||||
|
jsSuspendArityStorePhase,
|
||||||
|
addContinuationToNonLocalSuspendFunctionsLoweringPhase,
|
||||||
|
addContinuationToLocalSuspendFunctionsLoweringPhase,
|
||||||
|
addContinuationToFunctionCallsLoweringPhase,
|
||||||
returnableBlockLoweringPhase,
|
returnableBlockLoweringPhase,
|
||||||
rangeContainsLoweringPhase,
|
rangeContainsLoweringPhase,
|
||||||
forLoopsLoweringPhase,
|
forLoopsLoweringPhase,
|
||||||
@@ -855,7 +881,6 @@ private val loweringList = listOf<Lowering>(
|
|||||||
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?
|
||||||
|
|||||||
+81
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 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.BodyLoweringPass
|
||||||
|
import org.jetbrains.kotlin.backend.common.ir.isSuspend
|
||||||
|
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||||
|
import org.jetbrains.kotlin.backend.common.runOnFilePostfix
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
|
||||||
|
import org.jetbrains.kotlin.ir.builders.irGet
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrBody
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
|
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||||
|
import org.jetbrains.kotlin.ir.util.irCall
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add continuation to suspend function calls. Requires [AddContinuationToLocalSuspendFunctionsLowering] and
|
||||||
|
* [AddContinuationToNonLocalSuspendFunctionsLowering] to transform function declarations first.
|
||||||
|
*
|
||||||
|
* Additionally materialize continuation for `getContinuation` intrinsic calls.
|
||||||
|
*/
|
||||||
|
class AddContinuationToFunctionCallsLowering(val context: JsCommonBackendContext) : BodyLoweringPass {
|
||||||
|
override fun lower(irFile: IrFile) {
|
||||||
|
runOnFilePostfix(irFile, withLocalDeclarations = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun lower(irBody: IrBody, container: IrDeclaration) {
|
||||||
|
val continuation: IrValueParameter by lazy {
|
||||||
|
val function = container as IrSimpleFunction
|
||||||
|
if (function.overriddenSymbols
|
||||||
|
.any { it.owner.name.asString() == "doResume" && it.owner.parent == context.coroutineSymbols.coroutineImpl.owner }
|
||||||
|
) {
|
||||||
|
function.dispatchReceiverParameter!!
|
||||||
|
} else {
|
||||||
|
function.valueParameters.last()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val builder by lazy { context.createIrBuilder(container.symbol) }
|
||||||
|
fun getContinuation() = builder.irGet(continuation)
|
||||||
|
|
||||||
|
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||||
|
override fun visitBody(body: IrBody): IrBody {
|
||||||
|
// Nested bodies are covered by separate `lower` invocation
|
||||||
|
return body
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitCall(expression: IrCall): IrExpression {
|
||||||
|
expression.transformChildrenVoid()
|
||||||
|
|
||||||
|
if (!expression.isSuspend) {
|
||||||
|
if (expression.symbol == context.ir.symbols.getContinuation)
|
||||||
|
return getContinuation()
|
||||||
|
return expression
|
||||||
|
}
|
||||||
|
|
||||||
|
val oldFun = expression.symbol.owner
|
||||||
|
val newFun: IrSimpleFunction =
|
||||||
|
context.mapping.suspendFunctionsToFunctionWithContinuations[oldFun]
|
||||||
|
?: error("No mapping for ${oldFun.fqNameWhenAvailable}")
|
||||||
|
|
||||||
|
return irCall(
|
||||||
|
expression,
|
||||||
|
newFun.symbol,
|
||||||
|
newReturnType = newFun.returnType,
|
||||||
|
newSuperQualifierSymbol = expression.superQualifierSymbol
|
||||||
|
).also {
|
||||||
|
it.putValueArgument(it.valueArgumentsCount - 1, getContinuation())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+122
@@ -0,0 +1,122 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 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.*
|
||||||
|
import org.jetbrains.kotlin.backend.common.ir.*
|
||||||
|
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||||
|
import org.jetbrains.kotlin.ir.IrStatement
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
|
||||||
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
|
import org.jetbrains.kotlin.ir.builders.declarations.*
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
|
import org.jetbrains.kotlin.ir.types.isNullable
|
||||||
|
import org.jetbrains.kotlin.ir.types.typeWith
|
||||||
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces suspend functions with regular non-suspend functions with additional
|
||||||
|
* continuation parameter `$cont` of type [kotlin.coroutines.Continuation].
|
||||||
|
*
|
||||||
|
* Replaces return type with `Any?` or `Any` (for non-nullable types) to indicate that suspend
|
||||||
|
* functions can return special values like [kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED]
|
||||||
|
* which might not be a subtype of original return type.
|
||||||
|
*/
|
||||||
|
class AddContinuationToNonLocalSuspendFunctionsLowering(val context: JsCommonBackendContext) : DeclarationTransformer {
|
||||||
|
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? =
|
||||||
|
if (declaration is IrSimpleFunction && declaration.isSuspend) {
|
||||||
|
listOf(transformSuspendFunction(context, declaration))
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to [AddContinuationToNonLocalSuspendFunctionsLowering] but processes local functions.
|
||||||
|
* Useful for Kotlin/JS IR backend which keeps local declarations up until code generation.
|
||||||
|
*/
|
||||||
|
class AddContinuationToLocalSuspendFunctionsLowering(val context: JsCommonBackendContext) : BodyLoweringPass {
|
||||||
|
override fun lower(irBody: IrBody, container: IrDeclaration) {
|
||||||
|
irBody.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||||
|
override fun visitSimpleFunction(declaration: IrSimpleFunction): IrStatement {
|
||||||
|
declaration.transformChildrenVoid()
|
||||||
|
return if (declaration.isSuspend) {
|
||||||
|
transformSuspendFunction(context, declaration)
|
||||||
|
} else {
|
||||||
|
declaration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun transformSuspendFunction(context: JsCommonBackendContext, function: IrSimpleFunction): IrSimpleFunction {
|
||||||
|
val newFunctionWithContinuation = function.getOrCreateFunctionWithContinuationStub(context)
|
||||||
|
// Using custom mapping because number of parameters doesn't match
|
||||||
|
val parameterMapping = function.explicitParameters.zip(newFunctionWithContinuation.explicitParameters).toMap()
|
||||||
|
val newBody = function.moveBodyTo(newFunctionWithContinuation, parameterMapping)
|
||||||
|
|
||||||
|
// Since we are changing return type to Any, function can no longer return unit implicitly.
|
||||||
|
if (
|
||||||
|
function.returnType == context.irBuiltIns.unitType &&
|
||||||
|
newBody is IrBlockBody &&
|
||||||
|
newBody.statements.lastOrNull() !is IrReturn
|
||||||
|
) {
|
||||||
|
// Adding explicit return of Unit.
|
||||||
|
newBody.statements += context.createIrBuilder(newFunctionWithContinuation.symbol).irReturnUnit()
|
||||||
|
}
|
||||||
|
|
||||||
|
newFunctionWithContinuation.body = newBody
|
||||||
|
return newFunctionWithContinuation
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun IrSimpleFunction.getOrCreateFunctionWithContinuationStub(context: JsCommonBackendContext): IrSimpleFunction {
|
||||||
|
return context.mapping.suspendFunctionsToFunctionWithContinuations.getOrPut(this) {
|
||||||
|
createSuspendFunctionStub(context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IrSimpleFunction.createSuspendFunctionStub(context: JsCommonBackendContext): IrSimpleFunction {
|
||||||
|
require(this.isSuspend)
|
||||||
|
return factory.buildFun {
|
||||||
|
updateFrom(this@createSuspendFunctionStub)
|
||||||
|
isSuspend = false
|
||||||
|
name = this@createSuspendFunctionStub.name
|
||||||
|
origin = this@createSuspendFunctionStub.origin
|
||||||
|
returnType = loweredSuspendFunctionReturnType(this@createSuspendFunctionStub, context.irBuiltIns)
|
||||||
|
}.also { function ->
|
||||||
|
function.parent = parent
|
||||||
|
|
||||||
|
function.annotations += annotations
|
||||||
|
function.metadata = metadata
|
||||||
|
|
||||||
|
function.copyAttributes(this)
|
||||||
|
function.copyTypeParametersFrom(this)
|
||||||
|
val substitutionMap = makeTypeParameterSubstitutionMap(this, function)
|
||||||
|
function.copyReceiverParametersFrom(this, substitutionMap)
|
||||||
|
|
||||||
|
function.overriddenSymbols += overriddenSymbols.map {
|
||||||
|
it.owner.getOrCreateFunctionWithContinuationStub(context).symbol
|
||||||
|
}
|
||||||
|
function.valueParameters = valueParameters.map { it.copyTo(function) }
|
||||||
|
|
||||||
|
function.addValueParameter(
|
||||||
|
"\$cont",
|
||||||
|
continuationType(context).substitute(substitutionMap),
|
||||||
|
IrDeclarationOrigin.CONTINUATION
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IrFunction.continuationType(context: JsCommonBackendContext): IrType {
|
||||||
|
return context.coroutineSymbols.continuationClass.typeWith(returnType)
|
||||||
|
}
|
||||||
+40
-20
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.common.ir.isSuspend
|
|||||||
import org.jetbrains.kotlin.backend.common.lower.FinallyBlocksLowering
|
import org.jetbrains.kotlin.backend.common.lower.FinallyBlocksLowering
|
||||||
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
|
import org.jetbrains.kotlin.ir.backend.js.JsStatementOrigins
|
||||||
import org.jetbrains.kotlin.backend.common.lower.ReturnableBlockTransformer
|
import org.jetbrains.kotlin.backend.common.lower.ReturnableBlockTransformer
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
|
||||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||||
import org.jetbrains.kotlin.ir.builders.*
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
@@ -29,16 +30,18 @@ import org.jetbrains.kotlin.name.Name
|
|||||||
import org.jetbrains.kotlin.utils.DFS
|
import org.jetbrains.kotlin.utils.DFS
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.assertedCast
|
import org.jetbrains.kotlin.utils.addToStdlib.assertedCast
|
||||||
|
|
||||||
class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunctionsLowering<JsIrBackendContext>(ctx) {
|
class JsSuspendFunctionsLowering(ctx: JsCommonBackendContext) : AbstractSuspendFunctionsLowering<JsCommonBackendContext>(ctx) {
|
||||||
|
|
||||||
private val coroutineImplExceptionPropertyGetter = ctx.coroutineImplExceptionPropertyGetter
|
val coroutineSymbols = ctx.coroutineSymbols
|
||||||
private val coroutineImplExceptionPropertySetter = ctx.coroutineImplExceptionPropertySetter
|
|
||||||
private val coroutineImplExceptionStatePropertyGetter = ctx.coroutineImplExceptionStatePropertyGetter
|
private val coroutineImplExceptionPropertyGetter = coroutineSymbols.coroutineImplExceptionPropertyGetter
|
||||||
private val coroutineImplExceptionStatePropertySetter = ctx.coroutineImplExceptionStatePropertySetter
|
private val coroutineImplExceptionPropertySetter = coroutineSymbols.coroutineImplExceptionPropertySetter
|
||||||
private val coroutineImplLabelPropertySetter = ctx.coroutineImplLabelPropertySetter
|
private val coroutineImplExceptionStatePropertyGetter = coroutineSymbols.coroutineImplExceptionStatePropertyGetter
|
||||||
private val coroutineImplLabelPropertyGetter = ctx.coroutineImplLabelPropertyGetter
|
private val coroutineImplExceptionStatePropertySetter = coroutineSymbols.coroutineImplExceptionStatePropertySetter
|
||||||
private val coroutineImplResultSymbolGetter = ctx.coroutineImplResultSymbolGetter
|
private val coroutineImplLabelPropertySetter = coroutineSymbols.coroutineImplLabelPropertySetter
|
||||||
private val coroutineImplResultSymbolSetter = ctx.coroutineImplResultSymbolSetter
|
private val coroutineImplLabelPropertyGetter = coroutineSymbols.coroutineImplLabelPropertyGetter
|
||||||
|
private val coroutineImplResultSymbolGetter = coroutineSymbols.coroutineImplResultSymbolGetter
|
||||||
|
private val coroutineImplResultSymbolSetter = coroutineSymbols.coroutineImplResultSymbolSetter
|
||||||
|
|
||||||
private var coroutineId = 0
|
private var coroutineId = 0
|
||||||
|
|
||||||
@@ -53,7 +56,7 @@ class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunct
|
|||||||
argumentToPropertiesMap: Map<IrValueParameter, IrField>
|
argumentToPropertiesMap: Map<IrValueParameter, IrField>
|
||||||
) {
|
) {
|
||||||
val returnableBlockTransformer = ReturnableBlockTransformer(context)
|
val returnableBlockTransformer = ReturnableBlockTransformer(context)
|
||||||
val finallyBlockTransformer = FinallyBlocksLowering(context, context.dynamicType)
|
val finallyBlockTransformer = FinallyBlocksLowering(context, context.catchAllThrowableType)
|
||||||
val simplifiedFunction =
|
val simplifiedFunction =
|
||||||
transformingFunction.transform(finallyBlockTransformer, null).transform(returnableBlockTransformer, null) as IrFunction
|
transformingFunction.transform(finallyBlockTransformer, null).transform(returnableBlockTransformer, null) as IrFunction
|
||||||
|
|
||||||
@@ -117,7 +120,22 @@ class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunct
|
|||||||
coroutineImplExceptionStatePropertySetter,
|
coroutineImplExceptionStatePropertySetter,
|
||||||
coroutineImplLabelPropertySetter,
|
coroutineImplLabelPropertySetter,
|
||||||
thisReceiver,
|
thisReceiver,
|
||||||
suspendResult.symbol
|
getSuspendResultAsType = { type ->
|
||||||
|
JsIrBuilder.buildImplicitCast(
|
||||||
|
JsIrBuilder.buildGetValue(suspendResult.symbol),
|
||||||
|
type
|
||||||
|
)
|
||||||
|
},
|
||||||
|
setSuspendResultValue = { value ->
|
||||||
|
JsIrBuilder.buildSetVariable(
|
||||||
|
suspendResult.symbol,
|
||||||
|
JsIrBuilder.buildImplicitCast(
|
||||||
|
value,
|
||||||
|
context.irBuiltIns.anyNType
|
||||||
|
),
|
||||||
|
unit
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
body.acceptVoid(stateMachineBuilder)
|
body.acceptVoid(stateMachineBuilder)
|
||||||
@@ -179,9 +197,9 @@ class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunct
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stateMachineFunction.transform(LiveLocalsTransformer(localToPropertyMap, { JsIrBuilder.buildGetValue(thisReceiver) }, unit), null)
|
|
||||||
// TODO find out why some parents are incorrect
|
// TODO find out why some parents are incorrect
|
||||||
stateMachineFunction.body!!.patchDeclarationParents(stateMachineFunction)
|
stateMachineFunction.body!!.patchDeclarationParents(stateMachineFunction)
|
||||||
|
stateMachineFunction.transform(LiveLocalsTransformer(localToPropertyMap, { JsIrBuilder.buildGetValue(thisReceiver) }, unit), null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignStateIds(entryState: SuspendState, subject: IrVariableSymbol, switch: IrWhen, rootLoop: IrLoop) {
|
private fun assignStateIds(entryState: SuspendState, subject: IrVariableSymbol, switch: IrWhen, rootLoop: IrLoop) {
|
||||||
@@ -235,16 +253,18 @@ class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunct
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun IrBuilderWithScope.generateDelegatedCall(expectedType: IrType, delegatingCall: IrExpression): IrExpression {
|
override fun IrBuilderWithScope.generateDelegatedCall(expectedType: IrType, delegatingCall: IrExpression): IrExpression {
|
||||||
val fromType = (delegatingCall as? IrCall)?.symbol?.owner?.returnType ?: delegatingCall.type
|
val functionReturnType = (delegatingCall as? IrCall)?.symbol?.owner?.let { function ->
|
||||||
if (!needUnboxingOrUnit(fromType, expectedType)) return delegatingCall
|
loweredSuspendFunctionReturnType(function, context.irBuiltIns)
|
||||||
|
} ?: delegatingCall.type
|
||||||
|
|
||||||
val ctx = this@JsSuspendFunctionsLowering.context
|
if (!needUnboxingOrUnit(functionReturnType, expectedType)) return delegatingCall
|
||||||
return irComposite(resultType = fromType) {
|
|
||||||
val tmp = createTmpVariable(delegatingCall, irType = fromType)
|
return irComposite(resultType = expectedType) {
|
||||||
val coroutineSuspended = irCall(ctx.coroutineSuspendGetter)
|
val tmp = createTmpVariable(delegatingCall, irType = functionReturnType)
|
||||||
|
val coroutineSuspended = irCall(coroutineSymbols.coroutineSuspendedGetter)
|
||||||
val condition = irEqeqeq(irGet(tmp), coroutineSuspended)
|
val condition = irEqeqeq(irGet(tmp), coroutineSuspended)
|
||||||
+irIfThen(fromType, condition, irReturn(irReinterpretCast(irGet(tmp), expectedType)))
|
+irIfThen(context.irBuiltIns.unitType, condition, irReturn(irGet(tmp)))
|
||||||
+irGet(tmp)
|
+irImplicitCast(irGet(tmp), expectedType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
-9
@@ -77,7 +77,8 @@ class StateMachineBuilder(
|
|||||||
private val exStateSymbolSetter: IrSimpleFunction,
|
private val exStateSymbolSetter: IrSimpleFunction,
|
||||||
private val stateSymbolSetter: IrSimpleFunction,
|
private val stateSymbolSetter: IrSimpleFunction,
|
||||||
private val thisSymbol: IrValueParameterSymbol,
|
private val thisSymbol: IrValueParameterSymbol,
|
||||||
private val suspendResult: IrVariableSymbol
|
private val getSuspendResultAsType: (IrType) -> IrExpression,
|
||||||
|
private val setSuspendResultValue: (IrExpression) -> IrStatement
|
||||||
) : IrElementVisitorVoid {
|
) : IrElementVisitorVoid {
|
||||||
|
|
||||||
private val loopMap = mutableMapOf<IrLoop, LoopBounds>()
|
private val loopMap = mutableMapOf<IrLoop, LoopBounds>()
|
||||||
@@ -270,7 +271,6 @@ class StateMachineBuilder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun implicitCast(value: IrExpression, toType: IrType) = JsIrBuilder.buildImplicitCast(value, toType)
|
private fun implicitCast(value: IrExpression, toType: IrType) = JsIrBuilder.buildImplicitCast(value, toType)
|
||||||
private fun reinterpretCast(value: IrExpression, toType: IrType) = JsIrBuilder.buildReinterpretCast(value, toType)
|
|
||||||
|
|
||||||
override fun visitCall(expression: IrCall) {
|
override fun visitCall(expression: IrCall) {
|
||||||
super.visitCall(expression)
|
super.visitCall(expression)
|
||||||
@@ -295,11 +295,11 @@ class StateMachineBuilder(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addStatement(JsIrBuilder.buildSetVariable(suspendResult, reinterpretCast(result, anyN), unit))
|
addStatement(setSuspendResultValue(result))
|
||||||
|
|
||||||
val irReturn = JsIrBuilder.buildReturn(function, JsIrBuilder.buildGetValue(suspendResult), nothing)
|
val irReturn = JsIrBuilder.buildReturn(function, getSuspendResultAsType(anyN), nothing)
|
||||||
val check = JsIrBuilder.buildCall(eqeqeqSymbol).apply {
|
val check = JsIrBuilder.buildCall(eqeqeqSymbol).apply {
|
||||||
putValueArgument(0, JsIrBuilder.buildGetValue(suspendResult))
|
putValueArgument(0, getSuspendResultAsType(anyN))
|
||||||
putValueArgument(1, JsIrBuilder.buildCall(context.ir.symbols.coroutineSuspendedGetter))
|
putValueArgument(1, JsIrBuilder.buildCall(context.ir.symbols.coroutineSuspendedGetter))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,18 +318,17 @@ class StateMachineBuilder(
|
|||||||
unboxState?.let { buildUnboxingState(it, continueState, expectedType) }
|
unboxState?.let { buildUnboxingState(it, continueState, expectedType) }
|
||||||
|
|
||||||
updateState(continueState)
|
updateState(continueState)
|
||||||
val functionReturnType = expression.symbol.owner.returnType
|
addStatement(getSuspendResultAsType(expression.type))
|
||||||
addStatement(reinterpretCast(JsIrBuilder.buildGetValue(suspendResult), functionReturnType))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildUnboxingState(unboxState: SuspendState, continueState: SuspendState, expectedType: IrType) {
|
private fun buildUnboxingState(unboxState: SuspendState, continueState: SuspendState, expectedType: IrType) {
|
||||||
unboxState.successors += continueState
|
unboxState.successors += continueState
|
||||||
updateState(unboxState)
|
updateState(unboxState)
|
||||||
val result = JsIrBuilder.buildGetValue(suspendResult)
|
val result = getSuspendResultAsType(anyN)
|
||||||
val tmp = JsIrBuilder.buildVar(expectedType, function.owner, name = "unboxed", initializer = result)
|
val tmp = JsIrBuilder.buildVar(expectedType, function.owner, name = "unboxed", initializer = result)
|
||||||
addStatement(tmp)
|
addStatement(tmp)
|
||||||
addStatement(JsIrBuilder.buildSetVariable(suspendResult, reinterpretCast(JsIrBuilder.buildGetValue(tmp.symbol), anyN), anyN))
|
addStatement(setSuspendResultValue(JsIrBuilder.buildGetValue(tmp.symbol)))
|
||||||
|
|
||||||
doDispatch(continueState)
|
doDispatch(continueState)
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-4
@@ -6,9 +6,12 @@
|
|||||||
package org.jetbrains.kotlin.ir.backend.js.lower.coroutines
|
package org.jetbrains.kotlin.ir.backend.js.lower.coroutines
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.ir.isSuspend
|
import org.jetbrains.kotlin.backend.common.ir.isSuspend
|
||||||
|
import org.jetbrains.kotlin.ir.IrBuiltIns
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.IrStatement
|
import org.jetbrains.kotlin.ir.IrStatement
|
||||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.utils.isDispatchReceiver
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
|
||||||
@@ -17,7 +20,7 @@ import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
|
|||||||
import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
import org.jetbrains.kotlin.ir.types.isUnit
|
import org.jetbrains.kotlin.ir.types.isNullable
|
||||||
import org.jetbrains.kotlin.ir.visitors.*
|
import org.jetbrains.kotlin.ir.visitors.*
|
||||||
|
|
||||||
open class SuspendableNodesCollector(private val suspendableNodes: MutableSet<IrElement>) : IrElementVisitorVoid {
|
open class SuspendableNodesCollector(private val suspendableNodes: MutableSet<IrElement>) : IrElementVisitorVoid {
|
||||||
@@ -82,7 +85,12 @@ class LiveLocalsTransformer(
|
|||||||
) :
|
) :
|
||||||
IrElementTransformerVoid() {
|
IrElementTransformerVoid() {
|
||||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||||
val field = localMap[expression.symbol] ?: return expression
|
val field = localMap[expression.symbol]
|
||||||
|
?: return if (expression.symbol.owner.isDispatchReceiver)
|
||||||
|
receiver()
|
||||||
|
else
|
||||||
|
expression
|
||||||
|
|
||||||
return expression.run { IrGetFieldImpl(startOffset, endOffset, field, type, receiver(), origin) }
|
return expression.run { IrGetFieldImpl(startOffset, endOffset, field, type, receiver(), origin) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +107,10 @@ class LiveLocalsTransformer(
|
|||||||
return if (initializer != null) {
|
return if (initializer != null) {
|
||||||
declaration.run { IrSetFieldImpl(startOffset, endOffset, field, receiver(), initializer, unitType) }
|
declaration.run { IrSetFieldImpl(startOffset, endOffset, field, receiver(), initializer, unitType) }
|
||||||
} else {
|
} else {
|
||||||
JsIrBuilder.buildComposite(declaration.type)
|
JsIrBuilder.buildComposite(unitType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun loweredSuspendFunctionReturnType(function: IrFunction, irBuiltIns: IrBuiltIns): IrType =
|
||||||
|
if (function.returnType.isNullable()) irBuiltIns.anyNType else irBuiltIns.anyType
|
||||||
+12
-5
@@ -349,12 +349,16 @@ class IrModuleToJsTransformer(
|
|||||||
return statements
|
return statements
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateMainArguments(mainFunction: IrSimpleFunction, rootContext: JsGenerationContext): List<JsExpression> {
|
private fun generateMainArguments(
|
||||||
|
generateArgv: Boolean,
|
||||||
|
generateContinuation: Boolean,
|
||||||
|
rootContext: JsGenerationContext
|
||||||
|
): List<JsExpression> {
|
||||||
val mainArguments = this.mainArguments!!
|
val mainArguments = this.mainArguments!!
|
||||||
val mainArgumentsArray =
|
val mainArgumentsArray =
|
||||||
if (mainFunction.valueParameters.isNotEmpty()) JsArrayLiteral(mainArguments.map { JsStringLiteral(it) }) else null
|
if (generateArgv) JsArrayLiteral(mainArguments.map { JsStringLiteral(it) }) else null
|
||||||
|
|
||||||
val continuation = if (mainFunction.isSuspend) {
|
val continuation = if (generateContinuation) {
|
||||||
backendContext.coroutineEmptyContinuation.owner
|
backendContext.coroutineEmptyContinuation.owner
|
||||||
.let { it.getter!! }
|
.let { it.getter!! }
|
||||||
.let { rootContext.getNameForStaticFunction(it) }
|
.let { rootContext.getNameForStaticFunction(it) }
|
||||||
@@ -365,11 +369,14 @@ class IrModuleToJsTransformer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun generateCallToMain(modules: Iterable<IrModuleFragment>, rootContext: JsGenerationContext): List<JsStatement> {
|
private fun generateCallToMain(modules: Iterable<IrModuleFragment>, rootContext: JsGenerationContext): List<JsStatement> {
|
||||||
|
// TODO: Generate calls to main as IR->IR lowering
|
||||||
if (mainArguments == null) return emptyList() // in case `NO_MAIN` and `main(..)` exists
|
if (mainArguments == null) return emptyList() // in case `NO_MAIN` and `main(..)` exists
|
||||||
val mainFunction = JsMainFunctionDetector.getMainFunctionOrNull(modules.last())
|
val mainFunction = JsMainFunctionDetector(backendContext).getMainFunctionOrNull(modules.last())
|
||||||
return mainFunction?.let {
|
return mainFunction?.let {
|
||||||
val jsName = rootContext.getNameForStaticFunction(it)
|
val jsName = rootContext.getNameForStaticFunction(it)
|
||||||
listOf(JsInvocation(jsName.makeRef(), generateMainArguments(it, rootContext)).makeStmt())
|
val generateArgv = it.valueParameters.firstOrNull()?.isStringArrayParameter() ?: false
|
||||||
|
val generateContinuation = it.isLoweredSuspendFunction(backendContext)
|
||||||
|
listOf(JsInvocation(jsName.makeRef(), generateMainArguments(generateArgv, generateContinuation, rootContext)).makeStmt())
|
||||||
} ?: emptyList()
|
} ?: emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-14
@@ -121,21 +121,9 @@ class JsIntrinsicTransformers(backendContext: JsIrBackendContext) {
|
|||||||
|
|
||||||
addIfNotNull(intrinsics.jsCode) { _, _ -> error("Should not be called") }
|
addIfNotNull(intrinsics.jsCode) { _, _ -> error("Should not be called") }
|
||||||
|
|
||||||
add(intrinsics.jsGetContinuation) { _, context: JsGenerationContext ->
|
add(intrinsics.jsGetContinuation) { _, _ -> error("getContinuation should be lowered") }
|
||||||
context.continuation
|
|
||||||
}
|
|
||||||
|
|
||||||
add(backendContext.ir.symbols.returnIfSuspended) { call, context ->
|
add(intrinsics.jsCoroutineContext) { _, _ -> error("coroutineContext should be lowered") }
|
||||||
val args = translateCallArguments(call, context)
|
|
||||||
args[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
add(intrinsics.jsCoroutineContext) { _, context: JsGenerationContext ->
|
|
||||||
val contextGetter = backendContext.coroutineGetContext
|
|
||||||
val getterName = context.getNameForStaticFunction(contextGetter.owner)
|
|
||||||
val continuation = context.continuation
|
|
||||||
JsInvocation(JsNameRef(getterName, continuation))
|
|
||||||
}
|
|
||||||
|
|
||||||
add(intrinsics.jsArrayLength) { call, context ->
|
add(intrinsics.jsArrayLength) { call, context ->
|
||||||
val args = translateCallArguments(call, context)
|
val args = translateCallArguments(call, context)
|
||||||
|
|||||||
+3
-6
@@ -76,9 +76,7 @@ fun translateFunction(declaration: IrFunction, name: JsName?, context: JsGenerat
|
|||||||
|
|
||||||
declaration.extensionReceiverParameter?.let { function.addParameter(functionContext.getNameForValueDeclaration(it)) }
|
declaration.extensionReceiverParameter?.let { function.addParameter(functionContext.getNameForValueDeclaration(it)) }
|
||||||
functionParams.forEach { function.addParameter(it) }
|
functionParams.forEach { function.addParameter(it) }
|
||||||
if (declaration.isSuspend) {
|
check(!declaration.isSuspend) { "All Suspend functions should be lowered" }
|
||||||
function.addParameter(JsName(Namer.CONTINUATION)) // TODO: Use namer?
|
|
||||||
}
|
|
||||||
|
|
||||||
return function
|
return function
|
||||||
}
|
}
|
||||||
@@ -336,9 +334,8 @@ fun translateCallArguments(
|
|||||||
.dropLastWhile { it == null }
|
.dropLastWhile { it == null }
|
||||||
.map { it ?: JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(1)) }
|
.map { it ?: JsPrefixOperation(JsUnaryOperator.VOID, JsIntLiteral(1)) }
|
||||||
|
|
||||||
return if (expression.symbol.isSuspend) {
|
check(!expression.symbol.isSuspend) { "Suspend functions should be lowered" }
|
||||||
arguments + context.continuation
|
return arguments
|
||||||
} else arguments
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrMemberAccessExpression<*>.validWithNullArgs() =
|
private fun IrMemberAccessExpression<*>.validWithNullArgs() =
|
||||||
|
|||||||
-18
@@ -49,17 +49,6 @@ class JsGenerationContext(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val continuation
|
|
||||||
get() = if (isCoroutineDoResume()) {
|
|
||||||
JsThisRef()
|
|
||||||
} else {
|
|
||||||
if (currentFunction!!.isSuspend) {
|
|
||||||
JsNameRef(Namer.CONTINUATION)
|
|
||||||
} else {
|
|
||||||
JsNameRef(this.getNameForValueDeclaration(currentFunction.valueParameters.last()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getNameForValueDeclaration(declaration: IrDeclarationWithName): JsName {
|
fun getNameForValueDeclaration(declaration: IrDeclarationWithName): JsName {
|
||||||
val name = localNames!!.variableNames.names[declaration]
|
val name = localNames!!.variableNames.names[declaration]
|
||||||
?: error("Variable name is not found ${declaration.name}")
|
?: error("Variable name is not found ${declaration.name}")
|
||||||
@@ -76,12 +65,5 @@ class JsGenerationContext(
|
|||||||
return JsName(name)
|
return JsName(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isCoroutineDoResume(): Boolean {
|
|
||||||
val overriddenSymbols = (currentFunction as? IrSimpleFunction)?.overriddenSymbols ?: return false
|
|
||||||
return overriddenSymbols.any {
|
|
||||||
it.owner.name.asString() == "doResume" && it.owner.parent == staticContext.coroutineImplDeclaration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun checkIfJsCode(symbol: IrFunctionSymbol): Boolean = symbol == staticContext.backendContext.intrinsics.jsCode
|
fun checkIfJsCode(symbol: IrFunctionSymbol): Boolean = symbol == staticContext.backendContext.intrinsics.jsCode
|
||||||
}
|
}
|
||||||
|
|||||||
+41
-21
@@ -5,18 +5,16 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.ir.backend.js.utils
|
package org.jetbrains.kotlin.ir.backend.js.utils
|
||||||
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
|
||||||
import org.jetbrains.kotlin.ir.types.*
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
import org.jetbrains.kotlin.types.Variance
|
import org.jetbrains.kotlin.types.Variance
|
||||||
|
|
||||||
object JsMainFunctionDetector {
|
class JsMainFunctionDetector(val context: JsCommonBackendContext) {
|
||||||
|
|
||||||
|
|
||||||
private fun IrSimpleFunction.isSuitableForMainParametersSize(allowEmptyParameters: Boolean): Boolean =
|
private fun IrSimpleFunction.isSuitableForMainParametersSize(allowEmptyParameters: Boolean): Boolean =
|
||||||
when (valueParameters.size) {
|
when (valueParameters.size) {
|
||||||
1 -> true
|
1, 2 -> true
|
||||||
0 -> allowEmptyParameters
|
0 -> allowEmptyParameters
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
@@ -24,24 +22,20 @@ object JsMainFunctionDetector {
|
|||||||
private fun IrSimpleFunction.isMain(allowEmptyParameters: Boolean): Boolean {
|
private fun IrSimpleFunction.isMain(allowEmptyParameters: Boolean): Boolean {
|
||||||
if (typeParameters.isNotEmpty()) return false
|
if (typeParameters.isNotEmpty()) return false
|
||||||
if (!isSuitableForMainParametersSize(allowEmptyParameters)) return false
|
if (!isSuitableForMainParametersSize(allowEmptyParameters)) return false
|
||||||
if (!returnType.isUnit()) return false
|
val isLoweredSuspendFunction = isLoweredSuspendFunction(context)
|
||||||
|
if (!returnType.isUnit() &&
|
||||||
|
!(isLoweredSuspendFunction &&
|
||||||
|
(returnType == context.irBuiltIns.anyNType ||
|
||||||
|
returnType == context.irBuiltIns.anyType)))
|
||||||
|
return false
|
||||||
|
|
||||||
if (name.asString() != "main") return false
|
if (name.asString() != "main") return false
|
||||||
if (extensionReceiverParameter != null) return false
|
if (extensionReceiverParameter != null) return false
|
||||||
|
|
||||||
if (valueParameters.size == 1) {
|
if (valueParameters.size == 1) {
|
||||||
val parameter = valueParameters.single()
|
return isLoweredSuspendFunction || valueParameters.single().isStringArrayParameter()
|
||||||
|
} else if (valueParameters.size == 2) {
|
||||||
if (!parameter.type.isArray()) return false
|
return valueParameters[0].isStringArrayParameter() && isLoweredSuspendFunction
|
||||||
|
|
||||||
val type = parameter.type as IrSimpleType
|
|
||||||
|
|
||||||
if (type.arguments.size != 1) return false
|
|
||||||
|
|
||||||
val argument = type.arguments.single() as? IrTypeProjection ?: return false
|
|
||||||
|
|
||||||
if (argument.variance == Variance.IN_VARIANCE) return false
|
|
||||||
|
|
||||||
return argument.type.isString()
|
|
||||||
} else {
|
} else {
|
||||||
require(allowEmptyParameters)
|
require(allowEmptyParameters)
|
||||||
require(valueParameters.isEmpty())
|
require(valueParameters.isEmpty())
|
||||||
@@ -73,4 +67,30 @@ object JsMainFunctionDetector {
|
|||||||
|
|
||||||
return resultPair?.second
|
return resultPair?.second
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun IrValueParameter.isStringArrayParameter(): Boolean {
|
||||||
|
val type = this.type as? IrSimpleType ?: return false
|
||||||
|
|
||||||
|
if (!type.isArray()) return false
|
||||||
|
|
||||||
|
if (type.arguments.size != 1) return false
|
||||||
|
|
||||||
|
val argument = type.arguments.single() as? IrTypeProjection ?: return false
|
||||||
|
|
||||||
|
if (argument.variance == Variance.IN_VARIANCE) return false
|
||||||
|
|
||||||
|
return argument.type.isString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun IrFunction.isLoweredSuspendFunction(context: JsCommonBackendContext): Boolean {
|
||||||
|
val parameter = valueParameters.lastOrNull() ?: return false
|
||||||
|
val type = parameter.type as? IrSimpleType ?: return false
|
||||||
|
return type.classifier == context.coroutineSymbols.continuationClass
|
||||||
|
}
|
||||||
|
|
||||||
|
fun IrValueParameter.isContinuationParameter(context: JsCommonBackendContext): Boolean {
|
||||||
|
val type = this.type as? IrSimpleType ?: return false
|
||||||
|
return type.classifier == context.coroutineSymbols.continuationClass
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,8 +23,6 @@ object Namer {
|
|||||||
val PROTOTYPE_NAME = "prototype"
|
val PROTOTYPE_NAME = "prototype"
|
||||||
val CONSTRUCTOR_NAME = "constructor"
|
val CONSTRUCTOR_NAME = "constructor"
|
||||||
|
|
||||||
val CONTINUATION = "\$cont"
|
|
||||||
|
|
||||||
val JS_ERROR = JsNameRef("Error")
|
val JS_ERROR = JsNameRef("Error")
|
||||||
|
|
||||||
val JS_OBJECT = JsNameRef("Object")
|
val JS_OBJECT = JsNameRef("Object")
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
|||||||
import org.jetbrains.kotlin.descriptors.impl.EmptyPackageFragmentDescriptor
|
import org.jetbrains.kotlin.descriptors.impl.EmptyPackageFragmentDescriptor
|
||||||
import org.jetbrains.kotlin.ir.*
|
import org.jetbrains.kotlin.ir.*
|
||||||
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
|
import org.jetbrains.kotlin.ir.backend.js.JsCommonBackendContext
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.JsCommonCoroutineSymbols
|
||||||
import org.jetbrains.kotlin.ir.backend.js.JsMapping
|
import org.jetbrains.kotlin.ir.backend.js.JsMapping
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.JsInnerClassesSupport
|
import org.jetbrains.kotlin.ir.backend.js.lower.JsInnerClassesSupport
|
||||||
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
|
||||||
@@ -57,6 +58,9 @@ class WasmBackendContext(
|
|||||||
|
|
||||||
override val mapping = JsMapping(irFactory)
|
override val mapping = JsMapping(irFactory)
|
||||||
|
|
||||||
|
override val coroutineSymbols =
|
||||||
|
JsCommonCoroutineSymbols(symbolTable, module,this)
|
||||||
|
|
||||||
val innerClassesSupport = JsInnerClassesSupport(mapping, irFactory)
|
val innerClassesSupport = JsInnerClassesSupport(mapping, irFactory)
|
||||||
|
|
||||||
override val internalPackageFqn = FqName("kotlin.wasm")
|
override val internalPackageFqn = FqName("kotlin.wasm")
|
||||||
|
|||||||
+30
-13
@@ -13,6 +13,9 @@ import org.jetbrains.kotlin.backend.common.lower.optimizations.PropertyAccessorI
|
|||||||
import org.jetbrains.kotlin.backend.common.phaser.*
|
import org.jetbrains.kotlin.backend.common.phaser.*
|
||||||
import org.jetbrains.kotlin.backend.wasm.lower.*
|
import org.jetbrains.kotlin.backend.wasm.lower.*
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.*
|
import org.jetbrains.kotlin.ir.backend.js.lower.*
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.AddContinuationToFunctionCallsLowering
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.AddContinuationToNonLocalSuspendFunctionsLowering
|
||||||
|
import org.jetbrains.kotlin.ir.backend.js.lower.coroutines.JsSuspendFunctionsLowering
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.inline.RemoveInlineDeclarationsWithReifiedTypeParametersLowering
|
import org.jetbrains.kotlin.ir.backend.js.lower.inline.RemoveInlineDeclarationsWithReifiedTypeParametersLowering
|
||||||
import org.jetbrains.kotlin.ir.backend.js.lower.inline.WrapInlineDeclarationsWithReifiedTypeParametersLowering
|
import org.jetbrains.kotlin.ir.backend.js.lower.inline.WrapInlineDeclarationsWithReifiedTypeParametersLowering
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||||
@@ -80,14 +83,6 @@ private val lateinitUsageLoweringPhase = makeWasmModulePhase(
|
|||||||
description = "Insert checks for lateinit field references"
|
description = "Insert checks for lateinit field references"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
// TODO make all lambda-related stuff work with IrFunctionExpression and drop this phase
|
|
||||||
private val provisionalFunctionExpressionPhase = makeWasmModulePhase(
|
|
||||||
{ ProvisionalFunctionExpressionLowering() },
|
|
||||||
name = "FunctionExpression",
|
|
||||||
description = "Transform IrFunctionExpression to a local function reference"
|
|
||||||
)
|
|
||||||
|
|
||||||
private val wrapInlineDeclarationsWithReifiedTypeParametersPhase = makeWasmModulePhase(
|
private val wrapInlineDeclarationsWithReifiedTypeParametersPhase = makeWasmModulePhase(
|
||||||
::WrapInlineDeclarationsWithReifiedTypeParametersLowering,
|
::WrapInlineDeclarationsWithReifiedTypeParametersLowering,
|
||||||
name = "WrapInlineDeclarationsWithReifiedTypeParametersPhase",
|
name = "WrapInlineDeclarationsWithReifiedTypeParametersPhase",
|
||||||
@@ -247,6 +242,27 @@ private val innerClassConstructorCallsLoweringPhase = makeWasmModulePhase(
|
|||||||
description = "Replace inner class constructor invocation"
|
description = "Replace inner class constructor invocation"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private val suspendFunctionsLoweringPhase = makeWasmModulePhase(
|
||||||
|
::JsSuspendFunctionsLowering,
|
||||||
|
name = "SuspendFunctionsLowering",
|
||||||
|
description = "Transform suspend functions into CoroutineImpl instance and build state machine"
|
||||||
|
)
|
||||||
|
|
||||||
|
private val addContinuationToNonLocalSuspendFunctionsLoweringPhase = makeWasmModulePhase(
|
||||||
|
::AddContinuationToNonLocalSuspendFunctionsLowering,
|
||||||
|
name = "AddContinuationToNonLocalSuspendFunctionsLowering",
|
||||||
|
description = "Add explicit continuation as last parameter of suspend functions"
|
||||||
|
)
|
||||||
|
|
||||||
|
private val addContinuationToFunctionCallsLoweringPhase = makeWasmModulePhase(
|
||||||
|
::AddContinuationToFunctionCallsLowering,
|
||||||
|
name = "AddContinuationToFunctionCallsLowering",
|
||||||
|
description = "Replace suspend function calls with calls with continuation",
|
||||||
|
prerequisite = setOf(
|
||||||
|
addContinuationToNonLocalSuspendFunctionsLoweringPhase,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
private val defaultArgumentStubGeneratorPhase = makeWasmModulePhase(
|
private val defaultArgumentStubGeneratorPhase = makeWasmModulePhase(
|
||||||
::DefaultArgumentStubGenerator,
|
::DefaultArgumentStubGenerator,
|
||||||
name = "DefaultArgumentStubGenerator",
|
name = "DefaultArgumentStubGenerator",
|
||||||
@@ -461,7 +477,6 @@ val wasmPhases = NamedCompilerPhase(
|
|||||||
wrapInlineDeclarationsWithReifiedTypeParametersPhase then
|
wrapInlineDeclarationsWithReifiedTypeParametersPhase then
|
||||||
|
|
||||||
functionInliningPhase then
|
functionInliningPhase then
|
||||||
provisionalFunctionExpressionPhase then
|
|
||||||
lateinitNullableFieldsPhase then
|
lateinitNullableFieldsPhase then
|
||||||
lateinitDeclarationLoweringPhase then
|
lateinitDeclarationLoweringPhase then
|
||||||
lateinitUsageLoweringPhase then
|
lateinitUsageLoweringPhase then
|
||||||
@@ -483,8 +498,6 @@ val wasmPhases = NamedCompilerPhase(
|
|||||||
propertiesLoweringPhase then
|
propertiesLoweringPhase then
|
||||||
primaryConstructorLoweringPhase then
|
primaryConstructorLoweringPhase then
|
||||||
delegateToPrimaryConstructorLoweringPhase then
|
delegateToPrimaryConstructorLoweringPhase then
|
||||||
initializersLoweringPhase then
|
|
||||||
initializersCleanupLoweringPhase then
|
|
||||||
// Common prefix ends
|
// Common prefix ends
|
||||||
|
|
||||||
enumEntryInstancesLoweringPhase then
|
enumEntryInstancesLoweringPhase then
|
||||||
@@ -495,8 +508,12 @@ val wasmPhases = NamedCompilerPhase(
|
|||||||
enumUsageLoweringPhase then
|
enumUsageLoweringPhase then
|
||||||
enumEntryRemovalLoweringPhase then
|
enumEntryRemovalLoweringPhase then
|
||||||
|
|
||||||
// TODO: Requires stdlib
|
suspendFunctionsLoweringPhase then
|
||||||
// suspendFunctionsLoweringPhase then
|
initializersLoweringPhase then
|
||||||
|
initializersCleanupLoweringPhase then
|
||||||
|
|
||||||
|
addContinuationToNonLocalSuspendFunctionsLoweringPhase then
|
||||||
|
addContinuationToFunctionCallsLoweringPhase then
|
||||||
|
|
||||||
stringConstructorLowering then
|
stringConstructorLowering then
|
||||||
tryCatchCanonicalization then
|
tryCatchCanonicalization then
|
||||||
|
|||||||
@@ -55,24 +55,20 @@ class WasmSymbols(
|
|||||||
get() = TODO()
|
get() = TODO()
|
||||||
override val stringBuilder =
|
override val stringBuilder =
|
||||||
getIrClass(FqName("kotlin.text.StringBuilder"))
|
getIrClass(FqName("kotlin.text.StringBuilder"))
|
||||||
override val coroutineImpl
|
override val coroutineImpl =
|
||||||
get() = TODO()
|
context.coroutineSymbols.coroutineImpl
|
||||||
override val coroutineSuspendedGetter
|
override val coroutineSuspendedGetter =
|
||||||
get() = TODO()
|
context.coroutineSymbols.coroutineSuspendedGetter
|
||||||
override val getContinuation
|
override val getContinuation =
|
||||||
get() = TODO()
|
getInternalFunction("getContinuation")
|
||||||
override val coroutineContextGetter by lazy {
|
override val coroutineContextGetter =
|
||||||
context.irFactory.addFunction(context.getExcludedPackageFragment(FqName("kotlin.excluded"))) {
|
symbolTable.referenceSimpleFunction(context.coroutineSymbols.coroutineContextProperty.getter!!)
|
||||||
name = Name.identifier("coroutineContextGetter\$Stub")
|
override val suspendCoroutineUninterceptedOrReturn =
|
||||||
}.symbol
|
getInternalFunction("suspendCoroutineUninterceptedOrReturn")
|
||||||
}
|
override val coroutineGetContext =
|
||||||
|
getInternalFunction("getCoroutineContext")
|
||||||
override val suspendCoroutineUninterceptedOrReturn
|
override val returnIfSuspended =
|
||||||
get() = TODO()
|
getInternalFunction("returnIfSuspended")
|
||||||
override val coroutineGetContext
|
|
||||||
get() = TODO()
|
|
||||||
override val returnIfSuspended
|
|
||||||
get() = TODO()
|
|
||||||
|
|
||||||
override val functionAdapter: IrClassSymbol
|
override val functionAdapter: IrClassSymbol
|
||||||
get() = TODO()
|
get() = TODO()
|
||||||
@@ -153,6 +149,9 @@ class WasmSymbols(
|
|||||||
val exportString = getInternalFunction("exportString")
|
val exportString = getInternalFunction("exportString")
|
||||||
val unsafeGetScratchRawMemory = getInternalFunction("unsafeGetScratchRawMemory")
|
val unsafeGetScratchRawMemory = getInternalFunction("unsafeGetScratchRawMemory")
|
||||||
|
|
||||||
|
val startCoroutineUninterceptedOrReturnIntrinsics =
|
||||||
|
(0..2).map { getInternalFunction("startCoroutineUninterceptedOrReturnIntrinsic$it") }
|
||||||
|
|
||||||
// KProperty implementations
|
// KProperty implementations
|
||||||
val kLocalDelegatedPropertyImpl: IrClassSymbol = this.getInternalClass("KLocalDelegatedPropertyImpl")
|
val kLocalDelegatedPropertyImpl: IrClassSymbol = this.getInternalClass("KLocalDelegatedPropertyImpl")
|
||||||
val kLocalDelegatedMutablePropertyImpl: IrClassSymbol = this.getInternalClass("KLocalDelegatedMutablePropertyImpl")
|
val kLocalDelegatedMutablePropertyImpl: IrClassSymbol = this.getInternalClass("KLocalDelegatedMutablePropertyImpl")
|
||||||
|
|||||||
+6
-4
@@ -21,10 +21,7 @@ import org.jetbrains.kotlin.ir.expressions.IrExpression
|
|||||||
import org.jetbrains.kotlin.ir.types.IrType
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
import org.jetbrains.kotlin.ir.types.getClass
|
import org.jetbrains.kotlin.ir.types.getClass
|
||||||
import org.jetbrains.kotlin.ir.types.isNullable
|
import org.jetbrains.kotlin.ir.types.isNullable
|
||||||
import org.jetbrains.kotlin.ir.util.functions
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.ir.util.irCall
|
|
||||||
import org.jetbrains.kotlin.ir.util.isFunction
|
|
||||||
import org.jetbrains.kotlin.ir.util.isNullConst
|
|
||||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
|
|
||||||
@@ -121,6 +118,11 @@ class BuiltInsLowering(val context: WasmBackendContext) : FileLoweringPass {
|
|||||||
putValueArgument(0, call.getValueArgument(0))
|
putValueArgument(0, call.getValueArgument(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
in symbols.startCoroutineUninterceptedOrReturnIntrinsics -> {
|
||||||
|
val arity = symbols.startCoroutineUninterceptedOrReturnIntrinsics.indexOf(symbol)
|
||||||
|
val newSymbol = irBuiltins.suspendFunctionN(arity).getSimpleFunction("invoke")!!
|
||||||
|
return irCall(call, newSymbol, argumentsAsReceivers = true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return call
|
return call
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ interface IrDeclarationOrigin {
|
|||||||
object SYNTHETIC_JAVA_PROPERTY_DELEGATE : IrDeclarationOriginImpl("SYNTHETIC_JAVA_PROPERTY_DELEGATE", isSynthetic = true)
|
object SYNTHETIC_JAVA_PROPERTY_DELEGATE : IrDeclarationOriginImpl("SYNTHETIC_JAVA_PROPERTY_DELEGATE", isSynthetic = true)
|
||||||
|
|
||||||
object FIELD_FOR_OUTER_THIS : IrDeclarationOriginImpl("FIELD_FOR_OUTER_THIS", isSynthetic = true)
|
object FIELD_FOR_OUTER_THIS : IrDeclarationOriginImpl("FIELD_FOR_OUTER_THIS", isSynthetic = true)
|
||||||
|
object CONTINUATION : IrDeclarationOriginImpl("CONTINUATION", isSynthetic = true)
|
||||||
|
|
||||||
val isSynthetic: Boolean get() = false
|
val isSynthetic: Boolean get() = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ class JsMapping(val state: JsMappingState) : DefaultMapping(state) {
|
|||||||
val enumEntryToCorrespondingField = state.newDeclarationToDeclarationMapping<IrEnumEntry, IrField>()
|
val enumEntryToCorrespondingField = state.newDeclarationToDeclarationMapping<IrEnumEntry, IrField>()
|
||||||
val enumClassToInitEntryInstancesFun = state.newDeclarationToDeclarationMapping<IrClass, IrSimpleFunction>()
|
val enumClassToInitEntryInstancesFun = state.newDeclarationToDeclarationMapping<IrClass, IrSimpleFunction>()
|
||||||
|
|
||||||
|
val suspendFunctionsToFunctionWithContinuations =
|
||||||
|
state.newDeclarationToDeclarationMapping<IrSimpleFunction, IrSimpleFunction>()
|
||||||
|
|
||||||
val suspendArityStore = state.newDeclarationToDeclarationCollectionMapping<IrClass, Collection<IrSimpleFunction>>()
|
val suspendArityStore = state.newDeclarationToDeclarationCollectionMapping<IrClass, Collection<IrSimpleFunction>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,9 @@ internal fun <T> getContinuation(): Continuation<T> { throw Exception("Implement
|
|||||||
// Do we really need this intrinsic in JS?
|
// Do we really need this intrinsic in JS?
|
||||||
|
|
||||||
@PublishedApi
|
@PublishedApi
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
internal suspend fun <T> returnIfSuspended(@Suppress("UNUSED_PARAMETER") argument: Any?): T {
|
internal suspend fun <T> returnIfSuspended(@Suppress("UNUSED_PARAMETER") argument: Any?): T {
|
||||||
throw Exception("Implemented as intrinsic")
|
return argument as T
|
||||||
}
|
}
|
||||||
|
|
||||||
@PublishedApi
|
@PublishedApi
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@file:Suppress("RedundantSuspendModifier")
|
||||||
|
|
||||||
|
package kotlin.wasm.internal
|
||||||
|
|
||||||
|
import kotlin.coroutines.*
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
@ExcludedFromCodegen
|
||||||
|
internal fun <T> getContinuation(): Continuation<T> =
|
||||||
|
implementedAsIntrinsic
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
internal suspend fun <T> returnIfSuspended(@Suppress("UNUSED_PARAMETER") argument: Any?): T =
|
||||||
|
argument as T
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal fun <T> interceptContinuationIfNeeded(
|
||||||
|
context: CoroutineContext,
|
||||||
|
continuation: Continuation<T>
|
||||||
|
) = context[ContinuationInterceptor]?.interceptContinuation(continuation) ?: continuation
|
||||||
|
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal suspend fun getCoroutineContext(): CoroutineContext = getContinuation<Any?>().context
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal suspend fun <T> suspendCoroutineUninterceptedOrReturn(block: (Continuation<T>) -> Any?): T =
|
||||||
|
returnIfSuspended<T>(block(getContinuation<T>()))
|
||||||
|
|
||||||
|
@ExcludedFromCodegen
|
||||||
|
@PublishedApi
|
||||||
|
internal fun <T> startCoroutineUninterceptedOrReturnIntrinsic0(
|
||||||
|
f: (suspend () -> T),
|
||||||
|
completion: Continuation<T>
|
||||||
|
): Any? {
|
||||||
|
implementedAsIntrinsic
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExcludedFromCodegen
|
||||||
|
@PublishedApi
|
||||||
|
internal fun <R, T> startCoroutineUninterceptedOrReturnIntrinsic1(
|
||||||
|
f: (suspend R.() -> T),
|
||||||
|
receiver: R,
|
||||||
|
completion: Continuation<T>
|
||||||
|
): Any? {
|
||||||
|
implementedAsIntrinsic
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExcludedFromCodegen
|
||||||
|
@PublishedApi
|
||||||
|
internal fun <R, P, T> startCoroutineUninterceptedOrReturnIntrinsic2(
|
||||||
|
f: (suspend R.(P) -> T),
|
||||||
|
receiver: R,
|
||||||
|
param: P,
|
||||||
|
completion: Continuation<T>
|
||||||
|
): Any? {
|
||||||
|
implementedAsIntrinsic
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 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 kotlin.coroutines
|
||||||
|
|
||||||
|
import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
|
||||||
|
|
||||||
|
@SinceKotlin("1.3")
|
||||||
|
internal abstract class CoroutineImpl(private val resultContinuation: Continuation<Any?>?) : Continuation<Any?> {
|
||||||
|
protected var state = 0
|
||||||
|
protected var exceptionState = 0
|
||||||
|
protected var result: Any? = null
|
||||||
|
protected var exception: Throwable? = null
|
||||||
|
protected var finallyPath: Array<Int>? = null
|
||||||
|
|
||||||
|
private val _context: CoroutineContext? = resultContinuation?.context
|
||||||
|
|
||||||
|
public override val context: CoroutineContext get() = _context!!
|
||||||
|
|
||||||
|
private var intercepted_: Continuation<Any?>? = null
|
||||||
|
|
||||||
|
public fun intercepted(): Continuation<Any?> =
|
||||||
|
intercepted_
|
||||||
|
?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
|
||||||
|
.also { intercepted_ = it }
|
||||||
|
|
||||||
|
override fun resumeWith(result: Result<Any?>) {
|
||||||
|
var current = this
|
||||||
|
var currentResult: Any? = result.getOrNull()
|
||||||
|
var currentException: Throwable? = result.exceptionOrNull()
|
||||||
|
|
||||||
|
// This loop unrolls recursion in current.resumeWith(param) to make saner and shorter stack traces on resume
|
||||||
|
while (true) {
|
||||||
|
with(current) {
|
||||||
|
// Set result and exception fields in the current continuation
|
||||||
|
if (currentException == null) {
|
||||||
|
this.result = currentResult
|
||||||
|
} else {
|
||||||
|
state = exceptionState
|
||||||
|
exception = currentException
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val outcome = doResume()
|
||||||
|
if (outcome === COROUTINE_SUSPENDED) return
|
||||||
|
currentResult = outcome
|
||||||
|
currentException = null
|
||||||
|
} catch (exception: Throwable) { // Catch all exceptions
|
||||||
|
currentResult = null
|
||||||
|
currentException = exception
|
||||||
|
}
|
||||||
|
|
||||||
|
releaseIntercepted() // this state machine instance is terminating
|
||||||
|
|
||||||
|
val completion = resultContinuation!!
|
||||||
|
|
||||||
|
if (completion is CoroutineImpl) {
|
||||||
|
// unrolling recursion via loop
|
||||||
|
current = completion
|
||||||
|
} else {
|
||||||
|
// top-level completion reached -- invoke and return
|
||||||
|
if (currentException != null) {
|
||||||
|
completion.resumeWithException(currentException!!)
|
||||||
|
} else {
|
||||||
|
completion.resume(currentResult)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun releaseIntercepted() {
|
||||||
|
val intercepted = intercepted_
|
||||||
|
if (intercepted != null && intercepted !== this) {
|
||||||
|
context[ContinuationInterceptor]!!.releaseInterceptedContinuation(intercepted)
|
||||||
|
}
|
||||||
|
this.intercepted_ = CompletedContinuation // just in case
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract fun doResume(): Any?
|
||||||
|
|
||||||
|
public open fun create(completion: Continuation<*>): Continuation<Unit> {
|
||||||
|
throw UnsupportedOperationException("create(Continuation) has not been overridden")
|
||||||
|
}
|
||||||
|
|
||||||
|
public open fun create(value: Any?, completion: Continuation<*>): Continuation<Unit> {
|
||||||
|
throw UnsupportedOperationException("create(Any?;Continuation) has not been overridden")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal object CompletedContinuation : Continuation<Any?> {
|
||||||
|
override val context: CoroutineContext
|
||||||
|
get() = error("This continuation is already complete")
|
||||||
|
|
||||||
|
override fun resumeWith(result: Result<Any?>) {
|
||||||
|
error("This continuation is already complete")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String = "This continuation is already complete"
|
||||||
|
}
|
||||||
@@ -1,21 +1,53 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* Copyright 2010-2021 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.
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package kotlin.coroutines
|
package kotlin.coroutines
|
||||||
|
|
||||||
|
import kotlin.coroutines.intrinsics.CoroutineSingletons.*
|
||||||
|
import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
|
||||||
|
|
||||||
@PublishedApi
|
@PublishedApi
|
||||||
@SinceKotlin("1.3")
|
@SinceKotlin("1.3")
|
||||||
internal actual class SafeContinuation<in T> : Continuation<T> {
|
internal actual class SafeContinuation<in T>
|
||||||
actual internal constructor(delegate: Continuation<T>, initialResult: Any?) { TODO("Wasm stdlib: Coroutines") }
|
internal actual constructor(
|
||||||
|
private val delegate: Continuation<T>,
|
||||||
|
initialResult: Any?
|
||||||
|
) : Continuation<T> {
|
||||||
|
@PublishedApi
|
||||||
|
internal actual constructor(delegate: Continuation<T>) : this(delegate, UNDECIDED)
|
||||||
|
|
||||||
|
public actual override val context: CoroutineContext
|
||||||
|
get() = delegate.context
|
||||||
|
|
||||||
|
private var result: Any? = initialResult
|
||||||
|
|
||||||
|
public actual override fun resumeWith(result: Result<T>) {
|
||||||
|
val cur = this.result
|
||||||
|
when {
|
||||||
|
cur === UNDECIDED -> {
|
||||||
|
this.result = result.value
|
||||||
|
}
|
||||||
|
cur === COROUTINE_SUSPENDED -> {
|
||||||
|
this.result = RESUMED
|
||||||
|
delegate.resumeWith(result)
|
||||||
|
}
|
||||||
|
else -> throw IllegalStateException("Already resumed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@PublishedApi
|
@PublishedApi
|
||||||
actual internal constructor(delegate: Continuation<T>) { TODO("Wasm stdlib: Coroutines") }
|
internal actual fun getOrThrow(): Any? {
|
||||||
|
if (result === UNDECIDED) {
|
||||||
@PublishedApi
|
result = COROUTINE_SUSPENDED
|
||||||
actual internal fun getOrThrow(): Any? = TODO("Wasm stdlib: Coroutines")
|
return COROUTINE_SUSPENDED
|
||||||
|
}
|
||||||
actual override val context: CoroutineContext = TODO("Wasm stdlib: Coroutines")
|
val result = this.result
|
||||||
actual override fun resumeWith(result: Result<T>): Unit { TODO("Wasm stdlib: Coroutines") }
|
return when {
|
||||||
}
|
result === RESUMED -> COROUTINE_SUSPENDED // already called continuation, indicate COROUTINE_SUSPENDED upstream
|
||||||
|
result is Result.Failure -> throw result.exception
|
||||||
|
else -> result // either COROUTINE_SUSPENDED or data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* Copyright 2010-2021 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.
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package kotlin.coroutines.intrinsics
|
package kotlin.coroutines.intrinsics
|
||||||
|
|
||||||
import kotlin.coroutines.Continuation
|
import kotlin.coroutines.*
|
||||||
import kotlin.coroutines.ContinuationInterceptor
|
import kotlin.wasm.internal.*
|
||||||
import kotlin.coroutines.CoroutineContext
|
|
||||||
import kotlin.internal.InlineOnly
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts an unintercepted coroutine without a receiver and with result type [T] and executes it until its first suspension.
|
* Starts an unintercepted coroutine without a receiver and with result type [T] and executes it until its first suspension.
|
||||||
@@ -22,10 +20,11 @@ import kotlin.internal.InlineOnly
|
|||||||
* This function is designed to be used from inside of [suspendCoroutineUninterceptedOrReturn] to resume the execution of the suspended
|
* This function is designed to be used from inside of [suspendCoroutineUninterceptedOrReturn] to resume the execution of the suspended
|
||||||
* coroutine using a reference to the suspending function.
|
* coroutine using a reference to the suspending function.
|
||||||
*/
|
*/
|
||||||
@SinceKotlin("1.3")
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
@kotlin.internal.InlineOnly
|
||||||
public actual inline fun <T> (suspend () -> T).startCoroutineUninterceptedOrReturn(
|
public actual inline fun <T> (suspend () -> T).startCoroutineUninterceptedOrReturn(
|
||||||
completion: Continuation<T>
|
completion: Continuation<T>
|
||||||
): Any? = TODO("Wasm stdlib: Coroutines intrinsics")
|
): Any? = startCoroutineUninterceptedOrReturnIntrinsic0(this, completion)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts an unintercepted coroutine with receiver type [R] and result type [T] and executes it until its first suspension.
|
* Starts an unintercepted coroutine with receiver type [R] and result type [T] and executes it until its first suspension.
|
||||||
@@ -39,29 +38,81 @@ public actual inline fun <T> (suspend () -> T).startCoroutineUninterceptedOrRetu
|
|||||||
* This function is designed to be used from inside of [suspendCoroutineUninterceptedOrReturn] to resume the execution of the suspended
|
* This function is designed to be used from inside of [suspendCoroutineUninterceptedOrReturn] to resume the execution of the suspended
|
||||||
* coroutine using a reference to the suspending function.
|
* coroutine using a reference to the suspending function.
|
||||||
*/
|
*/
|
||||||
@SinceKotlin("1.3")
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
@kotlin.internal.InlineOnly
|
||||||
public actual inline fun <R, T> (suspend R.() -> T).startCoroutineUninterceptedOrReturn(
|
public actual inline fun <R, T> (suspend R.() -> T).startCoroutineUninterceptedOrReturn(
|
||||||
receiver: R,
|
receiver: R,
|
||||||
completion: Continuation<T>
|
completion: Continuation<T>
|
||||||
): Any? = TODO("Wasm stdlib: Coroutines intrinsics")
|
): Any? = startCoroutineUninterceptedOrReturnIntrinsic1(this, receiver, completion)
|
||||||
|
|
||||||
@InlineOnly
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
@kotlin.internal.InlineOnly
|
||||||
internal actual inline fun <R, P, T> (suspend R.(P) -> T).startCoroutineUninterceptedOrReturn(
|
internal actual inline fun <R, P, T> (suspend R.(P) -> T).startCoroutineUninterceptedOrReturn(
|
||||||
receiver: R,
|
receiver: R,
|
||||||
param: P,
|
param: P,
|
||||||
completion: Continuation<T>
|
completion: Continuation<T>
|
||||||
): Any? = TODO("Wasm stdlib: Coroutines intrinsics")
|
): Any? = startCoroutineUninterceptedOrReturnIntrinsic2(this, receiver, param, completion)
|
||||||
|
|
||||||
@SinceKotlin("1.3")
|
/**
|
||||||
|
* Creates unintercepted coroutine without receiver and with result type [T].
|
||||||
|
* This function creates a new, fresh instance of suspendable computation every time it is invoked.
|
||||||
|
*
|
||||||
|
* To start executing the created coroutine, invoke `resume(Unit)` on the returned [Continuation] instance.
|
||||||
|
* The [completion] continuation is invoked when coroutine completes with result or exception.
|
||||||
|
*
|
||||||
|
* This function returns unintercepted continuation.
|
||||||
|
* Invocation of `resume(Unit)` starts coroutine immediately in the invoker's call stack without going through the
|
||||||
|
* [ContinuationInterceptor] that might be present in the completion's [CoroutineContext].
|
||||||
|
* It is the invoker's responsibility to ensure that a proper invocation context is established.
|
||||||
|
* Note that [completion] of this function may get invoked in an arbitrary context.
|
||||||
|
*
|
||||||
|
* [Continuation.intercepted] can be used to acquire the intercepted continuation.
|
||||||
|
* Invocation of `resume(Unit)` on intercepted continuation guarantees that execution of
|
||||||
|
* both the coroutine and [completion] happens in the invocation context established by
|
||||||
|
* [ContinuationInterceptor].
|
||||||
|
*
|
||||||
|
* Repeated invocation of any resume function on the resulting continuation corrupts the
|
||||||
|
* state machine of the coroutine and may result in arbitrary behaviour or exception.
|
||||||
|
*/
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
public actual fun <T> (suspend () -> T).createCoroutineUnintercepted(
|
public actual fun <T> (suspend () -> T).createCoroutineUnintercepted(
|
||||||
completion: Continuation<T>
|
completion: Continuation<T>
|
||||||
): Continuation<Unit> = TODO("Wasm stdlib: Coroutines intrinsics")
|
): Continuation<Unit> {
|
||||||
|
return createCoroutineFromSuspendFunction(completion) {
|
||||||
|
this.startCoroutineUninterceptedOrReturn(completion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SinceKotlin("1.3")
|
/**
|
||||||
|
* Creates unintercepted coroutine with receiver type [R] and result type [T].
|
||||||
|
* This function creates a new, fresh instance of suspendable computation every time it is invoked.
|
||||||
|
*
|
||||||
|
* To start executing the created coroutine, invoke `resume(Unit)` on the returned [Continuation] instance.
|
||||||
|
* The [completion] continuation is invoked when coroutine completes with result or exception.
|
||||||
|
*
|
||||||
|
* This function returns unintercepted continuation.
|
||||||
|
* Invocation of `resume(Unit)` starts coroutine immediately in the invoker's call stack without going through the
|
||||||
|
* [ContinuationInterceptor] that might be present in the completion's [CoroutineContext].
|
||||||
|
* It is the invoker's responsibility to ensure that a proper invocation context is established.
|
||||||
|
* Note that [completion] of this function may get invoked in an arbitrary context.
|
||||||
|
*
|
||||||
|
* [Continuation.intercepted] can be used to acquire the intercepted continuation.
|
||||||
|
* Invocation of `resume(Unit)` on intercepted continuation guarantees that execution of
|
||||||
|
* both the coroutine and [completion] happens in the invocation context established by
|
||||||
|
* [ContinuationInterceptor].
|
||||||
|
*
|
||||||
|
* Repeated invocation of any resume function on the resulting continuation corrupts the
|
||||||
|
* state machine of the coroutine and may result in arbitrary behaviour or exception.
|
||||||
|
*/
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
public actual fun <R, T> (suspend R.() -> T).createCoroutineUnintercepted(
|
public actual fun <R, T> (suspend R.() -> T).createCoroutineUnintercepted(
|
||||||
receiver: R,
|
receiver: R,
|
||||||
completion: Continuation<T>
|
completion: Continuation<T>
|
||||||
): Continuation<Unit> = TODO("Wasm stdlib: Coroutines intrinsics")
|
): Continuation<Unit> {
|
||||||
|
return createCoroutineFromSuspendFunction(completion) {
|
||||||
|
this.startCoroutineUninterceptedOrReturn(receiver, completion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Intercepts this continuation with [ContinuationInterceptor].
|
* Intercepts this continuation with [ContinuationInterceptor].
|
||||||
@@ -72,5 +123,18 @@ public actual fun <R, T> (suspend R.() -> T).createCoroutineUnintercepted(
|
|||||||
*
|
*
|
||||||
* If this function is invoked on other [Continuation] instances it returns `this` continuation unchanged.
|
* If this function is invoked on other [Continuation] instances it returns `this` continuation unchanged.
|
||||||
*/
|
*/
|
||||||
@SinceKotlin("1.3")
|
public actual fun <T> Continuation<T>.intercepted(): Continuation<T> =
|
||||||
public actual fun <T> Continuation<T>.intercepted(): Continuation<T> = TODO("Wasm stdlib: Coroutines intrinsics")
|
(this as? CoroutineImpl)?.intercepted() ?: this
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
private inline fun <T> createCoroutineFromSuspendFunction(
|
||||||
|
completion: Continuation<T>,
|
||||||
|
crossinline block: () -> Any?
|
||||||
|
): Continuation<Unit> {
|
||||||
|
return object : CoroutineImpl(completion as Continuation<Any?>) {
|
||||||
|
override fun doResume(): Any? {
|
||||||
|
exception?.let { throw it }
|
||||||
|
return block()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user