Support coroutines stack-unwinding in JVM backend

#KT-14924 In Progress
This commit is contained in:
Denis Zharkov
2016-11-29 10:30:33 +03:00
parent 27e4caf046
commit fcd9ee037e
86 changed files with 404 additions and 35 deletions
@@ -2810,7 +2810,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
if (CoroutineCodegenUtilKt.isSuspensionPoint(resolvedCall, bindingContext)) {
// Suspension points should behave like they leave actual values on stack, while real methods return VOID
return new OperationStackValue(getSuspensionReturnTypeByResolvedCall(resolvedCall), ((OperationStackValue) result).getLambda());
return new OperationStackValue(getSuspensionBoxedReturnTypeByResolvedCall(resolvedCall), ((OperationStackValue) result).getLambda());
}
return result;
@@ -2925,7 +2925,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
}
if (isSuspensionPoint) {
v.tconst(getSuspensionReturnTypeByResolvedCall(resolvedCall));
v.tconst(getSuspensionBoxedReturnTypeByResolvedCall(resolvedCall));
v.invokestatic(
CoroutineCodegenUtilKt.COROUTINE_MARKER_OWNER,
CoroutineCodegenUtilKt.BEFORE_SUSPENSION_POINT_MARKER_NAME,
@@ -2935,7 +2935,6 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
callGenerator.genCall(callableMethod, resolvedCall, defaultMaskWasGenerated, this);
if (isSuspensionPoint) {
AsmUtil.pushDefaultValueOnStack(getSuspensionReturnTypeByResolvedCall(resolvedCall), v);
v.invokestatic(
CoroutineCodegenUtilKt.COROUTINE_MARKER_OWNER,
CoroutineCodegenUtilKt.AFTER_SUSPENSION_POINT_MARKER_NAME, "()V", false);
@@ -2950,7 +2949,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
}
@NotNull
private Type getSuspensionReturnTypeByResolvedCall(@NotNull ResolvedCall<?> resolvedCall) {
private Type getSuspensionBoxedReturnTypeByResolvedCall(@NotNull ResolvedCall<?> resolvedCall) {
assert resolvedCall.getResultingDescriptor() instanceof SimpleFunctionDescriptor
: "Suspension point resolved call should be built on SimpleFunctionDescriptor";
@@ -2963,7 +2962,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
KotlinType returnType = initialSignature.getReturnType();
assert returnType != null : "Return type of suspension point should not be null";
return typeMapper.mapType(returnType);
return typeMapper.mapType(TypeUtils.makeNullable(returnType));
}
@NotNull
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
import org.jetbrains.kotlin.codegen.optimization.common.analyzeLiveness
import org.jetbrains.kotlin.codegen.optimization.common.insnListOf
import org.jetbrains.kotlin.codegen.optimization.common.removeEmptyCatchBlocks
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.utils.sure
@@ -95,8 +96,10 @@ class CoroutineTransformerMethodVisitor(
spillVariables(suspensionPoints, methodNode)
val suspendMarkerVarIndex = methodNode.maxLocals++
val suspensionPointLabels = suspensionPoints.withIndex().map {
transformCallAndReturnContinuationLabel(it.index + 1, it.value, methodNode)
transformCallAndReturnContinuationLabel(it.index + 1, it.value, methodNode, suspendMarkerVarIndex)
}
methodNode.instructions.apply {
@@ -116,6 +119,13 @@ class CoroutineTransformerMethodVisitor(
// tableswitch(this.label)
insertBefore(firstToInsertBefore,
insnListOf(
FieldInsnNode(
Opcodes.GETSTATIC,
AsmTypes.COROUTINES_SUSPEND.internalName,
JvmAbi.INSTANCE_FIELD,
AsmTypes.COROUTINES_SUSPEND.descriptor
),
VarInsnNode(Opcodes.ASTORE, suspendMarkerVarIndex),
VarInsnNode(Opcodes.ALOAD, 0),
FieldInsnNode(
Opcodes.GETFIELD,
@@ -178,20 +188,16 @@ class CoroutineTransformerMethodVisitor(
methodNode.instructions.remove(beforeSuspensionPointMarker.previous)
beforeSuspensionPointMarker.desc = "()V"
if (suspensionPoint.returnType != Type.VOID_TYPE) {
val previous = suspensionPoint.suspensionCallEnd.previous
assert(previous.opcode in DEFAULT_VALUE_OPCODES) {
"Expected on of default value bytecodes, but ${previous.opcode} was found"
}
fakeReturnValueInsns.add(previous)
assert(suspensionPoint.returnType != Type.VOID_TYPE) { "Suspension point can't be VOID" }
if (previous.opcode == Opcodes.ACONST_NULL) {
// Checkcast is needed to tell analyzer what type exactly should be returned from suspension point
val checkCast = TypeInsnNode(Opcodes.CHECKCAST, suspensionPoint.returnType.internalName)
methodNode.instructions.insert(previous, checkCast)
fakeReturnValueInsns.add(checkCast)
}
}
// Checkcast only is needed to tell analyzer what type exactly should be returned from suspension point
// This type is used when for restoring spilled variables into fields
val checkCast = TypeInsnNode(Opcodes.CHECKCAST, suspensionPoint.returnType.internalName)
methodNode.instructions.insertBefore(
suspensionPoint.suspensionCallEnd, checkCast
)
fakeReturnValueInsns.add(checkCast)
}
}
}
@@ -317,8 +323,14 @@ class CoroutineTransformerMethodVisitor(
return suspensionCallEnd.next as LabelNode
}
private fun transformCallAndReturnContinuationLabel(id: Int, suspension: SuspensionPoint, methodNode: MethodNode): LabelNode {
private fun transformCallAndReturnContinuationLabel(
id: Int,
suspension: SuspensionPoint,
methodNode: MethodNode,
suspendMarkerVarIndex: Int
): LabelNode {
val continuationLabel = LabelNode()
val continuationLabelAfterLoadedResult = LabelNode()
with(methodNode.instructions) {
// Save state
insertBefore(suspension.suspensionCallBegin,
@@ -336,6 +348,10 @@ class CoroutineTransformerMethodVisitor(
suspension.fakeReturnValueInsns.forEach { methodNode.instructions.remove(it) }
insert(suspension.tryCatchBlockEndLabelAfterSuspensionCall, withInstructionAdapter {
dup()
load(suspendMarkerVarIndex, AsmTypes.COROUTINES_SUSPEND)
ifacmpne(continuationLabelAfterLoadedResult.label)
// Exit
areturn(Type.VOID_TYPE)
// Mark place for continuation
@@ -358,6 +374,8 @@ class CoroutineTransformerMethodVisitor(
// Load continuation argument just like suspending function returns it
load(1, AsmTypes.OBJECT_TYPE)
visitLabel(continuationLabelAfterLoadedResult.label)
StackValue.coerce(AsmTypes.OBJECT_TYPE, suspension.returnType, this)
})
}
@@ -371,8 +389,7 @@ class CoroutineTransformerMethodVisitor(
// How suspension point area will look like after all transformations:
// <spill variables>
// INVOKESTATIC beforeSuspensionMarker
// INVOKEVIRTUAL suspensionMethod()V
// ACONST_NULL -- default value
// INVOKEVIRTUAL suspensionMethod()Ljava/lang/Object;
// CHECKCAST SomeType
// INVOKESTATIC afterSuspensionMarker
// L1: -- end of all TCB's that are containing the suspension point (inserted by this method)
@@ -480,8 +497,7 @@ private fun Type.normalize() =
/**
* Suspension call may consists of several instructions:
* INVOKESTATIC beforeSuspensionMarker
* INVOKEVIRTUAL suspensionMethod()V // actually it could be some inline method instead of plain call
* ACONST_NULL // It's only needed when the suspension point returns something beside VOID
* INVOKEVIRTUAL suspensionMethod()Ljava/lang/Object; // actually it could be some inline method instead of plain call
* CHECKCAST Type
* INVOKESTATIC afterSuspensionMarker
*/
@@ -189,7 +189,7 @@ fun ResolvedCall<*>.isSuspensionPoint(bindingContext: BindingContext) =
bindingContext[BindingContext.COROUTINE_RECEIVER_FOR_SUSPENSION_POINT, call] != null
// Suspend functions have irregular signatures on JVM, containing an additional last parameter with type `Continuation<return-type>`,
// and return type Unit (later it will be replaced with 'Any?')
// and return type Any?
// This function returns a function descriptor reflecting how the suspend function looks from point of view of JVM
fun <D : FunctionDescriptor> createJvmSuspendFunctionView(function: D): D {
val continuationParameter = ValueParameterDescriptorImpl(
@@ -201,7 +201,7 @@ fun <D : FunctionDescriptor> createJvmSuspendFunctionView(function: D): D {
return function.createCustomCopy {
setPreserveSourceElement()
setReturnType(function.builtIns.unitType)
setReturnType(function.builtIns.nullableAnyType)
setValueParameters(it.valueParameters + continuationParameter)
putUserData(INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION, it)
}
@@ -288,8 +288,7 @@ fun createMethodNodeForSuspendWithCurrentContinuation(
"(${AsmTypes.OBJECT_TYPE})${AsmTypes.OBJECT_TYPE}",
true
)
node.visitInsn(Opcodes.POP)
node.visitInsn(Opcodes.RETURN)
node.visitInsn(Opcodes.ARETURN)
node.visitMaxs(2, 2)
return node
@@ -41,6 +41,7 @@ public class AsmTypes {
public static final Type MUTABLE_PROPERTY_REFERENCE1 = Type.getObjectType("kotlin/jvm/internal/MutablePropertyReference1");
public static final Type MUTABLE_PROPERTY_REFERENCE2 = Type.getObjectType("kotlin/jvm/internal/MutablePropertyReference2");
public static final Type COROUTINE_IMPL = Type.getObjectType("kotlin/jvm/internal/CoroutineImpl");
public static final Type COROUTINES_SUSPEND = Type.getObjectType("kotlin/coroutines/Suspend");
public static final Type[] PROPERTY_REFERENCE_IMPL = {
@@ -7,7 +7,7 @@ class Controller {
exception = t
}
suspend fun suspendHere(): Any = suspendWithCurrentContinuation { x ->}
suspend fun suspendHere(): Any = suspendWithCurrentContinuation { x -> }
// INTERCEPT_RESUME_PLACEHOLDER
}
@@ -7,6 +7,7 @@ class Controller {
suspend fun <T> await(t: T): T = suspendWithCurrentContinuation { c ->
c.resume(t)
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -7,6 +7,7 @@ class Controller {
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
c.resume(value)
Suspend
}
}
@@ -7,6 +7,7 @@ class Controller {
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
c.resume(value)
Suspend
}
}
@@ -7,6 +7,7 @@ class Controller {
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
c.resume(value)
Suspend
}
}
@@ -7,6 +7,7 @@ class Controller {
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
c.resume(value)
Suspend
}
}
@@ -7,6 +7,7 @@ class Controller {
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
c.resume(value)
Suspend
}
}
@@ -7,6 +7,7 @@ class Controller {
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
c.resume(value)
Suspend
}
}
@@ -12,6 +12,7 @@ class Controller {
suspend fun <T> suspendAndLog(value: T): T = suspendWithCurrentContinuation { c ->
result += "suspend($value);"
c.resume(value)
Suspend
}
operator fun handleResult(value: String, c: Continuation<Nothing>) {
@@ -8,6 +8,7 @@ class Controller {
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
result += "["
c.resume(value)
Suspend
}
}
@@ -8,11 +8,13 @@ class Controller {
suspend fun <T> suspendAndLog(value: T): T = suspendWithCurrentContinuation { c ->
result += "suspend($value);"
c.resume(value)
Suspend
}
suspend fun suspendLogAndThrow(exception: Throwable): Nothing = suspendWithCurrentContinuation { c ->
result += "throw(${exception.message});"
c.resumeWithException(exception)
Suspend
}
operator fun handleException(exception: Throwable, c: Continuation<Nothing>) {
@@ -8,6 +8,7 @@ class Controller {
suspend fun <T> suspendAndLog(value: T): T = suspendWithCurrentContinuation { c ->
result += "suspend($value);"
c.resume(value)
Suspend
}
operator fun handleException(exception: Throwable, c: Continuation<Nothing>) {
@@ -7,6 +7,7 @@ class Controller {
suspend fun <T> suspendWithResult(value: T): T = suspendWithCurrentContinuation { c ->
c.resume(value)
Suspend
}
}
@@ -3,6 +3,7 @@ class Controller {
var result = false
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
x.resume("OK")
Suspend
}
fun foo() {
@@ -2,6 +2,7 @@
class Controller {
suspend fun suspendHere(a: String = "abc", i: Int = 2): String = suspendWithCurrentContinuation { x ->
x.resume(a + "#" + (i + 1))
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -5,6 +5,7 @@ class Controller {
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
result++
x.resume("OK")
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -2,6 +2,7 @@
class Controller {
suspend fun <T> suspendHere(v: T): T = suspendWithCurrentContinuation { x ->
x.resume(v)
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
+2
View File
@@ -47,6 +47,8 @@ class GeneratorController<T>() : AbstractIterator<T>() {
suspend fun yield(value: T): Unit = suspendWithCurrentContinuation { c ->
setNext(value)
setNextStep(c)
Suspend
}
operator fun handleResult(result: Unit, c: Continuation<Nothing>) {
@@ -8,12 +8,16 @@ class Controller {
postponedActions.add {
x.resume(v)
}
Suspend
}
suspend fun suspendWithException(e: Exception): String = suspendWithCurrentContinuation { x ->
postponedActions.add {
x.resumeWithException(e)
}
Suspend
}
operator fun handleException(t: Throwable, c: Continuation<Nothing>) {
@@ -3,6 +3,7 @@ class Controller {
var isCompleted = false
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
x.resume("OK")
Suspend
}
operator fun handleResult(x: Unit, y: Continuation<Nothing>) {
@@ -5,6 +5,7 @@ class Controller {
suspend fun <T> suspendAndLog(value: T): T = suspendWithCurrentContinuation { x ->
log += "suspend($value);"
x.resume(value)
Suspend
}
operator fun handleResult(value: String, y: Continuation<Nothing>) {
@@ -5,6 +5,7 @@
class Controller {
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
x.resume(Unit)
Suspend
}
}
@@ -11,6 +11,7 @@ class Controller {
suspend inline fun suspendInline(v: String): String = suspendWithCurrentContinuation { x ->
withValue(v, x)
Suspend
}
suspend inline fun suspendInline(crossinline b: () -> String): String = suspendInline(b())
@@ -9,12 +9,16 @@ class Controller {
postponedActions.add {
x.resume(v)
}
Suspend
}
suspend fun suspendWithException(e: Exception): String = suspendWithCurrentContinuation { x ->
postponedActions.add {
x.resumeWithException(e)
}
Suspend
}
operator fun handleResult(x: String, c: Continuation<Nothing>) {
@@ -3,6 +3,7 @@ class Controller {
var i = 0
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
x.resume((i++).toString())
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -6,11 +6,13 @@ class Controller {
suspend fun runInstanceOf(): Boolean = suspendWithCurrentContinuation { x ->
val y: Any = x
x.resume(x is Continuation<*>)
Suspend
}
suspend fun runCast(): Boolean = suspendWithCurrentContinuation { x ->
val y: Any = x
x.resume(Continuation::class.isInstance(y as Continuation<*>))
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -2,6 +2,7 @@
class Controller {
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
x.resume(Unit)
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -2,6 +2,7 @@
class Controller {
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
x.resume(Unit)
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -2,6 +2,7 @@
class Controller {
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
x.resume(Unit)
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -2,6 +2,7 @@
class Controller {
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
x.resume(Unit)
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -2,6 +2,7 @@
class Controller {
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
x.resume(Unit)
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -2,6 +2,7 @@
class Controller {
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
x.resume(Unit)
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -4,6 +4,7 @@
class Controller {
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
x.resume(Unit)
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -2,6 +2,7 @@
class Controller {
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
x.resume(Unit)
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -4,6 +4,7 @@
class Controller {
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
x.resume(Unit)
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -2,6 +2,7 @@
class Controller {
suspend fun suspendHere(): Unit = suspendWithCurrentContinuation { x ->
x.resume(Unit)
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -2,6 +2,7 @@
class Controller {
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
x.resume("OK")
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
+1
View File
@@ -3,6 +3,7 @@ class Controller {
var result = "fail"
suspend fun <V> suspendHere(v: V): V = suspendWithCurrentContinuation { x ->
x.resume(v)
Suspend
}
operator fun handleResult(x: String, c: Continuation<Nothing>) {
@@ -2,6 +2,7 @@
class Controller {
suspend fun suspendHere(v: String): String = suspendWithCurrentContinuation { x ->
x.resume(v)
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -5,6 +5,7 @@ class Controller {
suspend fun suspendHere(v: String): Unit = suspendWithCurrentContinuation { x ->
result += v
x.resume(Unit)
Suspend
}
operator fun handleResult(u: Unit, v: Continuation<Nothing>) {
@@ -3,6 +3,7 @@ class Controller {
var wasHandleResultCalled = false
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
x.resume("OK")
Suspend
}
operator fun handleResult(x: Unit, y: Continuation<Nothing>) {
@@ -3,6 +3,7 @@ class Controller {
var wasHandleResultCalled = false
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
x.resume("OK")
Suspend
}
operator fun handleResult(x: Unit, y: Continuation<Nothing>) {
@@ -5,6 +5,7 @@ class Controller {
suspend fun suspendHere(v: String): Unit = suspendWithCurrentContinuation { x ->
this.v = v
x.resume(Unit)
Suspend
}
operator fun handleResult(u: Unit, v: Continuation<Nothing>) {
@@ -3,6 +3,7 @@
class Controller {
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
x.resume("OK")
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -6,6 +6,7 @@ package lib
class Controller {
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
x.resume("OK")
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -7,6 +7,7 @@ package lib
class Controller {
suspend fun String.suspendHere(): String = suspendWithCurrentContinuation { x ->
x.resume(this)
Suspend
}
inline suspend fun String.inlineSuspendHere(): String = suspendHere()
@@ -4,7 +4,7 @@ class Controller {
var result = "fail"
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
lastSuspension = x
Unit
Suspend
}
fun hasNext() = lastSuspension != null
@@ -4,7 +4,7 @@ class Controller {
var result = "fail"
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
lastSuspension = x
Unit
Suspend
}
fun hasNext() = lastSuspension != null
@@ -4,7 +4,7 @@ class Controller {
var result = "fail"
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
lastSuspension = x
Unit
Suspend
}
fun hasNext() = lastSuspension != null
@@ -4,7 +4,7 @@ class Controller {
var result = "fail"
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
lastSuspension = x
Unit
Suspend
}
fun hasNext() = lastSuspension != null
@@ -9,12 +9,16 @@ class Controller {
postponedActions.add {
x.resume(v)
}
Suspend
}
suspend fun suspendWithException(e: Exception): String = suspendWithCurrentContinuation { x ->
postponedActions.add {
x.resumeWithException(e)
}
Suspend
}
operator fun handleResult(x: String, c: Continuation<Nothing>) {
@@ -3,6 +3,7 @@ class Controller {
var cResult = 0
suspend fun suspendHere(v: Int): Int = suspendWithCurrentContinuation { x ->
x.resume(v * 2)
Suspend
}
operator fun handleResult(x: Int, y: Continuation<Nothing>) {
@@ -5,6 +5,7 @@ class Controller {
var cResult = 0
suspend fun suspendHere(v: Int): Int = suspendWithCurrentContinuation { x ->
x.resume(v * 2)
Suspend
}
operator fun handleResult(x: Int, y: Continuation<Nothing>) {
@@ -3,6 +3,7 @@ class Controller {
var res = 0
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
x.resume("OK")
Suspend
}
operator fun handleResult(x: Int, y: Continuation<Nothing>) {
+1
View File
@@ -2,6 +2,7 @@
class Controller {
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
x.resume("OK")
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -2,6 +2,7 @@
class Controller {
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
x.resumeWithException(RuntimeException("OK"))
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -3,6 +3,7 @@ class Controller {
var res = 0
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
x.resume("OK")
Suspend
}
operator fun handleResult(x: Int, y: Continuation<Nothing>) {
@@ -0,0 +1,19 @@
class Controller {
suspend fun suspendHere(): String = throw RuntimeException("OK")
// INTERCEPT_RESUME_PLACEHOLDER
}
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
c(Controller()).resume(Unit)
}
fun box(): String {
var result = ""
builder {
result = try { suspendHere() } catch (e: RuntimeException) { e.message!! }
}
return result
}
@@ -0,0 +1,36 @@
// WITH_RUNTIME
// WITH_REFLECT
// CHECK_NOT_CALLED: suspendInline_die06n$
// CHECK_NOT_CALLED: suspendInline_nesahw$
// CHECK_NOT_CALLED: suspendInline_grpnnl$
class Controller {
suspend inline fun suspendInline(v: String): String = v
suspend inline fun suspendInline(crossinline b: () -> String): String = suspendInline(b())
suspend inline fun <reified T : Any> suspendInline(): String = suspendInline({ T::class.simpleName!! })
// INTERCEPT_RESUME_PLACEHOLDER
}
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
c(Controller()).resume(Unit)
}
class OK
fun box(): String {
var result = ""
builder {
result = suspendInline("56")
if (result != "56") throw RuntimeException("fail 1")
result = suspendInline { "57" }
if (result != "57") throw RuntimeException("fail 2")
result = suspendInline<OK>()
}
return result
}
@@ -0,0 +1,19 @@
class Controller {
suspend fun suspendHere() = "OK"
// INTERCEPT_RESUME_PLACEHOLDER
}
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
c(Controller()).resume(Unit)
}
fun box(): String {
var result = ""
builder {
result = suspendHere()
}
return result
}
@@ -0,0 +1,47 @@
// IGNORE_BACKEND: JS
class Controller {
suspend fun suspendHere(): Int = suspendWithCurrentContinuation { x ->
1
}
suspend fun suspendThere(): String = suspendWithCurrentContinuation { x ->
"?"
}
// INTERCEPT_RESUME_PLACEHOLDER
}
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
c(Controller()).resume(Unit)
}
fun box(): String {
var result = ""
builder {
result += "-"
for (i in 0..10000) {
if (i % 2 == 0) {
result += suspendHere().toString()
}
else if (i == 3) {
result += suspendThere()
}
}
result += "+"
}
var mustBe = "-"
for (i in 0..10000) {
if (i % 2 == 0) {
mustBe += "1"
}
else if (i == 3) {
mustBe += "?"
}
}
mustBe += "+"
if (result != mustBe) return "fail: $result/$mustBe"
return "OK"
}
@@ -3,6 +3,7 @@ var globalResult = ""
class Controller {
suspend fun suspendWithValue(v: String): String = suspendWithCurrentContinuation { x ->
x.resume(v)
Suspend
}
operator fun handleResult(x: String, c: Continuation<Nothing>) {
@@ -4,6 +4,7 @@ class Controller {
suspend fun suspendThere(): String = suspendWithCurrentContinuation { x ->
x.resume("OK")
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -3,6 +3,7 @@
class Controller {
suspend fun String.suspendHere(): String = suspendWithCurrentContinuation { x ->
x.resume(this)
Suspend
}
inline suspend fun String.inlineSuspendHere(): String = suspendHere()
@@ -2,6 +2,7 @@
class Controller {
suspend fun suspendHere(v: Int): Int = suspendWithCurrentContinuation { x ->
x.resume(v * 2)
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -3,9 +3,11 @@ class Controller {
var i = 0
suspend fun suspendHere(): Int = suspendWithCurrentContinuation { x ->
x.resume(i++)
Suspend
}
suspend fun suspendThere(): String = suspendWithCurrentContinuation { x ->
x.resume("?")
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -2,14 +2,17 @@
class Controller {
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
x.resume("K")
Suspend
}
suspend fun suspendWithArgument(v: String): String = suspendWithCurrentContinuation { x ->
x.resume(v)
Suspend
}
suspend fun suspendWithDouble(v: Double): Double = suspendWithCurrentContinuation { x ->
x.resume(v)
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -9,12 +9,16 @@ class Controller {
postponedActions.add {
x.resume(v)
}
Suspend
}
suspend fun suspendWithException(e: Exception): String = suspendWithCurrentContinuation { x ->
postponedActions.add {
x.resumeWithException(e)
}
Suspend
}
operator fun handleResult(x: String, c: Continuation<Nothing>) {
@@ -9,12 +9,16 @@ class Controller {
postponedActions.add {
x.resume(v)
}
Suspend
}
suspend fun suspendWithException(e: Exception): String = suspendWithCurrentContinuation { x ->
postponedActions.add {
x.resumeWithException(e)
}
Suspend
}
operator fun handleResult(x: String, c: Continuation<Nothing>) {
@@ -2,6 +2,8 @@
class Controller {
suspend fun suspendHere(v: String): String = suspendWithCurrentContinuation { x ->
x.resume(v)
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -9,12 +9,16 @@ class Controller {
postponedActions.add {
x.resume(v)
}
Suspend
}
suspend fun suspendWithException(e: Exception): String = suspendWithCurrentContinuation { x ->
postponedActions.add {
x.resumeWithException(e)
}
Suspend
}
operator fun handleResult(x: String, c: Continuation<Nothing>) {
@@ -2,6 +2,7 @@
class Controller {
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
x.resume("OK")
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -2,6 +2,7 @@
class Controller {
suspend fun suspendHere(): String = suspendWithCurrentContinuation { x ->
x.resume("OK")
Suspend
}
// INTERCEPT_RESUME_PLACEHOLDER
@@ -1,7 +1,7 @@
@kotlin.Metadata
public final class Controller {
public method <init>(): void
public final method suspendHere(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.Nullable method suspendHere(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object
}
@kotlin.Metadata
+2 -1
View File
@@ -19,4 +19,5 @@ fun box(): String {
}
// 1 GETSTATIC kotlin/Unit.INSTANCE : Lkotlin/Unit;
// 1 GETSTATIC
// 1 GETSTATIC kotlin/coroutines/Suspend.INSTANCE
// 2 GETSTATIC
+2
View File
@@ -60,6 +60,8 @@ class FutureController<T> {
else
machine.resumeWithException(throwable)
}
Suspend
}
operator fun handleResult(value: T, c: Continuation<Nothing>) {
+2
View File
@@ -56,6 +56,8 @@ class FutureController<T> {
else
machine.resumeWithException(throwable)
}
Suspend
}
operator fun handleResult(value: T, c: Continuation<Nothing>) {
@@ -3,6 +3,7 @@ package a
class Controller {
suspend fun suspendHere() = suspendWithCurrentContinuation<String> { x ->
x.resume("OK")
Suspend
}
}
@@ -4945,6 +4945,39 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/coroutines/stackUnwinding")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class StackUnwinding extends AbstractIrBlackBoxCodegenTest {
public void testAllFilesPresentInStackUnwinding() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/stackUnwinding"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("exception.kt")
public void testException() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/stackUnwinding/exception.kt");
doTest(fileName);
}
@TestMetadata("inlineSuspendFunction.kt")
public void testInlineSuspendFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/stackUnwinding/inlineSuspendFunction.kt");
doTest(fileName);
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/stackUnwinding/simple.kt");
doTest(fileName);
}
@TestMetadata("suspendInCycle.kt")
public void testSuspendInCycle() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/stackUnwinding/suspendInCycle.kt");
doTest(fileName);
}
}
}
@TestMetadata("compiler/testData/codegen/box/dataClasses")
@@ -423,4 +423,37 @@ public class AdditionalCoroutineBlackBoxCodegenTestGenerated extends AbstractAdd
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/coroutines/stackUnwinding")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class StackUnwinding extends AbstractAdditionalCoroutineBlackBoxCodegenTest {
public void testAllFilesPresentInStackUnwinding() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/stackUnwinding"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("exception.kt")
public void testException() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/stackUnwinding/exception.kt");
doTest(fileName);
}
@TestMetadata("inlineSuspendFunction.kt")
public void testInlineSuspendFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/stackUnwinding/inlineSuspendFunction.kt");
doTest(fileName);
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/stackUnwinding/simple.kt");
doTest(fileName);
}
@TestMetadata("suspendInCycle.kt")
public void testSuspendInCycle() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/stackUnwinding/suspendInCycle.kt");
doTest(fileName);
}
}
}
@@ -4945,6 +4945,39 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/coroutines/stackUnwinding")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class StackUnwinding extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInStackUnwinding() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/stackUnwinding"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("exception.kt")
public void testException() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/stackUnwinding/exception.kt");
doTest(fileName);
}
@TestMetadata("inlineSuspendFunction.kt")
public void testInlineSuspendFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/stackUnwinding/inlineSuspendFunction.kt");
doTest(fileName);
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/stackUnwinding/simple.kt");
doTest(fileName);
}
@TestMetadata("suspendInCycle.kt")
public void testSuspendInCycle() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/stackUnwinding/suspendInCycle.kt");
doTest(fileName);
}
}
}
@TestMetadata("compiler/testData/codegen/box/dataClasses")
@@ -6152,6 +6152,45 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
}
}
@TestMetadata("compiler/testData/codegen/box/coroutines/stackUnwinding")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class StackUnwinding extends AbstractJsCodegenBoxTest {
public void testAllFilesPresentInStackUnwinding() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/stackUnwinding"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true);
}
@TestMetadata("exception.kt")
public void testException() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/stackUnwinding/exception.kt");
doTest(fileName);
}
@TestMetadata("inlineSuspendFunction.kt")
public void testInlineSuspendFunction() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/stackUnwinding/inlineSuspendFunction.kt");
doTest(fileName);
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/stackUnwinding/simple.kt");
doTest(fileName);
}
@TestMetadata("suspendInCycle.kt")
public void testSuspendInCycle() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/stackUnwinding/suspendInCycle.kt");
try {
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
}
}
}
@TestMetadata("compiler/testData/codegen/box/dataClasses")