[JS IR BE] Fix state machine generation in case of composition of loops, inline functions and finally blocks
* lower finally blocks in any cases * do not optimize exit blocks for if-statements
This commit is contained in:
+2
-2
@@ -36,7 +36,7 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
|
|||||||
protected abstract fun nameForCoroutineClass(function: IrFunction): Name
|
protected abstract fun nameForCoroutineClass(function: IrFunction): Name
|
||||||
|
|
||||||
protected abstract fun buildStateMachine(
|
protected abstract fun buildStateMachine(
|
||||||
originalBody: IrBody, stateMachineFunction: IrFunction,
|
stateMachineFunction: IrFunction,
|
||||||
transformingFunction: IrFunction,
|
transformingFunction: IrFunction,
|
||||||
argumentToPropertiesMap: Map<IrValueParameter, IrField>
|
argumentToPropertiesMap: Map<IrValueParameter, IrField>
|
||||||
)
|
)
|
||||||
@@ -617,7 +617,7 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildStateMachine(originalBody, function, irFunction, argumentToPropertiesMap)
|
buildStateMachine(function, irFunction, argumentToPropertiesMap)
|
||||||
return function
|
return function
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-21
@@ -216,11 +216,6 @@ class FinallyBlocksLowering(val context: CommonBackendContext, private val throw
|
|||||||
val irBuilder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, startOffset, endOffset)
|
val irBuilder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, startOffset, endOffset)
|
||||||
val transformer = this
|
val transformer = this
|
||||||
irBuilder.run {
|
irBuilder.run {
|
||||||
val transformedTry = IrTryImpl(
|
|
||||||
startOffset = startOffset,
|
|
||||||
endOffset = endOffset,
|
|
||||||
type = context.irBuiltIns.unitType
|
|
||||||
)
|
|
||||||
val transformedFinallyExpression = finallyExpression.transform(transformer, null)
|
val transformedFinallyExpression = finallyExpression.transform(transformer, null)
|
||||||
val parameter = WrappedVariableDescriptor()
|
val parameter = WrappedVariableDescriptor()
|
||||||
val catchParameter = IrVariableImpl(
|
val catchParameter = IrVariableImpl(
|
||||||
@@ -233,27 +228,42 @@ class FinallyBlocksLowering(val context: CommonBackendContext, private val throw
|
|||||||
val syntheticTry = IrTryImpl(
|
val syntheticTry = IrTryImpl(
|
||||||
startOffset = startOffset,
|
startOffset = startOffset,
|
||||||
endOffset = endOffset,
|
endOffset = endOffset,
|
||||||
type = context.irBuiltIns.unitType,
|
type = context.irBuiltIns.unitType
|
||||||
tryResult = transformedTry,
|
).apply {
|
||||||
catches = listOf(
|
this.catches += irCatch(catchParameter).apply {
|
||||||
irCatch(catchParameter).apply {
|
result = irComposite {
|
||||||
result = irComposite {
|
+finallyExpression.copy()
|
||||||
+finallyExpression.copy()
|
+irThrow(irGet(catchParameter))
|
||||||
+irThrow(irGet(catchParameter))
|
}
|
||||||
}
|
}
|
||||||
}),
|
|
||||||
finallyExpression = null
|
this.finallyExpression = null
|
||||||
)
|
}
|
||||||
|
|
||||||
using(TryScope(syntheticTry, transformedFinallyExpression, this)) {
|
using(TryScope(syntheticTry, transformedFinallyExpression, this)) {
|
||||||
|
|
||||||
val fallThroughType = aTry.type
|
val fallThroughType = aTry.type
|
||||||
val fallThroughSymbol = IrReturnableBlockSymbolImpl(WrappedSimpleFunctionDescriptor())
|
val fallThroughSymbol = IrReturnableBlockSymbolImpl(WrappedSimpleFunctionDescriptor())
|
||||||
val transformedResult = aTry.tryResult.transform(transformer, null)
|
val transformedResult = aTry.tryResult.transform(transformer, null)
|
||||||
transformedTry.tryResult = irReturn(fallThroughSymbol, transformedResult)
|
val returnedResult = irReturn(fallThroughSymbol, transformedResult)
|
||||||
for (aCatch in aTry.catches) {
|
|
||||||
val transformedCatch = aCatch.transform(transformer, null)
|
if (aTry.catches.isNotEmpty()) {
|
||||||
transformedCatch.result = irReturn(fallThroughSymbol, transformedCatch.result)
|
val transformedTry = IrTryImpl(
|
||||||
transformedTry.catches.add(transformedCatch)
|
startOffset = startOffset,
|
||||||
|
endOffset = endOffset,
|
||||||
|
type = context.irBuiltIns.unitType
|
||||||
|
)
|
||||||
|
transformedTry.tryResult = returnedResult
|
||||||
|
for (aCatch in aTry.catches) {
|
||||||
|
val transformedCatch = aCatch.transform(transformer, null)
|
||||||
|
transformedCatch.result = irReturn(fallThroughSymbol, transformedCatch.result)
|
||||||
|
transformedTry.catches.add(transformedCatch)
|
||||||
|
}
|
||||||
|
syntheticTry.tryResult = transformedTry
|
||||||
|
} else {
|
||||||
|
syntheticTry.tryResult = returnedResult
|
||||||
}
|
}
|
||||||
|
|
||||||
return irInlineFinally(fallThroughSymbol, fallThroughType, it.expression, it.finallyExpression)
|
return irInlineFinally(fallThroughSymbol, fallThroughType, it.expression, it.finallyExpression)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -380,8 +380,8 @@ val jsPhases = namedIrModulePhase(
|
|||||||
moveBodilessDeclarationsToSeparatePlacePhase then
|
moveBodilessDeclarationsToSeparatePlacePhase then
|
||||||
enumClassLoweringPhase then
|
enumClassLoweringPhase then
|
||||||
enumUsageLoweringPhase then
|
enumUsageLoweringPhase then
|
||||||
returnableBlockLoweringPhase then
|
|
||||||
suspendFunctionsLoweringPhase then
|
suspendFunctionsLoweringPhase then
|
||||||
|
returnableBlockLoweringPhase then
|
||||||
privateMembersLoweringPhase then
|
privateMembersLoweringPhase then
|
||||||
callableReferenceLoweringPhase then
|
callableReferenceLoweringPhase then
|
||||||
defaultArgumentStubGeneratorPhase then
|
defaultArgumentStubGeneratorPhase then
|
||||||
|
|||||||
+48
-43
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.ir.backend.js.lower.coroutines
|
|||||||
import org.jetbrains.kotlin.backend.common.descriptors.synthesizedName
|
import org.jetbrains.kotlin.backend.common.descriptors.synthesizedName
|
||||||
import org.jetbrains.kotlin.backend.common.ir.isSuspend
|
import org.jetbrains.kotlin.backend.common.ir.isSuspend
|
||||||
import org.jetbrains.kotlin.backend.common.lower.AbstractSuspendFunctionsLowering
|
import org.jetbrains.kotlin.backend.common.lower.AbstractSuspendFunctionsLowering
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.backend.common.lower.FinallyBlocksLowering
|
||||||
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.*
|
||||||
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.ir.declarations.*
|
|||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
|
||||||
import org.jetbrains.kotlin.ir.util.explicitParameters
|
import org.jetbrains.kotlin.ir.util.explicitParameters
|
||||||
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
|
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
|
||||||
@@ -43,21 +44,20 @@ class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunct
|
|||||||
override fun nameForCoroutineClass(function: IrFunction) = "${function.name}COROUTINE\$${coroutineId++}".synthesizedName
|
override fun nameForCoroutineClass(function: IrFunction) = "${function.name}COROUTINE\$${coroutineId++}".synthesizedName
|
||||||
|
|
||||||
override fun buildStateMachine(
|
override fun buildStateMachine(
|
||||||
originalBody: IrBody,
|
|
||||||
stateMachineFunction: IrFunction,
|
stateMachineFunction: IrFunction,
|
||||||
transformingFunction: IrFunction,
|
transformingFunction: IrFunction,
|
||||||
argumentToPropertiesMap: Map<IrValueParameter, IrField>
|
argumentToPropertiesMap: Map<IrValueParameter, IrField>
|
||||||
) {
|
) {
|
||||||
val body =
|
val simplifiedFunction = transformingFunction.transform(FinallyBlocksLowering(context, context.dynamicType), null) as IrFunction
|
||||||
(originalBody as IrBlockBody).run {
|
val originalBody = simplifiedFunction.body as IrBlockBody
|
||||||
IrBlockImpl(
|
|
||||||
transformingFunction.startOffset,
|
val body = IrBlockImpl(
|
||||||
transformingFunction.endOffset,
|
simplifiedFunction.startOffset,
|
||||||
context.irBuiltIns.unitType,
|
simplifiedFunction.endOffset,
|
||||||
STATEMENT_ORIGIN_COROUTINE_IMPL,
|
context.irBuiltIns.unitType,
|
||||||
statements
|
STATEMENT_ORIGIN_COROUTINE_IMPL,
|
||||||
)
|
originalBody.statements
|
||||||
}
|
)
|
||||||
|
|
||||||
val coroutineClass = stateMachineFunction.parent as IrClass
|
val coroutineClass = stateMachineFunction.parent as IrClass
|
||||||
val suspendResult = JsIrBuilder.buildVar(
|
val suspendResult = JsIrBuilder.buildVar(
|
||||||
@@ -83,11 +83,11 @@ class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunct
|
|||||||
COROUTINE_ROOT_LOOP,
|
COROUTINE_ROOT_LOOP,
|
||||||
rootTry,
|
rootTry,
|
||||||
JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, true)
|
JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, true)
|
||||||
)
|
).also {
|
||||||
|
it.label = "\$sm"
|
||||||
|
}
|
||||||
|
|
||||||
val suspendableNodes = mutableSetOf<IrElement>()
|
val suspendableNodes = collectSuspendableNodes(body)
|
||||||
val loweredBody =
|
|
||||||
collectSuspendableNodes(body, suspendableNodes, context, stateMachineFunction, context.dynamicType)
|
|
||||||
val thisReceiver = (stateMachineFunction.dispatchReceiverParameter as IrValueParameter).symbol
|
val thisReceiver = (stateMachineFunction.dispatchReceiverParameter as IrValueParameter).symbol
|
||||||
|
|
||||||
val stateMachineBuilder = StateMachineBuilder(
|
val stateMachineBuilder = StateMachineBuilder(
|
||||||
@@ -104,36 +104,13 @@ class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunct
|
|||||||
suspendResult.symbol
|
suspendResult.symbol
|
||||||
)
|
)
|
||||||
|
|
||||||
loweredBody.acceptVoid(stateMachineBuilder)
|
body.acceptVoid(stateMachineBuilder)
|
||||||
|
|
||||||
stateMachineBuilder.finalizeStateMachine()
|
stateMachineBuilder.finalizeStateMachine()
|
||||||
|
|
||||||
rootTry.catches += stateMachineBuilder.globalCatch
|
rootTry.catches += stateMachineBuilder.globalCatch
|
||||||
|
|
||||||
val visited = mutableSetOf<SuspendState>()
|
assignStateIds(stateMachineBuilder.entryState, thisReceiver, switch, rootLoop)
|
||||||
|
|
||||||
val sortedStates = DFS.topologicalOrder(listOf(stateMachineBuilder.entryState), { it.successors }, { visited.add(it) })
|
|
||||||
sortedStates.withIndex().forEach { it.value.id = it.index }
|
|
||||||
|
|
||||||
fun buildDispatch(target: SuspendState) = target.run {
|
|
||||||
assert(id >= 0)
|
|
||||||
JsIrBuilder.buildInt(context.irBuiltIns.intType, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
val eqeqeqInt = context.irBuiltIns.eqeqeqSymbol
|
|
||||||
|
|
||||||
for (state in sortedStates) {
|
|
||||||
val condition = JsIrBuilder.buildCall(eqeqeqInt).apply {
|
|
||||||
putValueArgument(0, JsIrBuilder.buildCall(coroutineImplLabelPropertyGetter.symbol).also {
|
|
||||||
it.dispatchReceiver = JsIrBuilder.buildGetValue(thisReceiver)
|
|
||||||
})
|
|
||||||
putValueArgument(1, JsIrBuilder.buildInt(context.irBuiltIns.intType, state.id))
|
|
||||||
}
|
|
||||||
|
|
||||||
switch.branches += IrBranchImpl(state.entryBlock.startOffset, state.entryBlock.endOffset, condition, state.entryBlock)
|
|
||||||
}
|
|
||||||
|
|
||||||
rootLoop.transform(DispatchPointTransformer(::buildDispatch), null)
|
|
||||||
|
|
||||||
exceptionTrapId = stateMachineBuilder.rootExceptionTrap.id
|
exceptionTrapId = stateMachineBuilder.rootExceptionTrap.id
|
||||||
|
|
||||||
@@ -149,7 +126,7 @@ class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunct
|
|||||||
override fun visitReturn(expression: IrReturn): IrExpression {
|
override fun visitReturn(expression: IrReturn): IrExpression {
|
||||||
expression.transformChildrenVoid(this)
|
expression.transformChildrenVoid(this)
|
||||||
|
|
||||||
return if (expression.returnTargetSymbol != transformingFunction.symbol)
|
return if (expression.returnTargetSymbol != simplifiedFunction.symbol)
|
||||||
expression
|
expression
|
||||||
else
|
else
|
||||||
JsIrBuilder.buildReturn(stateMachineFunction.symbol, expression.value, expression.type)
|
JsIrBuilder.buildReturn(stateMachineFunction.symbol, expression.value, expression.type)
|
||||||
@@ -169,7 +146,7 @@ class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunct
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
transformingFunction.explicitParameters.forEach {
|
simplifiedFunction.explicitParameters.forEach {
|
||||||
localToPropertyMap.getOrPut(it.symbol) {
|
localToPropertyMap.getOrPut(it.symbol) {
|
||||||
argumentToPropertiesMap.getValue(it).symbol
|
argumentToPropertiesMap.getValue(it).symbol
|
||||||
}
|
}
|
||||||
@@ -178,6 +155,33 @@ class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunct
|
|||||||
stateMachineFunction.transform(LiveLocalsTransformer(localToPropertyMap, { JsIrBuilder.buildGetValue(thisReceiver) }, unit), null)
|
stateMachineFunction.transform(LiveLocalsTransformer(localToPropertyMap, { JsIrBuilder.buildGetValue(thisReceiver) }, unit), null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun assignStateIds(entryState: SuspendState, thisReceiver: IrValueParameterSymbol, switch: IrWhen, rootLoop: IrLoop) {
|
||||||
|
val visited = mutableSetOf<SuspendState>()
|
||||||
|
|
||||||
|
val sortedStates = DFS.topologicalOrder(listOf(entryState), { it.successors }, { visited.add(it) })
|
||||||
|
sortedStates.withIndex().forEach { it.value.id = it.index }
|
||||||
|
|
||||||
|
val eqeqeqInt = context.irBuiltIns.eqeqeqSymbol
|
||||||
|
|
||||||
|
for (state in sortedStates) {
|
||||||
|
val condition = JsIrBuilder.buildCall(eqeqeqInt).apply {
|
||||||
|
putValueArgument(0, JsIrBuilder.buildCall(coroutineImplLabelPropertyGetter.symbol).also {
|
||||||
|
it.dispatchReceiver = JsIrBuilder.buildGetValue(thisReceiver)
|
||||||
|
})
|
||||||
|
putValueArgument(1, JsIrBuilder.buildInt(context.irBuiltIns.intType, state.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch.branches += IrBranchImpl(state.entryBlock.startOffset, state.entryBlock.endOffset, condition, state.entryBlock)
|
||||||
|
}
|
||||||
|
|
||||||
|
val dispatchPointTransformer = DispatchPointTransformer {
|
||||||
|
assert(it.id >= 0)
|
||||||
|
JsIrBuilder.buildInt(context.irBuiltIns.intType, it.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
rootLoop.transformChildrenVoid(dispatchPointTransformer)
|
||||||
|
}
|
||||||
|
|
||||||
private fun computeLivenessAtSuspensionPoints(body: IrBody): Map<IrCall, List<IrValueDeclaration>> {
|
private fun computeLivenessAtSuspensionPoints(body: IrBody): Map<IrCall, List<IrValueDeclaration>> {
|
||||||
// TODO: data flow analysis.
|
// TODO: data flow analysis.
|
||||||
// Just save all visible for now.
|
// Just save all visible for now.
|
||||||
@@ -201,6 +205,7 @@ class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunct
|
|||||||
for (it in coroutineConstructors) {
|
for (it in coroutineConstructors) {
|
||||||
(it.body as? IrBlockBody)?.run {
|
(it.body as? IrBlockBody)?.run {
|
||||||
val receiver = JsIrBuilder.buildGetValue(coroutineClassThis.symbol)
|
val receiver = JsIrBuilder.buildGetValue(coroutineClassThis.symbol)
|
||||||
|
assert(exceptionTrapId >= 0)
|
||||||
val id = JsIrBuilder.buildInt(context.irBuiltIns.intType, exceptionTrapId)
|
val id = JsIrBuilder.buildInt(context.irBuiltIns.intType, exceptionTrapId)
|
||||||
statements += JsIrBuilder.buildCall(coroutineImplExceptionStatePropertySetter.symbol).also { call ->
|
statements += JsIrBuilder.buildCall(coroutineImplExceptionStatePropertySetter.symbol).also { call ->
|
||||||
call.dispatchReceiver = receiver
|
call.dispatchReceiver = receiver
|
||||||
|
|||||||
+27
-100
@@ -23,10 +23,7 @@ import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
|||||||
import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol
|
||||||
import org.jetbrains.kotlin.ir.types.IrDynamicType
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
import org.jetbrains.kotlin.ir.types.IrType
|
|
||||||
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
|
||||||
import org.jetbrains.kotlin.ir.types.isNothing
|
|
||||||
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
|
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
|
||||||
import org.jetbrains.kotlin.ir.visitors.*
|
import org.jetbrains.kotlin.ir.visitors.*
|
||||||
|
|
||||||
@@ -38,9 +35,7 @@ class SuspendState(type: IrType) {
|
|||||||
|
|
||||||
data class LoopBounds(val headState: SuspendState, val exitState: SuspendState)
|
data class LoopBounds(val headState: SuspendState, val exitState: SuspendState)
|
||||||
|
|
||||||
data class FinallyTargets(val normal: SuspendState, val fromThrow: SuspendState)
|
data class TryState(val tryState: SuspendState, val catchState: SuspendState)
|
||||||
|
|
||||||
data class TryState(val tryState: SuspendState, val catchState: SuspendState, val finallyState: FinallyTargets?)
|
|
||||||
|
|
||||||
class IrDispatchPoint(val target: SuspendState) : IrExpressionBase(UNDEFINED_OFFSET, UNDEFINED_OFFSET, target.entryBlock.type) {
|
class IrDispatchPoint(val target: SuspendState) : IrExpressionBase(UNDEFINED_OFFSET, UNDEFINED_OFFSET, target.entryBlock.type) {
|
||||||
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D) = visitor.visitExpression(this, data)
|
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D) = visitor.visitExpression(this, data)
|
||||||
@@ -50,7 +45,6 @@ class IrDispatchPoint(val target: SuspendState) : IrExpressionBase(UNDEFINED_OFF
|
|||||||
override fun <D> transformChildren(transformer: IrElementTransformer<D>, data: D) {}
|
override fun <D> transformChildren(transformer: IrElementTransformer<D>, data: D) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class DispatchPointTransformer(val action: (SuspendState) -> IrExpression) : IrElementTransformerVoid() {
|
class DispatchPointTransformer(val action: (SuspendState) -> IrExpression) : IrElementTransformerVoid() {
|
||||||
override fun visitExpression(expression: IrExpression): IrExpression {
|
override fun visitExpression(expression: IrExpression): IrExpression {
|
||||||
val dispatchPoint = expression as? IrDispatchPoint
|
val dispatchPoint = expression as? IrDispatchPoint
|
||||||
@@ -76,7 +70,6 @@ class StateMachineBuilder(
|
|||||||
private val loopMap = mutableMapOf<IrLoop, LoopBounds>()
|
private val loopMap = mutableMapOf<IrLoop, LoopBounds>()
|
||||||
private val unit = context.irBuiltIns.unitType
|
private val unit = context.irBuiltIns.unitType
|
||||||
private val nothing = context.irBuiltIns.nothingType
|
private val nothing = context.irBuiltIns.nothingType
|
||||||
private val int = context.irBuiltIns.intType
|
|
||||||
private val booleanNotSymbol = context.irBuiltIns.booleanNotSymbol
|
private val booleanNotSymbol = context.irBuiltIns.booleanNotSymbol
|
||||||
private val eqeqeqSymbol = context.irBuiltIns.eqeqeqSymbol
|
private val eqeqeqSymbol = context.irBuiltIns.eqeqeqSymbol
|
||||||
|
|
||||||
@@ -166,9 +159,13 @@ class StateMachineBuilder(
|
|||||||
|
|
||||||
private fun addStatement(statement: IrStatement) = currentBlock.addStatement(statement)
|
private fun addStatement(statement: IrStatement) = currentBlock.addStatement(statement)
|
||||||
|
|
||||||
|
private fun isBlockEnded(): Boolean {
|
||||||
|
val lastExpression = currentBlock.statements.lastOrNull() as? IrExpression ?: return false
|
||||||
|
return lastExpression.type.isNothing()
|
||||||
|
}
|
||||||
|
|
||||||
private fun maybeDoDispatch(target: SuspendState) {
|
private fun maybeDoDispatch(target: SuspendState) {
|
||||||
val lastStatement = currentBlock.statements.lastOrNull()
|
if (!isBlockEnded()) {
|
||||||
if (lastStatement !is IrReturn && lastStatement !is IrContinue && lastStatement !is IrThrow) {
|
|
||||||
doDispatch(target)
|
doDispatch(target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -360,9 +357,6 @@ class StateMachineBuilder(
|
|||||||
branches = expression.branches
|
branches = expression.branches
|
||||||
}
|
}
|
||||||
|
|
||||||
val rootState = currentState
|
|
||||||
val rootBlock = currentBlock
|
|
||||||
|
|
||||||
for (branch in branches) {
|
for (branch in branches) {
|
||||||
if (!isElseBranch(branch)) {
|
if (!isElseBranch(branch)) {
|
||||||
branch.condition.acceptVoid(this)
|
branch.condition.acceptVoid(this)
|
||||||
@@ -378,35 +372,24 @@ class StateMachineBuilder(
|
|||||||
currentBlock = branchBlock
|
currentBlock = branchBlock
|
||||||
branch.result.acceptVoid(this)
|
branch.result.acceptVoid(this)
|
||||||
|
|
||||||
// TODO: block should not be empty
|
if (!isBlockEnded()) {
|
||||||
val lastStatement = currentBlock.statements.lastOrNull()
|
doDispatch(exitState)
|
||||||
if (lastStatement != null && lastStatement !is IrContinue) {
|
|
||||||
if (currentState !== rootState) {
|
|
||||||
doDispatch(exitState)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
currentState = dispatchState
|
currentState = dispatchState
|
||||||
currentBlock = elseBlock
|
currentBlock = elseBlock
|
||||||
} else {
|
} else {
|
||||||
branch.result.acceptVoid(this)
|
branch.result.acceptVoid(this)
|
||||||
|
if (!isBlockEnded()) {
|
||||||
// TODO: block should not be empty
|
doDispatch(exitState)
|
||||||
val lastStatement = currentBlock.statements.lastOrNull()
|
|
||||||
if (lastStatement != null && lastStatement !is IrContinue) {
|
|
||||||
if (currentState !== rootState) {
|
|
||||||
doDispatch(exitState)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
currentState = rootState
|
|
||||||
currentBlock = rootBlock
|
|
||||||
maybeDoDispatch(exitState)
|
maybeDoDispatch(exitState)
|
||||||
|
|
||||||
updateState(exitState)
|
updateState(exitState)
|
||||||
|
|
||||||
if (varSymbol != null) {
|
if (varSymbol != null) {
|
||||||
addStatement(JsIrBuilder.buildGetValue(varSymbol))
|
addStatement(JsIrBuilder.buildGetValue(varSymbol))
|
||||||
}
|
}
|
||||||
@@ -542,7 +525,7 @@ class StateMachineBuilder(
|
|||||||
if (varSymbol != null) {
|
if (varSymbol != null) {
|
||||||
transformLastExpression { JsIrBuilder.buildSetVariable(varSymbol, it, it.type) }
|
transformLastExpression { JsIrBuilder.buildSetVariable(varSymbol, it, it.type) }
|
||||||
}
|
}
|
||||||
doDispatch(exitState)
|
maybeDoDispatch(exitState)
|
||||||
} else {
|
} else {
|
||||||
transformLastExpression { expression.apply { value = it } }
|
transformLastExpression { expression.apply { value = it } }
|
||||||
}
|
}
|
||||||
@@ -553,7 +536,7 @@ class StateMachineBuilder(
|
|||||||
currentState.successors += catchBlockStack.peek()!!
|
currentState.successors += catchBlockStack.peek()!!
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hasResultingValue(expression: IrExpression) = !expression.type.isNothing()
|
private fun hasResultingValue(expression: IrExpression) = !expression.type.run { isNothing() || isUnit() }
|
||||||
|
|
||||||
override fun visitThrow(expression: IrThrow) {
|
override fun visitThrow(expression: IrThrow) {
|
||||||
expression.acceptChildrenVoid(this)
|
expression.acceptChildrenVoid(this)
|
||||||
@@ -562,20 +545,18 @@ class StateMachineBuilder(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitTry(aTry: IrTry) {
|
override fun visitTry(aTry: IrTry) {
|
||||||
val tryState = buildTryState(aTry)
|
|
||||||
|
require(aTry.finallyExpression == null)
|
||||||
|
|
||||||
|
val tryState = buildTryState()
|
||||||
val enclosingCatch = catchBlockStack.peek()!!
|
val enclosingCatch = catchBlockStack.peek()!!
|
||||||
|
|
||||||
catchBlockStack.push(tryState.catchState)
|
catchBlockStack.push(tryState.catchState)
|
||||||
|
|
||||||
val finallyStateVar = tempVar(int, "FINALLY_STATE")
|
|
||||||
val exitState = SuspendState(unit)
|
val exitState = SuspendState(unit)
|
||||||
|
|
||||||
val varSymbol = if (hasResultingValue(aTry)) tempVar(aTry.type, "TRY_RESULT") else null
|
val varSymbol = if (hasResultingValue(aTry)) tempVar(aTry.type, "TRY_RESULT") else null
|
||||||
|
|
||||||
if (aTry.finallyExpression != null) {
|
|
||||||
finallyStateVar.initializer = IrDispatchPoint(exitState)
|
|
||||||
addStatement(finallyStateVar)
|
|
||||||
}
|
|
||||||
if (varSymbol != null) {
|
if (varSymbol != null) {
|
||||||
addStatement(varSymbol)
|
addStatement(varSymbol)
|
||||||
}
|
}
|
||||||
@@ -591,23 +572,16 @@ class StateMachineBuilder(
|
|||||||
|
|
||||||
tryResult.acceptVoid(this)
|
tryResult.acceptVoid(this)
|
||||||
|
|
||||||
if (tryState.finallyState != null) {
|
if (!isBlockEnded()) {
|
||||||
doDispatch(tryState.finallyState.normal)
|
|
||||||
} else {
|
|
||||||
setupExceptionState(enclosingCatch)
|
setupExceptionState(enclosingCatch)
|
||||||
doDispatch(exitState)
|
doDispatch(exitState)
|
||||||
}
|
}
|
||||||
|
|
||||||
addExceptionEdge()
|
addExceptionEdge()
|
||||||
|
|
||||||
catchBlockStack.pop()
|
catchBlockStack.pop()
|
||||||
updateState(tryState.catchState)
|
updateState(tryState.catchState)
|
||||||
|
|
||||||
if (tryState.finallyState != null) {
|
setupExceptionState(enclosingCatch)
|
||||||
setupExceptionState(tryState.finallyState.fromThrow)
|
|
||||||
} else {
|
|
||||||
setupExceptionState(enclosingCatch)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var rethrowNeeded = true
|
var rethrowNeeded = true
|
||||||
|
|
||||||
@@ -628,9 +602,7 @@ class StateMachineBuilder(
|
|||||||
|
|
||||||
addStatement(irVar)
|
addStatement(irVar)
|
||||||
catchResult.acceptVoid(this)
|
catchResult.acceptVoid(this)
|
||||||
val exitDispatch = tryState.finallyState?.run { normal } ?: exitState
|
maybeDoDispatch(exitState)
|
||||||
maybeDoDispatch(exitDispatch)
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
val check = buildIsCheck(pendingException(), type)
|
val check = buildIsCheck(pendingException(), type)
|
||||||
|
|
||||||
@@ -644,8 +616,7 @@ class StateMachineBuilder(
|
|||||||
|
|
||||||
addStatement(irVar)
|
addStatement(irVar)
|
||||||
catchResult.acceptVoid(this)
|
catchResult.acceptVoid(this)
|
||||||
val exitDispatch = tryState.finallyState?.run { normal } ?: exitState
|
maybeDoDispatch(exitState)
|
||||||
maybeDoDispatch(exitDispatch)
|
|
||||||
|
|
||||||
currentBlock = ifBlock
|
currentBlock = ifBlock
|
||||||
addStatement(irIf)
|
addStatement(irIf)
|
||||||
@@ -658,42 +629,10 @@ class StateMachineBuilder(
|
|||||||
addStatement(JsIrBuilder.buildThrow(nothing, pendingException()))
|
addStatement(JsIrBuilder.buildThrow(nothing, pendingException()))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tryState.finallyState == null) {
|
currentState.successors += enclosingCatch
|
||||||
currentState.successors += enclosingCatch
|
|
||||||
}
|
|
||||||
|
|
||||||
val finallyState = tryState.finallyState
|
|
||||||
if (finallyState != null) {
|
|
||||||
val throwExitState = SuspendState(unit)
|
|
||||||
updateState(finallyState.fromThrow)
|
|
||||||
tryState.tryState.successors += finallyState.fromThrow
|
|
||||||
addStatement(
|
|
||||||
JsIrBuilder.buildSetVariable(
|
|
||||||
finallyStateVar.symbol,
|
|
||||||
IrDispatchPoint(throwExitState), int
|
|
||||||
)
|
|
||||||
)
|
|
||||||
doDispatch(finallyState.normal)
|
|
||||||
|
|
||||||
updateState(finallyState.normal)
|
|
||||||
tryState.tryState.successors += finallyState.normal
|
|
||||||
setupExceptionState(enclosingCatch)
|
|
||||||
aTry.finallyExpression?.acceptVoid(this)
|
|
||||||
currentState.successors += listOf(throwExitState, exitState)
|
|
||||||
addStatement(
|
|
||||||
JsIrBuilder.buildCall(stateSymbolSetter.symbol, unit).also {
|
|
||||||
it.dispatchReceiver = thisReceiver
|
|
||||||
it.putValueArgument(0, JsIrBuilder.buildGetValue(finallyStateVar.symbol))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
doContinue()
|
|
||||||
|
|
||||||
updateState(throwExitState)
|
|
||||||
addStatement(JsIrBuilder.buildThrow(nothing, pendingException()))
|
|
||||||
addExceptionEdge()
|
|
||||||
}
|
|
||||||
|
|
||||||
updateState(exitState)
|
updateState(exitState)
|
||||||
|
|
||||||
if (varSymbol != null) {
|
if (varSymbol != null) {
|
||||||
addStatement(JsIrBuilder.buildGetValue(varSymbol.symbol))
|
addStatement(JsIrBuilder.buildGetValue(varSymbol.symbol))
|
||||||
}
|
}
|
||||||
@@ -711,19 +650,7 @@ class StateMachineBuilder(
|
|||||||
private fun exceptionState() = JsIrBuilder.buildCall(exStateSymbolGetter.symbol).also { it.dispatchReceiver = thisReceiver }
|
private fun exceptionState() = JsIrBuilder.buildCall(exStateSymbolGetter.symbol).also { it.dispatchReceiver = thisReceiver }
|
||||||
private fun pendingException() = JsIrBuilder.buildCall(exceptionSymbolGetter.symbol).also { it.dispatchReceiver = thisReceiver }
|
private fun pendingException() = JsIrBuilder.buildCall(exceptionSymbolGetter.symbol).also { it.dispatchReceiver = thisReceiver }
|
||||||
|
|
||||||
private fun buildTryState(aTry: IrTry) =
|
private fun buildTryState() = TryState(currentState, SuspendState(unit))
|
||||||
TryState(
|
|
||||||
currentState,
|
|
||||||
SuspendState(unit),
|
|
||||||
aTry.finallyExpression?.run {
|
|
||||||
FinallyTargets(
|
|
||||||
SuspendState(
|
|
||||||
unit
|
|
||||||
), SuspendState(unit)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
private fun buildIsCheck(value: IrExpression, toType: IrType) =
|
private fun buildIsCheck(value: IrExpression, toType: IrType) =
|
||||||
JsIrBuilder.buildTypeOperator(
|
JsIrBuilder.buildTypeOperator(
|
||||||
|
|||||||
+25
-89
@@ -5,18 +5,14 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.ir.backend.js.lower.coroutines
|
package org.jetbrains.kotlin.ir.backend.js.lower.coroutines
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
|
||||||
import org.jetbrains.kotlin.backend.common.ir.isSuspend
|
import org.jetbrains.kotlin.backend.common.ir.isSuspend
|
||||||
import org.jetbrains.kotlin.backend.common.lower.FinallyBlocksLowering
|
|
||||||
import org.jetbrains.kotlin.backend.common.pop
|
import org.jetbrains.kotlin.backend.common.pop
|
||||||
import org.jetbrains.kotlin.backend.common.push
|
import org.jetbrains.kotlin.backend.common.push
|
||||||
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.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.IrBlockBodyImpl
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
|
||||||
@@ -28,16 +24,23 @@ import org.jetbrains.kotlin.ir.visitors.*
|
|||||||
object COROUTINE_ROOT_LOOP : IrStatementOriginImpl("COROUTINE_ROOT_LOOP")
|
object COROUTINE_ROOT_LOOP : IrStatementOriginImpl("COROUTINE_ROOT_LOOP")
|
||||||
object COROUTINE_SWITCH : IrStatementOriginImpl("COROUTINE_SWITCH")
|
object COROUTINE_SWITCH : IrStatementOriginImpl("COROUTINE_SWITCH")
|
||||||
|
|
||||||
open class SuspendableNodesCollector(protected val suspendableNodes: MutableSet<IrElement>) : IrElementVisitorVoid {
|
open class SuspendableNodesCollector(private val suspendableNodes: MutableSet<IrElement>) : IrElementVisitorVoid {
|
||||||
|
|
||||||
protected var hasSuspendableChildren = false
|
private var hasSuspendableChildren = false
|
||||||
|
|
||||||
|
protected fun markNode(node: IrElement) {
|
||||||
|
suspendableNodes += node
|
||||||
|
hasSuspendableChildren = true
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun isSuspendableNode(node: IrElement) = node in suspendableNodes
|
||||||
|
|
||||||
override fun visitElement(element: IrElement) {
|
override fun visitElement(element: IrElement) {
|
||||||
val current = hasSuspendableChildren
|
val current = hasSuspendableChildren
|
||||||
hasSuspendableChildren = false
|
hasSuspendableChildren = false
|
||||||
element.acceptChildrenVoid(this)
|
element.acceptChildrenVoid(this)
|
||||||
if (hasSuspendableChildren) {
|
if (hasSuspendableChildren) {
|
||||||
suspendableNodes += element
|
markNode(element)
|
||||||
}
|
}
|
||||||
hasSuspendableChildren = hasSuspendableChildren || current
|
hasSuspendableChildren = hasSuspendableChildren || current
|
||||||
}
|
}
|
||||||
@@ -45,105 +48,38 @@ open class SuspendableNodesCollector(protected val suspendableNodes: MutableSet<
|
|||||||
override fun visitCall(expression: IrCall) {
|
override fun visitCall(expression: IrCall) {
|
||||||
super.visitCall(expression)
|
super.visitCall(expression)
|
||||||
if (expression.isSuspend) {
|
if (expression.isSuspend) {
|
||||||
suspendableNodes += expression
|
markNode(expression)
|
||||||
hasSuspendableChildren = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun collectSuspendableNodes(
|
|
||||||
body: IrBlock,
|
|
||||||
suspendableNodes: MutableSet<IrElement>,
|
|
||||||
context: CommonBackendContext,
|
|
||||||
function: IrFunction,
|
|
||||||
throwableType: IrType
|
|
||||||
): IrBlock {
|
|
||||||
|
|
||||||
// 1st: mark suspendable loops and tries
|
|
||||||
body.acceptVoid(SuspendableNodesCollector(suspendableNodes))
|
|
||||||
// 2nd: mark inner terminators
|
|
||||||
val terminatorsCollector = SuspendedTerminatorsCollector(suspendableNodes)
|
|
||||||
body.acceptVoid(terminatorsCollector)
|
|
||||||
|
|
||||||
if (terminatorsCollector.shouldFinalliesBeLowered) {
|
|
||||||
val finallyLower = FinallyBlocksLowering(context, throwableType)
|
|
||||||
|
|
||||||
function.body = IrBlockBodyImpl(body.startOffset, body.endOffset, body.statements)
|
|
||||||
function.transform(finallyLower, null)
|
|
||||||
|
|
||||||
val newBody = function.body as IrBlockBody
|
|
||||||
function.body = null
|
|
||||||
suspendableNodes.clear()
|
|
||||||
val newBlock = JsIrBuilder.buildBlock(body.type, newBody.statements)
|
|
||||||
|
|
||||||
return collectSuspendableNodes(newBlock, suspendableNodes, context, function, throwableType)
|
|
||||||
}
|
|
||||||
|
|
||||||
return body
|
|
||||||
}
|
|
||||||
|
|
||||||
class SuspendedTerminatorsCollector(suspendableNodes: MutableSet<IrElement>) : SuspendableNodesCollector(suspendableNodes) {
|
class SuspendedTerminatorsCollector(suspendableNodes: MutableSet<IrElement>) : SuspendableNodesCollector(suspendableNodes) {
|
||||||
|
|
||||||
var shouldFinalliesBeLowered = false
|
|
||||||
|
|
||||||
override fun visitBreakContinue(jump: IrBreakContinue) {
|
override fun visitBreakContinue(jump: IrBreakContinue) {
|
||||||
if (jump.loop in suspendableNodes) {
|
if (isSuspendableNode(jump.loop)) {
|
||||||
suspendableNodes.add(jump)
|
markNode(jump)
|
||||||
hasSuspendableChildren = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldFinalliesBeLowered = shouldFinalliesBeLowered || tryStack.any { it.finallyExpression != null && it in suspendableNodes }
|
|
||||||
}
|
|
||||||
|
|
||||||
private val tryStack = mutableListOf<IrTry>()
|
|
||||||
private val tryLoopStack = mutableListOf<IrStatement>()
|
|
||||||
|
|
||||||
private fun pushTry(aTry: IrTry) {
|
|
||||||
tryStack.push(aTry)
|
|
||||||
tryLoopStack.push(aTry)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun popTry() {
|
|
||||||
tryLoopStack.pop()
|
|
||||||
tryStack.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun pushLoop(loop: IrLoop) {
|
|
||||||
tryLoopStack.push(loop)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun popLoop() {
|
|
||||||
tryLoopStack.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitLoop(loop: IrLoop) {
|
|
||||||
pushLoop(loop)
|
|
||||||
|
|
||||||
super.visitLoop(loop)
|
|
||||||
|
|
||||||
popLoop()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitTry(aTry: IrTry) {
|
|
||||||
pushTry(aTry)
|
|
||||||
|
|
||||||
super.visitTry(aTry)
|
|
||||||
|
|
||||||
popTry()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitReturn(expression: IrReturn) {
|
override fun visitReturn(expression: IrReturn) {
|
||||||
shouldFinalliesBeLowered = shouldFinalliesBeLowered || tryStack.any { it.finallyExpression != null && it in suspendableNodes }
|
|
||||||
|
|
||||||
super.visitReturn(expression)
|
super.visitReturn(expression)
|
||||||
|
|
||||||
if (expression.returnTargetSymbol is IrReturnableBlockSymbol) {
|
if (expression.returnTargetSymbol is IrReturnableBlockSymbol && isSuspendableNode(expression.returnTargetSymbol.owner)) {
|
||||||
suspendableNodes.add(expression)
|
markNode(expression)
|
||||||
hasSuspendableChildren = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun collectSuspendableNodes(function: IrBlock): MutableSet<IrElement> {
|
||||||
|
|
||||||
|
val suspendableNodes = mutableSetOf<IrElement>()
|
||||||
|
// 1st: mark suspendable loops and tries
|
||||||
|
function.acceptVoid(SuspendableNodesCollector(suspendableNodes))
|
||||||
|
// 2nd: mark inner terminators
|
||||||
|
function.acceptVoid(SuspendedTerminatorsCollector(suspendableNodes))
|
||||||
|
|
||||||
|
return suspendableNodes
|
||||||
|
}
|
||||||
|
|
||||||
class LiveLocalsTransformer(
|
class LiveLocalsTransformer(
|
||||||
private val localMap: Map<IrValueSymbol, IrFieldSymbol>,
|
private val localMap: Map<IrValueSymbol, IrFieldSymbol>,
|
||||||
|
|||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
// KJS_WITH_FULL_RUNTIME
|
||||||
|
// IGNORE_BACKEND: JVM_IR
|
||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
|
// COMMON_COROUTINES_TEST
|
||||||
|
import helpers.*
|
||||||
|
import COROUTINES_PACKAGE.*
|
||||||
|
import COROUTINES_PACKAGE.intrinsics.*
|
||||||
|
|
||||||
|
|
||||||
|
class Controller {
|
||||||
|
var result = ""
|
||||||
|
|
||||||
|
suspend fun <T> suspendWithResult(value: T): T = suspendCoroutineUninterceptedOrReturn { c ->
|
||||||
|
c.resume(value)
|
||||||
|
COROUTINE_SUSPENDED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun lock(owner: Controller) {
|
||||||
|
owner.result += "L"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unlock(owner: Controller) {
|
||||||
|
owner.result += "U"
|
||||||
|
}
|
||||||
|
|
||||||
|
public suspend inline fun doInline(owner: Controller, action: () -> Unit): Unit {
|
||||||
|
lock(owner)
|
||||||
|
try {
|
||||||
|
return action()
|
||||||
|
} finally {
|
||||||
|
unlock(owner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun builder(c: suspend Controller.() -> Unit): String {
|
||||||
|
val controller = Controller()
|
||||||
|
c.startCoroutine(controller, EmptyContinuation)
|
||||||
|
return controller.result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val value = builder {
|
||||||
|
doInline(this) {
|
||||||
|
result += "X"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (value != "LXU") return "fail: $value"
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
Vendored
+73
@@ -0,0 +1,73 @@
|
|||||||
|
// KJS_WITH_FULL_RUNTIME
|
||||||
|
// IGNORE_BACKEND: JVM_IR
|
||||||
|
// WITH_RUNTIME
|
||||||
|
// WITH_COROUTINES
|
||||||
|
// COMMON_COROUTINES_TEST
|
||||||
|
import helpers.*
|
||||||
|
import COROUTINES_PACKAGE.*
|
||||||
|
import COROUTINES_PACKAGE.intrinsics.*
|
||||||
|
|
||||||
|
|
||||||
|
class Controller {
|
||||||
|
var result = ""
|
||||||
|
|
||||||
|
suspend fun <T> suspendWithResult(value: T): T = suspendCoroutineUninterceptedOrReturn { c ->
|
||||||
|
c.resume(value)
|
||||||
|
COROUTINE_SUSPENDED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Controller.consumeCancel(c: Throwable?) {
|
||||||
|
result += if (c == null) "?" else "!"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun newIterator() = Iterator()
|
||||||
|
|
||||||
|
class Iterator() {
|
||||||
|
var hasNextX = true
|
||||||
|
public suspend fun hasNext(): Boolean {
|
||||||
|
val tmp = hasNextX
|
||||||
|
hasNextX = false
|
||||||
|
return tmp
|
||||||
|
}
|
||||||
|
public suspend fun next(): String = "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline fun Controller.consume(action: Controller.() -> String?): String? {
|
||||||
|
var cause: Throwable? = null
|
||||||
|
try {
|
||||||
|
return action()
|
||||||
|
} catch(x: Exception) {
|
||||||
|
cause = x
|
||||||
|
throw x
|
||||||
|
} finally {
|
||||||
|
consumeCancel(cause)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public suspend fun Controller.doTest(): String? {
|
||||||
|
return consume {
|
||||||
|
val iterator = newIterator()
|
||||||
|
if (!iterator.hasNext())
|
||||||
|
return null
|
||||||
|
return iterator.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun builder(c: suspend Controller.() -> Unit): String {
|
||||||
|
val controller = Controller()
|
||||||
|
c.startCoroutine(controller, EmptyContinuation)
|
||||||
|
return controller.result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Controller.add(s: String?) {
|
||||||
|
result += s
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val value = builder {
|
||||||
|
add(doTest())
|
||||||
|
}
|
||||||
|
return if (value != "?OK") return "Fail: $value" else "OK"
|
||||||
|
}
|
||||||
+20
@@ -6825,6 +6825,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt", "kotlin.coroutines");
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt", "kotlin.coroutines");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineSuspendFinally.kt")
|
||||||
|
public void testInlineSuspendFinally_1_2() throws Exception {
|
||||||
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/inlineSuspendFinally.kt", "kotlin.coroutines.experimental");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineSuspendFinally.kt")
|
||||||
|
public void testInlineSuspendFinally_1_3() throws Exception {
|
||||||
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/inlineSuspendFinally.kt", "kotlin.coroutines");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("safeCallOnTwoReceiversLong.kt")
|
@TestMetadata("safeCallOnTwoReceiversLong.kt")
|
||||||
public void testSafeCallOnTwoReceiversLong_1_2() throws Exception {
|
public void testSafeCallOnTwoReceiversLong_1_2() throws Exception {
|
||||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/safeCallOnTwoReceiversLong.kt", "kotlin.coroutines.experimental");
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/safeCallOnTwoReceiversLong.kt", "kotlin.coroutines.experimental");
|
||||||
@@ -6865,6 +6875,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
runTest("compiler/testData/codegen/box/coroutines/featureIntersection/suspendFunctionIsAs.kt");
|
runTest("compiler/testData/codegen/box/coroutines/featureIntersection/suspendFunctionIsAs.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("suspendInlineSuspendFinally.kt")
|
||||||
|
public void testSuspendInlineSuspendFinally_1_2() throws Exception {
|
||||||
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/suspendInlineSuspendFinally.kt", "kotlin.coroutines.experimental");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("suspendInlineSuspendFinally.kt")
|
||||||
|
public void testSuspendInlineSuspendFinally_1_3() throws Exception {
|
||||||
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/suspendInlineSuspendFinally.kt", "kotlin.coroutines");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("suspendOperatorPlusAssign.kt")
|
@TestMetadata("suspendOperatorPlusAssign.kt")
|
||||||
public void testSuspendOperatorPlusAssign_1_2() throws Exception {
|
public void testSuspendOperatorPlusAssign_1_2() throws Exception {
|
||||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/suspendOperatorPlusAssign.kt", "kotlin.coroutines.experimental");
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/suspendOperatorPlusAssign.kt", "kotlin.coroutines.experimental");
|
||||||
|
|||||||
+20
@@ -6825,6 +6825,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt", "kotlin.coroutines");
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt", "kotlin.coroutines");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineSuspendFinally.kt")
|
||||||
|
public void testInlineSuspendFinally_1_2() throws Exception {
|
||||||
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/inlineSuspendFinally.kt", "kotlin.coroutines.experimental");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineSuspendFinally.kt")
|
||||||
|
public void testInlineSuspendFinally_1_3() throws Exception {
|
||||||
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/inlineSuspendFinally.kt", "kotlin.coroutines");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("safeCallOnTwoReceiversLong.kt")
|
@TestMetadata("safeCallOnTwoReceiversLong.kt")
|
||||||
public void testSafeCallOnTwoReceiversLong_1_2() throws Exception {
|
public void testSafeCallOnTwoReceiversLong_1_2() throws Exception {
|
||||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/safeCallOnTwoReceiversLong.kt", "kotlin.coroutines.experimental");
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/safeCallOnTwoReceiversLong.kt", "kotlin.coroutines.experimental");
|
||||||
@@ -6865,6 +6875,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
runTest("compiler/testData/codegen/box/coroutines/featureIntersection/suspendFunctionIsAs.kt");
|
runTest("compiler/testData/codegen/box/coroutines/featureIntersection/suspendFunctionIsAs.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("suspendInlineSuspendFinally.kt")
|
||||||
|
public void testSuspendInlineSuspendFinally_1_2() throws Exception {
|
||||||
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/suspendInlineSuspendFinally.kt", "kotlin.coroutines.experimental");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("suspendInlineSuspendFinally.kt")
|
||||||
|
public void testSuspendInlineSuspendFinally_1_3() throws Exception {
|
||||||
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/suspendInlineSuspendFinally.kt", "kotlin.coroutines");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("suspendOperatorPlusAssign.kt")
|
@TestMetadata("suspendOperatorPlusAssign.kt")
|
||||||
public void testSuspendOperatorPlusAssign_1_2() throws Exception {
|
public void testSuspendOperatorPlusAssign_1_2() throws Exception {
|
||||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/suspendOperatorPlusAssign.kt", "kotlin.coroutines.experimental");
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/suspendOperatorPlusAssign.kt", "kotlin.coroutines.experimental");
|
||||||
|
|||||||
+20
@@ -6825,6 +6825,16 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt", "kotlin.coroutines");
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt", "kotlin.coroutines");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineSuspendFinally.kt")
|
||||||
|
public void testInlineSuspendFinally_1_2() throws Exception {
|
||||||
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/inlineSuspendFinally.kt", "kotlin.coroutines.experimental");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineSuspendFinally.kt")
|
||||||
|
public void testInlineSuspendFinally_1_3() throws Exception {
|
||||||
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/inlineSuspendFinally.kt", "kotlin.coroutines");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("safeCallOnTwoReceiversLong.kt")
|
@TestMetadata("safeCallOnTwoReceiversLong.kt")
|
||||||
public void testSafeCallOnTwoReceiversLong_1_2() throws Exception {
|
public void testSafeCallOnTwoReceiversLong_1_2() throws Exception {
|
||||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/safeCallOnTwoReceiversLong.kt", "kotlin.coroutines.experimental");
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/safeCallOnTwoReceiversLong.kt", "kotlin.coroutines.experimental");
|
||||||
@@ -6865,6 +6875,16 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTest("compiler/testData/codegen/box/coroutines/featureIntersection/suspendFunctionIsAs.kt");
|
runTest("compiler/testData/codegen/box/coroutines/featureIntersection/suspendFunctionIsAs.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("suspendInlineSuspendFinally.kt")
|
||||||
|
public void testSuspendInlineSuspendFinally_1_2() throws Exception {
|
||||||
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/suspendInlineSuspendFinally.kt", "kotlin.coroutines.experimental");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("suspendInlineSuspendFinally.kt")
|
||||||
|
public void testSuspendInlineSuspendFinally_1_3() throws Exception {
|
||||||
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/suspendInlineSuspendFinally.kt", "kotlin.coroutines");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("suspendOperatorPlusAssign.kt")
|
@TestMetadata("suspendOperatorPlusAssign.kt")
|
||||||
public void testSuspendOperatorPlusAssign_1_2() throws Exception {
|
public void testSuspendOperatorPlusAssign_1_2() throws Exception {
|
||||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/suspendOperatorPlusAssign.kt", "kotlin.coroutines.experimental");
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/suspendOperatorPlusAssign.kt", "kotlin.coroutines.experimental");
|
||||||
|
|||||||
Generated
+10
@@ -5360,6 +5360,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
|||||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt", "kotlin.coroutines");
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt", "kotlin.coroutines");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineSuspendFinally.kt")
|
||||||
|
public void testInlineSuspendFinally_1_3() throws Exception {
|
||||||
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/inlineSuspendFinally.kt", "kotlin.coroutines");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("safeCallOnTwoReceiversLong.kt")
|
@TestMetadata("safeCallOnTwoReceiversLong.kt")
|
||||||
public void testSafeCallOnTwoReceiversLong_1_3() throws Exception {
|
public void testSafeCallOnTwoReceiversLong_1_3() throws Exception {
|
||||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/safeCallOnTwoReceiversLong.kt", "kotlin.coroutines");
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/safeCallOnTwoReceiversLong.kt", "kotlin.coroutines");
|
||||||
@@ -5385,6 +5390,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
|||||||
runTest("compiler/testData/codegen/box/coroutines/featureIntersection/suspendFunctionIsAs.kt");
|
runTest("compiler/testData/codegen/box/coroutines/featureIntersection/suspendFunctionIsAs.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("suspendInlineSuspendFinally.kt")
|
||||||
|
public void testSuspendInlineSuspendFinally_1_3() throws Exception {
|
||||||
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/suspendInlineSuspendFinally.kt", "kotlin.coroutines");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("suspendOperatorPlusAssign.kt")
|
@TestMetadata("suspendOperatorPlusAssign.kt")
|
||||||
public void testSuspendOperatorPlusAssign_1_3() throws Exception {
|
public void testSuspendOperatorPlusAssign_1_3() throws Exception {
|
||||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/suspendOperatorPlusAssign.kt", "kotlin.coroutines");
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/suspendOperatorPlusAssign.kt", "kotlin.coroutines");
|
||||||
|
|||||||
+20
@@ -5915,6 +5915,16 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt", "kotlin.coroutines");
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt", "kotlin.coroutines");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineSuspendFinally.kt")
|
||||||
|
public void testInlineSuspendFinally_1_2() throws Exception {
|
||||||
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/inlineSuspendFinally.kt", "kotlin.coroutines.experimental");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineSuspendFinally.kt")
|
||||||
|
public void testInlineSuspendFinally_1_3() throws Exception {
|
||||||
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/inlineSuspendFinally.kt", "kotlin.coroutines");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("safeCallOnTwoReceiversLong.kt")
|
@TestMetadata("safeCallOnTwoReceiversLong.kt")
|
||||||
public void testSafeCallOnTwoReceiversLong_1_2() throws Exception {
|
public void testSafeCallOnTwoReceiversLong_1_2() throws Exception {
|
||||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/safeCallOnTwoReceiversLong.kt", "kotlin.coroutines.experimental");
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/safeCallOnTwoReceiversLong.kt", "kotlin.coroutines.experimental");
|
||||||
@@ -5955,6 +5965,16 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
runTest("compiler/testData/codegen/box/coroutines/featureIntersection/suspendFunctionIsAs.kt");
|
runTest("compiler/testData/codegen/box/coroutines/featureIntersection/suspendFunctionIsAs.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("suspendInlineSuspendFinally.kt")
|
||||||
|
public void testSuspendInlineSuspendFinally_1_2() throws Exception {
|
||||||
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/suspendInlineSuspendFinally.kt", "kotlin.coroutines.experimental");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("suspendInlineSuspendFinally.kt")
|
||||||
|
public void testSuspendInlineSuspendFinally_1_3() throws Exception {
|
||||||
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/suspendInlineSuspendFinally.kt", "kotlin.coroutines");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("suspendOperatorPlusAssign.kt")
|
@TestMetadata("suspendOperatorPlusAssign.kt")
|
||||||
public void testSuspendOperatorPlusAssign_1_2() throws Exception {
|
public void testSuspendOperatorPlusAssign_1_2() throws Exception {
|
||||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/suspendOperatorPlusAssign.kt", "kotlin.coroutines.experimental");
|
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/suspendOperatorPlusAssign.kt", "kotlin.coroutines.experimental");
|
||||||
|
|||||||
Reference in New Issue
Block a user