[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 buildStateMachine(
|
||||
originalBody: IrBody, stateMachineFunction: IrFunction,
|
||||
stateMachineFunction: IrFunction,
|
||||
transformingFunction: IrFunction,
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
+31
-21
@@ -216,11 +216,6 @@ class FinallyBlocksLowering(val context: CommonBackendContext, private val throw
|
||||
val irBuilder = context.createIrBuilder(currentScope!!.scope.scopeOwnerSymbol, startOffset, endOffset)
|
||||
val transformer = this
|
||||
irBuilder.run {
|
||||
val transformedTry = IrTryImpl(
|
||||
startOffset = startOffset,
|
||||
endOffset = endOffset,
|
||||
type = context.irBuiltIns.unitType
|
||||
)
|
||||
val transformedFinallyExpression = finallyExpression.transform(transformer, null)
|
||||
val parameter = WrappedVariableDescriptor()
|
||||
val catchParameter = IrVariableImpl(
|
||||
@@ -233,27 +228,42 @@ class FinallyBlocksLowering(val context: CommonBackendContext, private val throw
|
||||
val syntheticTry = IrTryImpl(
|
||||
startOffset = startOffset,
|
||||
endOffset = endOffset,
|
||||
type = context.irBuiltIns.unitType,
|
||||
tryResult = transformedTry,
|
||||
catches = listOf(
|
||||
irCatch(catchParameter).apply {
|
||||
result = irComposite {
|
||||
+finallyExpression.copy()
|
||||
+irThrow(irGet(catchParameter))
|
||||
}
|
||||
}),
|
||||
finallyExpression = null
|
||||
)
|
||||
type = context.irBuiltIns.unitType
|
||||
).apply {
|
||||
this.catches += irCatch(catchParameter).apply {
|
||||
result = irComposite {
|
||||
+finallyExpression.copy()
|
||||
+irThrow(irGet(catchParameter))
|
||||
}
|
||||
}
|
||||
|
||||
this.finallyExpression = null
|
||||
}
|
||||
|
||||
using(TryScope(syntheticTry, transformedFinallyExpression, this)) {
|
||||
|
||||
val fallThroughType = aTry.type
|
||||
val fallThroughSymbol = IrReturnableBlockSymbolImpl(WrappedSimpleFunctionDescriptor())
|
||||
val transformedResult = aTry.tryResult.transform(transformer, null)
|
||||
transformedTry.tryResult = irReturn(fallThroughSymbol, transformedResult)
|
||||
for (aCatch in aTry.catches) {
|
||||
val transformedCatch = aCatch.transform(transformer, null)
|
||||
transformedCatch.result = irReturn(fallThroughSymbol, transformedCatch.result)
|
||||
transformedTry.catches.add(transformedCatch)
|
||||
val returnedResult = irReturn(fallThroughSymbol, transformedResult)
|
||||
|
||||
if (aTry.catches.isNotEmpty()) {
|
||||
val transformedTry = IrTryImpl(
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,8 +380,8 @@ val jsPhases = namedIrModulePhase(
|
||||
moveBodilessDeclarationsToSeparatePlacePhase then
|
||||
enumClassLoweringPhase then
|
||||
enumUsageLoweringPhase then
|
||||
returnableBlockLoweringPhase then
|
||||
suspendFunctionsLoweringPhase then
|
||||
returnableBlockLoweringPhase then
|
||||
privateMembersLoweringPhase then
|
||||
callableReferenceLoweringPhase 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.ir.isSuspend
|
||||
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.ir.JsIrBuilder
|
||||
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.impl.*
|
||||
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.util.explicitParameters
|
||||
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 buildStateMachine(
|
||||
originalBody: IrBody,
|
||||
stateMachineFunction: IrFunction,
|
||||
transformingFunction: IrFunction,
|
||||
argumentToPropertiesMap: Map<IrValueParameter, IrField>
|
||||
) {
|
||||
val body =
|
||||
(originalBody as IrBlockBody).run {
|
||||
IrBlockImpl(
|
||||
transformingFunction.startOffset,
|
||||
transformingFunction.endOffset,
|
||||
context.irBuiltIns.unitType,
|
||||
STATEMENT_ORIGIN_COROUTINE_IMPL,
|
||||
statements
|
||||
)
|
||||
}
|
||||
val simplifiedFunction = transformingFunction.transform(FinallyBlocksLowering(context, context.dynamicType), null) as IrFunction
|
||||
val originalBody = simplifiedFunction.body as IrBlockBody
|
||||
|
||||
val body = IrBlockImpl(
|
||||
simplifiedFunction.startOffset,
|
||||
simplifiedFunction.endOffset,
|
||||
context.irBuiltIns.unitType,
|
||||
STATEMENT_ORIGIN_COROUTINE_IMPL,
|
||||
originalBody.statements
|
||||
)
|
||||
|
||||
val coroutineClass = stateMachineFunction.parent as IrClass
|
||||
val suspendResult = JsIrBuilder.buildVar(
|
||||
@@ -83,11 +83,11 @@ class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunct
|
||||
COROUTINE_ROOT_LOOP,
|
||||
rootTry,
|
||||
JsIrBuilder.buildBoolean(context.irBuiltIns.booleanType, true)
|
||||
)
|
||||
).also {
|
||||
it.label = "\$sm"
|
||||
}
|
||||
|
||||
val suspendableNodes = mutableSetOf<IrElement>()
|
||||
val loweredBody =
|
||||
collectSuspendableNodes(body, suspendableNodes, context, stateMachineFunction, context.dynamicType)
|
||||
val suspendableNodes = collectSuspendableNodes(body)
|
||||
val thisReceiver = (stateMachineFunction.dispatchReceiverParameter as IrValueParameter).symbol
|
||||
|
||||
val stateMachineBuilder = StateMachineBuilder(
|
||||
@@ -104,36 +104,13 @@ class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunct
|
||||
suspendResult.symbol
|
||||
)
|
||||
|
||||
loweredBody.acceptVoid(stateMachineBuilder)
|
||||
body.acceptVoid(stateMachineBuilder)
|
||||
|
||||
stateMachineBuilder.finalizeStateMachine()
|
||||
|
||||
rootTry.catches += stateMachineBuilder.globalCatch
|
||||
|
||||
val visited = mutableSetOf<SuspendState>()
|
||||
|
||||
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)
|
||||
assignStateIds(stateMachineBuilder.entryState, thisReceiver, switch, rootLoop)
|
||||
|
||||
exceptionTrapId = stateMachineBuilder.rootExceptionTrap.id
|
||||
|
||||
@@ -149,7 +126,7 @@ class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunct
|
||||
override fun visitReturn(expression: IrReturn): IrExpression {
|
||||
expression.transformChildrenVoid(this)
|
||||
|
||||
return if (expression.returnTargetSymbol != transformingFunction.symbol)
|
||||
return if (expression.returnTargetSymbol != simplifiedFunction.symbol)
|
||||
expression
|
||||
else
|
||||
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) {
|
||||
argumentToPropertiesMap.getValue(it).symbol
|
||||
}
|
||||
@@ -178,6 +155,33 @@ class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunct
|
||||
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>> {
|
||||
// TODO: data flow analysis.
|
||||
// Just save all visible for now.
|
||||
@@ -201,6 +205,7 @@ class JsSuspendFunctionsLowering(ctx: JsIrBackendContext) : AbstractSuspendFunct
|
||||
for (it in coroutineConstructors) {
|
||||
(it.body as? IrBlockBody)?.run {
|
||||
val receiver = JsIrBuilder.buildGetValue(coroutineClassThis.symbol)
|
||||
assert(exceptionTrapId >= 0)
|
||||
val id = JsIrBuilder.buildInt(context.irBuiltIns.intType, exceptionTrapId)
|
||||
statements += JsIrBuilder.buildCall(coroutineImplExceptionStatePropertySetter.symbol).also { call ->
|
||||
call.dispatchReceiver = receiver
|
||||
|
||||
+28
-101
@@ -23,10 +23,7 @@ import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrDynamicType
|
||||
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.types.*
|
||||
import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols
|
||||
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 FinallyTargets(val normal: SuspendState, val fromThrow: SuspendState)
|
||||
|
||||
data class TryState(val tryState: SuspendState, val catchState: SuspendState, val finallyState: FinallyTargets?)
|
||||
data class TryState(val tryState: SuspendState, val catchState: SuspendState)
|
||||
|
||||
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)
|
||||
@@ -50,7 +45,6 @@ class IrDispatchPoint(val target: SuspendState) : IrExpressionBase(UNDEFINED_OFF
|
||||
override fun <D> transformChildren(transformer: IrElementTransformer<D>, data: D) {}
|
||||
}
|
||||
|
||||
|
||||
class DispatchPointTransformer(val action: (SuspendState) -> IrExpression) : IrElementTransformerVoid() {
|
||||
override fun visitExpression(expression: IrExpression): IrExpression {
|
||||
val dispatchPoint = expression as? IrDispatchPoint
|
||||
@@ -76,7 +70,6 @@ class StateMachineBuilder(
|
||||
private val loopMap = mutableMapOf<IrLoop, LoopBounds>()
|
||||
private val unit = context.irBuiltIns.unitType
|
||||
private val nothing = context.irBuiltIns.nothingType
|
||||
private val int = context.irBuiltIns.intType
|
||||
private val booleanNotSymbol = context.irBuiltIns.booleanNotSymbol
|
||||
private val eqeqeqSymbol = context.irBuiltIns.eqeqeqSymbol
|
||||
|
||||
@@ -166,9 +159,13 @@ class StateMachineBuilder(
|
||||
|
||||
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) {
|
||||
val lastStatement = currentBlock.statements.lastOrNull()
|
||||
if (lastStatement !is IrReturn && lastStatement !is IrContinue && lastStatement !is IrThrow) {
|
||||
if (!isBlockEnded()) {
|
||||
doDispatch(target)
|
||||
}
|
||||
}
|
||||
@@ -360,9 +357,6 @@ class StateMachineBuilder(
|
||||
branches = expression.branches
|
||||
}
|
||||
|
||||
val rootState = currentState
|
||||
val rootBlock = currentBlock
|
||||
|
||||
for (branch in branches) {
|
||||
if (!isElseBranch(branch)) {
|
||||
branch.condition.acceptVoid(this)
|
||||
@@ -378,35 +372,24 @@ class StateMachineBuilder(
|
||||
currentBlock = branchBlock
|
||||
branch.result.acceptVoid(this)
|
||||
|
||||
// TODO: block should not be empty
|
||||
val lastStatement = currentBlock.statements.lastOrNull()
|
||||
if (lastStatement != null && lastStatement !is IrContinue) {
|
||||
if (currentState !== rootState) {
|
||||
doDispatch(exitState)
|
||||
}
|
||||
if (!isBlockEnded()) {
|
||||
doDispatch(exitState)
|
||||
}
|
||||
|
||||
currentState = dispatchState
|
||||
currentBlock = elseBlock
|
||||
} else {
|
||||
branch.result.acceptVoid(this)
|
||||
|
||||
// TODO: block should not be empty
|
||||
val lastStatement = currentBlock.statements.lastOrNull()
|
||||
if (lastStatement != null && lastStatement !is IrContinue) {
|
||||
if (currentState !== rootState) {
|
||||
doDispatch(exitState)
|
||||
}
|
||||
if (!isBlockEnded()) {
|
||||
doDispatch(exitState)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
currentState = rootState
|
||||
currentBlock = rootBlock
|
||||
maybeDoDispatch(exitState)
|
||||
|
||||
updateState(exitState)
|
||||
|
||||
if (varSymbol != null) {
|
||||
addStatement(JsIrBuilder.buildGetValue(varSymbol))
|
||||
}
|
||||
@@ -542,7 +525,7 @@ class StateMachineBuilder(
|
||||
if (varSymbol != null) {
|
||||
transformLastExpression { JsIrBuilder.buildSetVariable(varSymbol, it, it.type) }
|
||||
}
|
||||
doDispatch(exitState)
|
||||
maybeDoDispatch(exitState)
|
||||
} else {
|
||||
transformLastExpression { expression.apply { value = it } }
|
||||
}
|
||||
@@ -553,7 +536,7 @@ class StateMachineBuilder(
|
||||
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) {
|
||||
expression.acceptChildrenVoid(this)
|
||||
@@ -562,20 +545,18 @@ class StateMachineBuilder(
|
||||
}
|
||||
|
||||
override fun visitTry(aTry: IrTry) {
|
||||
val tryState = buildTryState(aTry)
|
||||
|
||||
require(aTry.finallyExpression == null)
|
||||
|
||||
val tryState = buildTryState()
|
||||
val enclosingCatch = catchBlockStack.peek()!!
|
||||
|
||||
catchBlockStack.push(tryState.catchState)
|
||||
|
||||
val finallyStateVar = tempVar(int, "FINALLY_STATE")
|
||||
val exitState = SuspendState(unit)
|
||||
|
||||
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) {
|
||||
addStatement(varSymbol)
|
||||
}
|
||||
@@ -591,23 +572,16 @@ class StateMachineBuilder(
|
||||
|
||||
tryResult.acceptVoid(this)
|
||||
|
||||
if (tryState.finallyState != null) {
|
||||
doDispatch(tryState.finallyState.normal)
|
||||
} else {
|
||||
if (!isBlockEnded()) {
|
||||
setupExceptionState(enclosingCatch)
|
||||
doDispatch(exitState)
|
||||
}
|
||||
|
||||
addExceptionEdge()
|
||||
|
||||
catchBlockStack.pop()
|
||||
updateState(tryState.catchState)
|
||||
|
||||
if (tryState.finallyState != null) {
|
||||
setupExceptionState(tryState.finallyState.fromThrow)
|
||||
} else {
|
||||
setupExceptionState(enclosingCatch)
|
||||
}
|
||||
|
||||
setupExceptionState(enclosingCatch)
|
||||
|
||||
var rethrowNeeded = true
|
||||
|
||||
@@ -628,9 +602,7 @@ class StateMachineBuilder(
|
||||
|
||||
addStatement(irVar)
|
||||
catchResult.acceptVoid(this)
|
||||
val exitDispatch = tryState.finallyState?.run { normal } ?: exitState
|
||||
maybeDoDispatch(exitDispatch)
|
||||
|
||||
maybeDoDispatch(exitState)
|
||||
} else {
|
||||
val check = buildIsCheck(pendingException(), type)
|
||||
|
||||
@@ -644,8 +616,7 @@ class StateMachineBuilder(
|
||||
|
||||
addStatement(irVar)
|
||||
catchResult.acceptVoid(this)
|
||||
val exitDispatch = tryState.finallyState?.run { normal } ?: exitState
|
||||
maybeDoDispatch(exitDispatch)
|
||||
maybeDoDispatch(exitState)
|
||||
|
||||
currentBlock = ifBlock
|
||||
addStatement(irIf)
|
||||
@@ -658,42 +629,10 @@ class StateMachineBuilder(
|
||||
addStatement(JsIrBuilder.buildThrow(nothing, pendingException()))
|
||||
}
|
||||
|
||||
if (tryState.finallyState == null) {
|
||||
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()
|
||||
}
|
||||
currentState.successors += enclosingCatch
|
||||
|
||||
updateState(exitState)
|
||||
|
||||
if (varSymbol != null) {
|
||||
addStatement(JsIrBuilder.buildGetValue(varSymbol.symbol))
|
||||
}
|
||||
@@ -711,19 +650,7 @@ class StateMachineBuilder(
|
||||
private fun exceptionState() = JsIrBuilder.buildCall(exStateSymbolGetter.symbol).also { it.dispatchReceiver = thisReceiver }
|
||||
private fun pendingException() = JsIrBuilder.buildCall(exceptionSymbolGetter.symbol).also { it.dispatchReceiver = thisReceiver }
|
||||
|
||||
private fun buildTryState(aTry: IrTry) =
|
||||
TryState(
|
||||
currentState,
|
||||
SuspendState(unit),
|
||||
aTry.finallyExpression?.run {
|
||||
FinallyTargets(
|
||||
SuspendState(
|
||||
unit
|
||||
), SuspendState(unit)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
private fun buildTryState() = TryState(currentState, SuspendState(unit))
|
||||
|
||||
private fun buildIsCheck(value: IrExpression, toType: IrType) =
|
||||
JsIrBuilder.buildTypeOperator(
|
||||
@@ -736,4 +663,4 @@ class StateMachineBuilder(
|
||||
|
||||
private fun tempVar(type: IrType, name: String = "tmp") =
|
||||
JsIrBuilder.buildVar(type, function.owner, name)
|
||||
}
|
||||
}
|
||||
|
||||
+25
-89
@@ -5,18 +5,14 @@
|
||||
|
||||
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.lower.FinallyBlocksLowering
|
||||
import org.jetbrains.kotlin.backend.common.pop
|
||||
import org.jetbrains.kotlin.backend.common.push
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
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.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
|
||||
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_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) {
|
||||
val current = hasSuspendableChildren
|
||||
hasSuspendableChildren = false
|
||||
element.acceptChildrenVoid(this)
|
||||
if (hasSuspendableChildren) {
|
||||
suspendableNodes += element
|
||||
markNode(element)
|
||||
}
|
||||
hasSuspendableChildren = hasSuspendableChildren || current
|
||||
}
|
||||
@@ -45,105 +48,38 @@ open class SuspendableNodesCollector(protected val suspendableNodes: MutableSet<
|
||||
override fun visitCall(expression: IrCall) {
|
||||
super.visitCall(expression)
|
||||
if (expression.isSuspend) {
|
||||
suspendableNodes += expression
|
||||
hasSuspendableChildren = true
|
||||
markNode(expression)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
var shouldFinalliesBeLowered = false
|
||||
|
||||
override fun visitBreakContinue(jump: IrBreakContinue) {
|
||||
if (jump.loop in suspendableNodes) {
|
||||
suspendableNodes.add(jump)
|
||||
hasSuspendableChildren = true
|
||||
if (isSuspendableNode(jump.loop)) {
|
||||
markNode(jump)
|
||||
}
|
||||
|
||||
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) {
|
||||
shouldFinalliesBeLowered = shouldFinalliesBeLowered || tryStack.any { it.finallyExpression != null && it in suspendableNodes }
|
||||
|
||||
super.visitReturn(expression)
|
||||
|
||||
if (expression.returnTargetSymbol is IrReturnableBlockSymbol) {
|
||||
suspendableNodes.add(expression)
|
||||
hasSuspendableChildren = true
|
||||
if (expression.returnTargetSymbol is IrReturnableBlockSymbol && isSuspendableNode(expression.returnTargetSymbol.owner)) {
|
||||
markNode(expression)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
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");
|
||||
}
|
||||
|
||||
@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")
|
||||
public void testSafeCallOnTwoReceiversLong_1_2() throws Exception {
|
||||
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");
|
||||
}
|
||||
|
||||
@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")
|
||||
public void testSuspendOperatorPlusAssign_1_2() throws Exception {
|
||||
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");
|
||||
}
|
||||
|
||||
@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")
|
||||
public void testSafeCallOnTwoReceiversLong_1_2() throws Exception {
|
||||
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");
|
||||
}
|
||||
|
||||
@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")
|
||||
public void testSuspendOperatorPlusAssign_1_2() throws Exception {
|
||||
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");
|
||||
}
|
||||
|
||||
@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")
|
||||
public void testSafeCallOnTwoReceiversLong_1_2() throws Exception {
|
||||
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");
|
||||
}
|
||||
|
||||
@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")
|
||||
public void testSuspendOperatorPlusAssign_1_2() throws Exception {
|
||||
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");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineSuspendFinally.kt")
|
||||
public void testInlineSuspendFinally_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/inlineSuspendFinally.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("safeCallOnTwoReceiversLong.kt")
|
||||
public void testSafeCallOnTwoReceiversLong_1_3() throws Exception {
|
||||
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");
|
||||
}
|
||||
|
||||
@TestMetadata("suspendInlineSuspendFinally.kt")
|
||||
public void testSuspendInlineSuspendFinally_1_3() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/suspendInlineSuspendFinally.kt", "kotlin.coroutines");
|
||||
}
|
||||
|
||||
@TestMetadata("suspendOperatorPlusAssign.kt")
|
||||
public void testSuspendOperatorPlusAssign_1_3() throws Exception {
|
||||
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");
|
||||
}
|
||||
|
||||
@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")
|
||||
public void testSafeCallOnTwoReceiversLong_1_2() throws Exception {
|
||||
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");
|
||||
}
|
||||
|
||||
@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")
|
||||
public void testSuspendOperatorPlusAssign_1_2() throws Exception {
|
||||
runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/featureIntersection/suspendOperatorPlusAssign.kt", "kotlin.coroutines.experimental");
|
||||
|
||||
Reference in New Issue
Block a user