Skip to content

Commit b61a66c

Browse files
authored
Display artificial stack frames as calls to functions instead of text (#2461)
Fixes #2291
1 parent d23dd3d commit b61a66c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+179
-145
lines changed

docs/images/after.png

-134 KB
Loading

docs/images/before.png

-61.8 KB
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
/* This package name is like this so that
6+
1) the artificial stack frames look pretty, and
7+
2) the IDE reliably navigates to this file. */
8+
package _COROUTINE
9+
10+
/**
11+
* A collection of artificial stack trace elements to be included in stack traces by the coroutines machinery.
12+
*
13+
* There are typically two ways in which one can encounter an artificial stack frame:
14+
* 1. By using the debug mode, via the stacktrace recovery mechanism; see
15+
* [stacktrace recovery](https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/topics/debugging.md#stacktrace-recovery)
16+
* documentation. The usual way to enable the debug mode is with the [kotlinx.coroutines.DEBUG_PROPERTY_NAME] system
17+
* property.
18+
* 2. By looking at the output of DebugProbes; see the
19+
* [kotlinx-coroutines-debug](https://github.com/Kotlin/kotlinx.coroutines/tree/master/kotlinx-coroutines-debug) module.
20+
*/
21+
internal class ArtificialStackFrames {
22+
/**
23+
* Returns an artificial stack trace element denoting the boundary between coroutine creation and its execution.
24+
*
25+
* Appearance of this function in stack traces does not mean that it was called. Instead, it is used as a marker
26+
* that separates the part of the stack trace with the code executed in a coroutine from the stack trace of the code
27+
* that launched the coroutine.
28+
*
29+
* In earlier versions of kotlinx-coroutines, this was displayed as "(Coroutine creation stacktrace)", which caused
30+
* problems for tooling that processes stack traces: https://github.com/Kotlin/kotlinx.coroutines/issues/2291
31+
*
32+
* Note that presence of this marker in a stack trace implies that coroutine creation stack traces were enabled.
33+
*/
34+
fun coroutineCreation(): StackTraceElement = Exception().artificialFrame(_CREATION::class.java.simpleName)
35+
36+
/**
37+
* Returns an artificial stack trace element denoting a coroutine boundary.
38+
*
39+
* Appearance of this function in stack traces does not mean that it was called. Instead, when one coroutine invokes
40+
* another, this is used as a marker in the stack trace to denote where the execution of one coroutine ends and that
41+
* of another begins.
42+
*
43+
* In earlier versions of kotlinx-coroutines, this was displayed as "(Coroutine boundary)", which caused
44+
* problems for tooling that processes stack traces: https://github.com/Kotlin/kotlinx.coroutines/issues/2291
45+
*/
46+
fun coroutineBoundary(): StackTraceElement = Exception().artificialFrame(_BOUNDARY::class.java.simpleName)
47+
}
48+
49+
// These are needed for the IDE navigation to detect that this file does contain the definition.
50+
private class _CREATION
51+
private class _BOUNDARY
52+
53+
internal val ARTIFICIAL_FRAME_PACKAGE_NAME = "_COROUTINE"
54+
55+
/**
56+
* Forms an artificial stack frame with the given class name.
57+
*
58+
* It consists of the following parts:
59+
* 1. The package name, it seems, is needed for the IDE to detect stack trace elements reliably. It is `_COROUTINE` since
60+
* this is a valid identifier.
61+
* 2. Class names represents what type of artificial frame this is.
62+
* 3. The method name is `_`. The methods not being present in class definitions does not seem to affect navigation.
63+
*/
64+
private fun Throwable.artificialFrame(name: String): StackTraceElement =
65+
with(stackTrace[0]) { StackTraceElement(ARTIFICIAL_FRAME_PACKAGE_NAME + "." + name, "_", fileName, lineNumber) }

kotlinx-coroutines-core/jvm/src/debug/internal/DebugProbesImpl.kt

+16-18
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ package kotlinx.coroutines.debug.internal
77
import kotlinx.atomicfu.*
88
import kotlinx.coroutines.*
99
import kotlinx.coroutines.debug.*
10-
import kotlinx.coroutines.internal.*
1110
import kotlinx.coroutines.internal.ScopeCoroutine
1211
import java.io.*
1312
import java.lang.StackTraceElement
@@ -18,10 +17,10 @@ import kotlin.concurrent.*
1817
import kotlin.coroutines.*
1918
import kotlin.coroutines.jvm.internal.CoroutineStackFrame
2019
import kotlin.synchronized
21-
import kotlinx.coroutines.internal.artificialFrame as createArtificialFrame // IDEA bug workaround
20+
import _COROUTINE.ArtificialStackFrames
2221

2322
internal object DebugProbesImpl {
24-
private const val ARTIFICIAL_FRAME_MESSAGE = "Coroutine creation stacktrace"
23+
private val ARTIFICIAL_FRAME = ArtificialStackFrames().coroutineCreation()
2524
private val dateFormat = SimpleDateFormat("yyyy/MM/dd HH:mm:ss")
2625

2726
private var weakRefCleanerThread: Thread? = null
@@ -219,7 +218,7 @@ internal object DebugProbesImpl {
219218
info.state
220219
out.print("\n\nCoroutine ${owner.delegate}, state: $state")
221220
if (observedStackTrace.isEmpty()) {
222-
out.print("\n\tat ${createArtificialFrame(ARTIFICIAL_FRAME_MESSAGE)}")
221+
out.print("\n\tat $ARTIFICIAL_FRAME")
223222
printStackTrace(out, info.creationStackTrace)
224223
} else {
225224
printStackTrace(out, enhancedStackTrace)
@@ -416,15 +415,17 @@ internal object DebugProbesImpl {
416415
return createOwner(completion, frame)
417416
}
418417

419-
private fun List<StackTraceElement>.toStackTraceFrame(): StackTraceFrame? =
420-
foldRight<StackTraceElement, StackTraceFrame?>(null) { frame, acc ->
421-
StackTraceFrame(acc, frame)
422-
}
418+
private fun List<StackTraceElement>.toStackTraceFrame(): StackTraceFrame =
419+
StackTraceFrame(
420+
foldRight<StackTraceElement, StackTraceFrame?>(null) { frame, acc ->
421+
StackTraceFrame(acc, frame)
422+
}, ARTIFICIAL_FRAME
423+
)
423424

424425
private fun <T> createOwner(completion: Continuation<T>, frame: StackTraceFrame?): Continuation<T> {
425426
if (!isInstalled) return completion
426427
val info = DebugCoroutineInfoImpl(completion.context, frame, sequenceNumber.incrementAndGet())
427-
val owner = CoroutineOwner(completion, info, frame)
428+
val owner = CoroutineOwner(completion, info)
428429
capturedCoroutinesMap[owner] = true
429430
if (!isInstalled) capturedCoroutinesMap.clear()
430431
return owner
@@ -447,9 +448,9 @@ internal object DebugProbesImpl {
447448
*/
448449
private class CoroutineOwner<T>(
449450
@JvmField val delegate: Continuation<T>,
450-
@JvmField val info: DebugCoroutineInfoImpl,
451-
private val frame: CoroutineStackFrame?
451+
@JvmField val info: DebugCoroutineInfoImpl
452452
) : Continuation<T> by delegate, CoroutineStackFrame {
453+
private val frame get() = info.creationStackBottom
453454

454455
override val callerFrame: CoroutineStackFrame?
455456
get() = frame?.callerFrame
@@ -467,12 +468,10 @@ internal object DebugProbesImpl {
467468
private fun <T : Throwable> sanitizeStackTrace(throwable: T): List<StackTraceElement> {
468469
val stackTrace = throwable.stackTrace
469470
val size = stackTrace.size
470-
val probeIndex = stackTrace.indexOfLast { it.className == "kotlin.coroutines.jvm.internal.DebugProbesKt" }
471+
val traceStart = 1 + stackTrace.indexOfLast { it.className == "kotlin.coroutines.jvm.internal.DebugProbesKt" }
471472

472473
if (!sanitizeStackTraces) {
473-
return List(size - probeIndex) {
474-
if (it == 0) createArtificialFrame(ARTIFICIAL_FRAME_MESSAGE) else stackTrace[it + probeIndex]
475-
}
474+
return List(size - traceStart) { stackTrace[it + traceStart] }
476475
}
477476

478477
/*
@@ -483,9 +482,8 @@ internal object DebugProbesImpl {
483482
* If an interval of internal methods ends in a synthetic method, the outermost non-synthetic method in that
484483
* interval will also be included.
485484
*/
486-
val result = ArrayList<StackTraceElement>(size - probeIndex + 1)
487-
result += createArtificialFrame(ARTIFICIAL_FRAME_MESSAGE)
488-
var i = probeIndex + 1
485+
val result = ArrayList<StackTraceElement>(size - traceStart + 1)
486+
var i = traceStart
489487
while (i < size) {
490488
if (stackTrace[i].isInternalMethod) {
491489
result += stackTrace[i] // we include the boundary of the span in any case

kotlinx-coroutines-core/jvm/src/internal/StackTraceRecovery.kt

+8-9
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
package kotlinx.coroutines.internal
88

99
import kotlinx.coroutines.*
10+
import _COROUTINE.ARTIFICIAL_FRAME_PACKAGE_NAME
11+
import _COROUTINE.ArtificialStackFrames
1012
import java.util.*
1113
import kotlin.coroutines.*
1214
import kotlin.coroutines.intrinsics.*
@@ -18,6 +20,8 @@ import kotlin.coroutines.intrinsics.*
1820
private const val baseContinuationImplClass = "kotlin.coroutines.jvm.internal.BaseContinuationImpl"
1921
private const val stackTraceRecoveryClass = "kotlinx.coroutines.internal.StackTraceRecoveryKt"
2022

23+
private val ARTIFICIAL_FRAME = ArtificialStackFrames().coroutineBoundary()
24+
2125
private val baseContinuationImplClassName = runCatching {
2226
Class.forName(baseContinuationImplClass).canonicalName
2327
}.getOrElse { baseContinuationImplClass }
@@ -42,7 +46,7 @@ private fun <E : Throwable> E.sanitizeStackTrace(): E {
4246
val adjustment = if (endIndex == -1) 0 else size - endIndex
4347
val trace = Array(size - lastIntrinsic - adjustment) {
4448
if (it == 0) {
45-
artificialFrame("Coroutine boundary")
49+
ARTIFICIAL_FRAME
4650
} else {
4751
stackTrace[startIndex + it - 1]
4852
}
@@ -91,13 +95,13 @@ private fun <E : Throwable> recoverFromStackFrame(exception: E, continuation: Co
9195
* IllegalStateException
9296
* at foo
9397
* at kotlin.coroutines.resumeWith
94-
* (Coroutine boundary)
98+
* at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)
9599
* at bar
96100
* ...real stackTrace...
97101
* caused by "IllegalStateException" (original one)
98102
*/
99103
private fun <E : Throwable> createFinalException(cause: E, result: E, resultStackTrace: ArrayDeque<StackTraceElement>): E {
100-
resultStackTrace.addFirst(artificialFrame("Coroutine boundary"))
104+
resultStackTrace.addFirst(ARTIFICIAL_FRAME)
101105
val causeTrace = cause.stackTrace
102106
val size = causeTrace.frameIndex(baseContinuationImplClassName)
103107
if (size == -1) {
@@ -187,12 +191,7 @@ private fun createStackTrace(continuation: CoroutineStackFrame): ArrayDeque<Stac
187191
return stack
188192
}
189193

190-
/**
191-
* @suppress
192-
*/
193-
@InternalCoroutinesApi
194-
public fun artificialFrame(message: String): StackTraceElement = java.lang.StackTraceElement("\b\b\b($message", "\b", "\b", -1)
195-
internal fun StackTraceElement.isArtificial() = className.startsWith("\b\b\b")
194+
internal fun StackTraceElement.isArtificial() = className.startsWith(ARTIFICIAL_FRAME_PACKAGE_NAME)
196195
private fun Array<StackTraceElement>.frameIndex(methodName: String) = indexOfFirst { methodName == it.className }
197196

198197
private fun StackTraceElement.elementWiseEquals(e: StackTraceElement): Boolean {

kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testCancelledOffer.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
kotlinx.coroutines.JobCancellationException: Job was cancelled; job=JobImpl{Cancelling}@2a06d350
2-
(Coroutine boundary)
2+
at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)
33
at kotlinx.coroutines.channels.AbstractSendChannel.offer(AbstractChannel.kt:170)
44
at kotlinx.coroutines.channels.ChannelCoroutine.offer(ChannelCoroutine.kt)
55
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testCancelledOffer$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:153)
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
kotlinx.coroutines.RecoverableTestException
22
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testOfferFromScope$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:109)
3-
(Coroutine boundary)
3+
at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)
44
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.sendInChannel(StackTraceRecoveryChannelsTest.kt:167)
55
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$sendWithContext$2.invokeSuspend(StackTraceRecoveryChannelsTest.kt:162)
66
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$sendFromScope$2.invokeSuspend(StackTraceRecoveryChannelsTest.kt:172)
77
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testOfferFromScope$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:112)
88
Caused by: kotlinx.coroutines.RecoverableTestException
99
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testOfferFromScope$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:109)
10-
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
10+
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)

kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testOfferWithContextWrapped.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
kotlinx.coroutines.RecoverableTestException
22
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testOfferWithContextWrapped$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:98)
3-
(Coroutine boundary)
3+
at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)
44
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.sendInChannel(StackTraceRecoveryChannelsTest.kt:199)
55
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$sendWithContext$2.invokeSuspend(StackTraceRecoveryChannelsTest.kt:194)
66
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testOfferWithContextWrapped$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:100)

kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testOfferWithCurrentContext.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
kotlinx.coroutines.RecoverableTestException
22
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testOfferWithCurrentContext$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:86)
3-
(Coroutine boundary)
3+
at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)
44
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.sendInChannel(StackTraceRecoveryChannelsTest.kt:210)
55
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$sendWithContext$2.invokeSuspend(StackTraceRecoveryChannelsTest.kt:205)
66
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testOfferWithCurrentContext$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:89)
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
kotlinx.coroutines.RecoverableTestException
22
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testReceiveFromChannel$1$job$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:97)
3-
(Coroutine boundary)
3+
at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)
44
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.channelReceive(StackTraceRecoveryChannelsTest.kt:116)
55
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testReceiveFromChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:101)
66
Caused by: kotlinx.coroutines.RecoverableTestException
77
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testReceiveFromChannel$1$job$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:97)
8-
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
8+
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
kotlinx.coroutines.RecoverableTestException
22
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testReceiveFromClosedChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:110)
3-
(Coroutine boundary)
3+
at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)
44
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.channelReceive(StackTraceRecoveryChannelsTest.kt:116)
55
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testReceiveFromClosedChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:111)
66
Caused by: kotlinx.coroutines.RecoverableTestException
77
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testReceiveFromClosedChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:110)
8-
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
8+
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
kotlinx.coroutines.RecoverableTestCancellationException
22
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendFromScope$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:136)
3-
(Coroutine boundary)
3+
at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)
44
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.sendInChannel(StackTraceRecoveryChannelsTest.kt:167)
55
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$sendWithContext$2.invokeSuspend(StackTraceRecoveryChannelsTest.kt:162)
66
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$sendFromScope$2.invokeSuspend(StackTraceRecoveryChannelsTest.kt:172)
77
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendFromScope$1$deferred$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:126)
88
Caused by: kotlinx.coroutines.RecoverableTestCancellationException
99
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendFromScope$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:136)
10-
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
10+
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)

kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testSendToChannel.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ java.util.concurrent.CancellationException: RendezvousChannel was cancelled
22
at kotlinx.coroutines.channels.AbstractChannel.cancel(AbstractChannel.kt:630)
33
at kotlinx.coroutines.channels.ReceiveChannel$DefaultImpls.cancel$default(Channel.kt:311)
44
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendToChannel$1$job$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:52)
5-
(Coroutine boundary)
5+
at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)
66
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.channelSend(StackTraceRecoveryChannelsTest.kt:73)
77
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendToChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:56)
88
Caused by: java.util.concurrent.CancellationException: RendezvousChannel was cancelled
99
at kotlinx.coroutines.channels.AbstractChannel.cancel(AbstractChannel.kt:630)
1010
at kotlinx.coroutines.channels.ReceiveChannel$DefaultImpls.cancel$default(Channel.kt:311)
1111
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendToChannel$1$job$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:52)
12-
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
12+
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
kotlinx.coroutines.RecoverableTestException
22
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendToClosedChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:43)
3-
(Coroutine boundary)
3+
at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt)
44
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.channelSend(StackTraceRecoveryChannelsTest.kt:74)
55
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendToClosedChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:44)
66
Caused by: kotlinx.coroutines.RecoverableTestException
77
at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendToClosedChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:43)
8-
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
8+
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)

0 commit comments

Comments
 (0)