diff --git a/compiler/util/src/org/jetbrains/kotlin/utils/kapt/MemoryLeakDetector.kt b/compiler/util/src/org/jetbrains/kotlin/utils/kapt/MemoryLeakDetector.kt index f549394cacb..05dacdf7cda 100644 --- a/compiler/util/src/org/jetbrains/kotlin/utils/kapt/MemoryLeakDetector.kt +++ b/compiler/util/src/org/jetbrains/kotlin/utils/kapt/MemoryLeakDetector.kt @@ -5,8 +5,8 @@ package org.jetbrains.kotlin.utils.kapt +import org.jetbrains.kotlin.utils.getSafe import java.lang.ref.WeakReference -import java.lang.reflect.Field import java.lang.reflect.Modifier import java.util.* import javax.annotation.processing.* @@ -139,19 +139,4 @@ private fun ClassLoader.loadedClasses(): Vector> { } catch (e: Throwable) { return Vector() } -} - -private fun Field.getSafe(obj: Any?): Any? { - return try { - val oldIsAccessible = isAccessible - - try { - isAccessible = true - get(obj) - } finally { - isAccessible = oldIsAccessible - } - } catch (e: Throwable) { - null - } } \ No newline at end of file diff --git a/compiler/util/src/org/jetbrains/kotlin/utils/reflectionUtil.kt b/compiler/util/src/org/jetbrains/kotlin/utils/reflectionUtil.kt new file mode 100644 index 00000000000..6da4270cb9f --- /dev/null +++ b/compiler/util/src/org/jetbrains/kotlin/utils/reflectionUtil.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.utils + +import java.lang.reflect.Field + +fun Field.getSafe(obj: Any?): Any? { + return try { + val oldIsAccessible = isAccessible + + try { + isAccessible = true + get(obj) + } finally { + isAccessible = oldIsAccessible + } + } catch (e: Throwable) { + null + } +} \ No newline at end of file diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index c4dca11dbd5..563fa1b5a81 100755 --- a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -656,6 +656,10 @@ fun main(args: Array) { model("debugger/tinyApp/src/evaluate/multipleBreakpoints", testMethod = "doMultipleBreakpointsTest") } + testClass { + model("debugger/tinyApp/src/asyncStackTrace") + } + testClass { model("debugger/fileRanking") } diff --git a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as34 b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as34 index 38dc4f50554..a4955ad215e 100644 --- a/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as34 +++ b/generators/tests/org/jetbrains/kotlin/generators/tests/GenerateTests.kt.as34 @@ -67,10 +67,7 @@ import org.jetbrains.kotlin.idea.conversion.copy.AbstractLiteralKotlinToKotlinCo import org.jetbrains.kotlin.idea.conversion.copy.AbstractLiteralTextToKotlinCopyPasteTest import org.jetbrains.kotlin.idea.conversion.copy.AbstractTextJavaToKotlinCopyPasteConversionTest import org.jetbrains.kotlin.idea.coverage.AbstractKotlinCoverageOutputFilesTest -import org.jetbrains.kotlin.idea.debugger.AbstractBeforeExtractFunctionInsertionTest -import org.jetbrains.kotlin.idea.debugger.AbstractKotlinSteppingTest -import org.jetbrains.kotlin.idea.debugger.AbstractPositionManagerTest -import org.jetbrains.kotlin.idea.debugger.AbstractSmartStepIntoTest +import org.jetbrains.kotlin.idea.debugger.* import org.jetbrains.kotlin.idea.debugger.evaluate.* import org.jetbrains.kotlin.idea.decompiler.navigation.AbstractNavigateToDecompiledLibraryTest import org.jetbrains.kotlin.idea.decompiler.navigation.AbstractNavigateToLibrarySourceTest @@ -639,6 +636,10 @@ fun main(args: Array) { model("debugger/tinyApp/src/evaluate/multipleBreakpoints", testMethod = "doMultipleBreakpointsTest") } + testClass { + model("debugger/tinyApp/src/asyncStackTrace") + } + testClass { model("stubs", extension = "kt") } diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinCoroutinesAsyncStackTraceProvider.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinCoroutinesAsyncStackTraceProvider.kt new file mode 100644 index 00000000000..a813fb9e6be --- /dev/null +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/debugger/KotlinCoroutinesAsyncStackTraceProvider.kt @@ -0,0 +1,201 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.debugger + +import com.intellij.debugger.DebuggerContext +import com.intellij.debugger.engine.AsyncStackTraceProvider +import com.intellij.debugger.engine.JavaStackFrame +import com.intellij.debugger.engine.JavaValue +import com.intellij.debugger.engine.SuspendContextImpl +import com.intellij.debugger.engine.evaluation.EvaluateException +import com.intellij.debugger.engine.evaluation.EvaluationContextImpl +import com.intellij.debugger.jdi.GeneratedLocation +import com.intellij.debugger.jdi.StackFrameProxyImpl +import com.intellij.debugger.memory.utils.StackFrameItem +import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl +import com.intellij.xdebugger.frame.XNamedValue +import com.sun.jdi.* +import org.jetbrains.kotlin.idea.debugger.evaluate.LOG +import org.jetbrains.kotlin.idea.debugger.evaluate.VariableFinder.Companion.SUSPEND_LAMBDA_CLASSES +import org.jetbrains.kotlin.idea.debugger.evaluate.getInvokePolicy +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull + +class KotlinCoroutinesAsyncStackTraceProvider : AsyncStackTraceProvider { + private companion object { + const val DEBUG_METADATA_KT = "kotlin.coroutines.jvm.internal.DebugMetadataKt" + + fun classByName(name: String, frameProxy: StackFrameProxyImpl, suspendContext: SuspendContextImpl): ReferenceType? { + val virtualMachine = frameProxy.virtualMachine + val classClass = virtualMachine.classesByName(Class::class.java.name).firstIsInstanceOrNull() ?: return null + val forNameMethod = classClass.methodsByName("forName").first { it.signature() == "(Ljava/lang/String;)Ljava/lang/Class;" } + + try { + val threadReference = frameProxy.threadProxy().threadReference + val args = listOf(virtualMachine.mirrorOf(name)) + val result = classClass.invokeMethod(threadReference, forNameMethod, args, suspendContext.getInvokePolicy()) + + if (result is ClassObjectReference) { + return result.reflectedType() + } + } catch (e: InvocationException) { + // Ignore ClassNotFoundException + } catch (e: Throwable) { + LOG.error(e) + } + + return null + } + + tailrec fun findBaseContinuationSuperSupertype(type: ClassType): ClassType? { + if (type.name() == "kotlin.coroutines.jvm.internal.BaseContinuationImpl") { + return type + } + + return findBaseContinuationSuperSupertype(type.superclass() ?: return null) + } + } + + override fun getAsyncStackTrace(stackFrame: JavaStackFrame, suspendContext: SuspendContextImpl): List? { + return getAsyncStackTrace(stackFrame.stackFrameProxy, suspendContext) + } + + fun getAsyncStackTrace(frameProxy: StackFrameProxyImpl, suspendContext: SuspendContextImpl): List? { + val location = frameProxy.location() + val method = location.safeMethod() ?: return null + val debugMetadataKtType = classByName(DEBUG_METADATA_KT, frameProxy, suspendContext) as? ClassType ?: return null + + val context = Context(suspendContext, frameProxy, method, debugMetadataKtType) + return context.getAsyncStackTraceForSuspendLambda() ?: context.getAsyncStackTraceForSuspendFunction() + } + + private fun Context.getAsyncStackTraceForSuspendLambda(): List? { + if (method.name() != "invokeSuspend" || method.signature() != "(Ljava/lang/Object;)Ljava/lang/Object;") { + return null + } + + val thisObject = frameProxy.thisObject() ?: return null + val thisType = thisObject.referenceType() + + if (SUSPEND_LAMBDA_CLASSES.none { thisType.isSubtype(it) }) { + return null + } + + return collectFrames(thisObject) + } + + private fun Context.getAsyncStackTraceForSuspendFunction(): List? { + if ("Lkotlin/coroutines/Continuation;)" !in method.signature()) { + return null + } + + val continuationVariable = frameProxy.visibleVariableByName("\$continuation") ?: return null + val continuation = frameProxy.getValue(continuationVariable) as? ObjectReference ?: return null + + return collectFrames(continuation) + } + + private fun Context.collectFrames(continuation: ObjectReference): List? { + val frames = mutableListOf() + collectFramesRecursively(continuation, frames) + return frames + } + + private fun Context.collectFramesRecursively(continuation: ObjectReference, consumer: MutableList) { + val continuationType = continuation.referenceType() as? ClassType ?: return + val baseContinuationSupertype = findBaseContinuationSuperSupertype(continuationType) ?: return + + val location = getLocation(continuation) + val spilledVariables = getSpilledVariables(continuation) ?: emptyList() + + if (location != null) { + consumer += StackFrameItem(location, spilledVariables) + } + + val completionField = baseContinuationSupertype.fieldByName("completion") ?: return + val completion = continuation.getValue(completionField) as? ObjectReference ?: return + collectFramesRecursively(completion, consumer) + } + + private class Context( + val suspendContext: SuspendContextImpl, + val frameProxy: StackFrameProxyImpl, + val method: Method, + private val debugMetadataKtType: ClassType + ) { + val evaluationContext: EvaluationContextImpl by lazy { EvaluationContextImpl(suspendContext, frameProxy) } + + fun getLocation(continuation: ObjectReference): Location? { + val getStackTraceElementMethod = debugMetadataKtType.methodsByName( + "getStackTraceElement", + "(Lkotlin/coroutines/jvm/internal/BaseContinuationImpl;)Ljava/lang/StackTraceElement;" + ).firstOrNull() ?: return null + + val threadReference = frameProxy.threadProxy().threadReference.takeIf { it.isSuspended } ?: return null + val args = listOf(continuation) + val invokePolicy = suspendContext.getInvokePolicy() + + val stackTraceElement = debugMetadataKtType + .invokeMethod(threadReference, getStackTraceElementMethod, args, invokePolicy) as? ObjectReference ?: return null + + val stackTraceElementType = stackTraceElement.referenceType().takeIf { it.name() == StackTraceElement::class.java.name } + ?: return null + + fun getValue(name: String, desc: String): Value? { + val method = stackTraceElementType.methodsByName(name, desc).single() + return stackTraceElement.invokeMethod(threadReference, method, emptyList(), invokePolicy) + } + + val className = (getValue("getClassName", "()Ljava/lang/String;") as? StringReference)?.value() ?: return null + val methodName = (getValue("getMethodName", "()Ljava/lang/String;") as? StringReference)?.value() ?: return null + val lineNumber = (getValue("getLineNumber", "()I") as? IntegerValue)?.value()?.takeIf { it >= 0 } ?: return null + + val locationClass = classByName(className, frameProxy, suspendContext) ?: return null + return GeneratedLocation(suspendContext.debugProcess, locationClass, methodName, lineNumber) + } + + fun getSpilledVariables(continuation: ObjectReference): List? { + val getSpilledVariableFieldMappingMethod = debugMetadataKtType.methodsByName( + "getSpilledVariableFieldMapping", + "(Lkotlin/coroutines/jvm/internal/BaseContinuationImpl;)[Ljava/lang/String;" + ).firstOrNull() ?: return null + + val threadReference = frameProxy.threadProxy().threadReference.takeIf { it.isSuspended } ?: return null + val args = listOf(continuation) + val invokePolicy = suspendContext.getInvokePolicy() + + val rawSpilledVariables = debugMetadataKtType + .invokeMethod(threadReference, getSpilledVariableFieldMappingMethod, args, invokePolicy) as? ArrayReference ?: return null + + val length = rawSpilledVariables.length() / 2 + val spilledVariables = ArrayList(length) + + for (index in 0 until length) { + val fieldName = (rawSpilledVariables.getValue(2 * index) as? StringReference)?.value() ?: continue + val variableName = (rawSpilledVariables.getValue(2 * index + 1) as? StringReference)?.value() ?: continue + val field = continuation.referenceType().fieldByName(fieldName) ?: continue + + val project = suspendContext.debugProcess.project + + val valueDescriptor = object : ValueDescriptorImpl(project) { + override fun calcValueName() = variableName + override fun calcValue(evaluationContext: EvaluationContextImpl?) = continuation.getValue(field) + override fun getDescriptorEvaluation(context: DebuggerContext?) = + throw EvaluateException("Spilled variable evaluation is not supported") + } + + spilledVariables += JavaValue.create( + null, + valueDescriptor, + evaluationContext, + suspendContext.debugProcess.xdebugProcess!!.nodeManager, + false + ) + } + + return spilledVariables + } + } +} \ No newline at end of file diff --git a/idea/resources/META-INF/jvm.xml b/idea/resources/META-INF/jvm.xml index 41911ad62a9..e5d406d852e 100644 --- a/idea/resources/META-INF/jvm.xml +++ b/idea/resources/META-INF/jvm.xml @@ -90,6 +90,7 @@ + diff --git a/idea/resources/META-INF/jvm.xml.182 b/idea/resources/META-INF/jvm.xml.182 new file mode 100644 index 00000000000..c011c8b097a --- /dev/null +++ b/idea/resources/META-INF/jvm.xml.182 @@ -0,0 +1,200 @@ + + + + + + + + org.jetbrains.kotlin.idea.JvmPluginStartupComponent + + + + + + org.jetbrains.kotlin.idea.compiler.KotlinCompilerManager + + + org.jetbrains.kotlin.idea.configuration.ui.KotlinConfigurationCheckerComponent + + + org.jetbrains.kotlin.idea.scratch.ui.ScratchFileHook + + + org.jetbrains.kotlin.idea.scratch.ScratchFileModuleInfoProvider + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/idea/testData/debugger/tinyApp/src/asyncStackTrace/asyncFunctions.kt b/idea/testData/debugger/tinyApp/src/asyncStackTrace/asyncFunctions.kt new file mode 100644 index 00000000000..7a9fc16789e --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/asyncStackTrace/asyncFunctions.kt @@ -0,0 +1,24 @@ +package asyncFunctions + +suspend fun main() { + val a = 5 + val b = 7 + none() + foo() +} + +suspend fun foo() { + val x = "foo" + none() + bar() +} + +suspend fun bar() { + var y = "zoo" + none() + val z = "bar" + //Breakpoint! + val a = 5 +} + +suspend fun none() {} \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/asyncStackTrace/asyncFunctions.out b/idea/testData/debugger/tinyApp/src/asyncStackTrace/asyncFunctions.out new file mode 100644 index 00000000000..cb29027ccce --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/asyncStackTrace/asyncFunctions.out @@ -0,0 +1,15 @@ +LineBreakpoint created at asyncFunctions.kt:21 +Run Java +Connected to the target VM +asyncFunctions.kt:21 +Async stack trace: + asyncFunctions.AsyncFunctionsKt:18 + y = "zoo" + asyncFunctions.AsyncFunctionsKt:13 + x = "foo" + asyncFunctions.AsyncFunctionsKt:7 + a = 5 + b = 7 +Disconnected from the target VM + +Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/src/asyncStackTrace/asyncLambdas.kt b/idea/testData/debugger/tinyApp/src/asyncStackTrace/asyncLambdas.kt new file mode 100644 index 00000000000..83975fbaf52 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/asyncStackTrace/asyncLambdas.kt @@ -0,0 +1,20 @@ +package asyncLambdas + +suspend fun main() { + foo() +} + +val foo: suspend () -> Unit = { + val a = 5 + bar() +} + +val bar: suspend () -> Unit = { + val b = 3 + baz() +} + +val baz: suspend () -> Unit = { + //Breakpoint! + val a = 5 +} \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/asyncStackTrace/asyncLambdas.out b/idea/testData/debugger/tinyApp/src/asyncStackTrace/asyncLambdas.out new file mode 100644 index 00000000000..dd14a8a3f05 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/asyncStackTrace/asyncLambdas.out @@ -0,0 +1,12 @@ +LineBreakpoint created at asyncLambdas.kt:19 +Run Java +Connected to the target VM +asyncLambdas.kt:19 +Async stack trace: + asyncLambdas.AsyncLambdasKt$bar$1:14 + b = 3 + asyncLambdas.AsyncLambdasKt$foo$1:9 + a = 5 +Disconnected from the target VM + +Process finished with exit code 0 diff --git a/idea/testData/debugger/tinyApp/src/asyncStackTrace/asyncSimple.kt b/idea/testData/debugger/tinyApp/src/asyncStackTrace/asyncSimple.kt new file mode 100644 index 00000000000..1c119e865ea --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/asyncStackTrace/asyncSimple.kt @@ -0,0 +1,10 @@ +package asyncSimple + +suspend fun main() { + val a = 5 + foo() + //Breakpoint! + val b = a +} + +suspend fun foo() {} \ No newline at end of file diff --git a/idea/testData/debugger/tinyApp/src/asyncStackTrace/asyncSimple.out b/idea/testData/debugger/tinyApp/src/asyncStackTrace/asyncSimple.out new file mode 100644 index 00000000000..d9e28e64f61 --- /dev/null +++ b/idea/testData/debugger/tinyApp/src/asyncStackTrace/asyncSimple.out @@ -0,0 +1,10 @@ +LineBreakpoint created at asyncSimple.kt:7 +Run Java +Connected to the target VM +asyncSimple.kt:7 +Async stack trace: + asyncSimple.AsyncSimpleKt:5 + a = 5 +Disconnected from the target VM + +Process finished with exit code 0 diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/AbstractAsyncStackTraceTest.kt b/idea/tests/org/jetbrains/kotlin/idea/debugger/AbstractAsyncStackTraceTest.kt new file mode 100644 index 00000000000..e88c8e50879 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/AbstractAsyncStackTraceTest.kt @@ -0,0 +1,109 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.debugger + +import com.intellij.debugger.engine.JavaValue +import com.intellij.debugger.memory.utils.StackFrameItem +import com.intellij.execution.process.ProcessOutputTypes +import com.intellij.openapi.extensions.Extensions +import com.intellij.openapi.util.io.FileUtil +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull +import org.jetbrains.kotlin.utils.getSafe +import java.io.File +import java.io.PrintWriter +import java.io.StringWriter +import java.lang.reflect.Modifier + +abstract class AbstractAsyncStackTraceTest : KotlinDebuggerTestBase() { + private companion object { + const val MARGIN = " " + + // Absent in 182, should be AsyncStackTraceProvider.EP.name + const val ASYNC_STACKTRACE_EP_NAME = "com.intellij.debugger.asyncStackTraceProvider" + } + + protected fun doTest(path: String) { + val fileText = FileUtil.loadFile(File(path)) + + configureSettings(fileText) + createAdditionalBreakpoints(fileText) + createDebugProcess(path) + + val area = Extensions.getArea(null) + if (!area.hasExtensionPoint(ASYNC_STACKTRACE_EP_NAME)) { + System.err.println("$ASYNC_STACKTRACE_EP_NAME extension point is not found (probably old IDE version)") + finish() + return + } + + val extensionPoint = area.getExtensionPoint(ASYNC_STACKTRACE_EP_NAME) + val asyncStackTraceProvider = extensionPoint.extensions.firstIsInstanceOrNull() + ?: run { + System.err.println("Kotlin coroutine async stack trace provider is not found") + finish() + return + } + + doOnBreakpoint { + val frameProxy = this.frameProxy + if (frameProxy != null) { + try { + val stackTrace = asyncStackTraceProvider.getAsyncStackTrace(frameProxy, this) + if (stackTrace != null && stackTrace.isNotEmpty()) { + print(renderAsyncStackTrace(stackTrace), ProcessOutputTypes.SYSTEM) + } else { + println("No async stack trace available", ProcessOutputTypes.SYSTEM) + } + } catch (e: Throwable) { + val stackTrace = e.stackTraceAsString() + System.err.println("Exception occurred on calculating async stack traces: $stackTrace") + throw e + } + } else { + println("FrameProxy is 'null', can't calculate async stack trace", ProcessOutputTypes.SYSTEM) + } + + resume(this) + } + } + + private fun Throwable.stackTraceAsString(): String { + val writer = StringWriter() + printStackTrace(PrintWriter(writer)) + return writer.toString() + } + + private fun renderAsyncStackTrace(trace: List) = buildString { + appendln("Async stack trace:") + for (item in trace) { + append(MARGIN).appendln(item.toString()) + + @Suppress("UNCHECKED_CAST") + val variablesField = item.javaClass.declaredFields.first { !Modifier.isStatic(it.modifiers) && it.type == List::class.java } + val variables = variablesField.getSafe(item) as? List + if (variables != null) { + for (variable in variables) { + val descriptor = variable.descriptor + val name = descriptor.calcValueName() + val value = descriptor.calcValue(evaluationContext) + + append(MARGIN).append(MARGIN).append(name).append(" = ").appendln(value) + } + } + } + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/debugger/AsyncStackTraceTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/debugger/AsyncStackTraceTestGenerated.java new file mode 100644 index 00000000000..38ec9a499d2 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/debugger/AsyncStackTraceTestGenerated.java @@ -0,0 +1,46 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.debugger; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("idea/testData/debugger/tinyApp/src/asyncStackTrace") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class AsyncStackTraceTestGenerated extends AbstractAsyncStackTraceTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInAsyncStackTrace() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/debugger/tinyApp/src/asyncStackTrace"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("asyncFunctions.kt") + public void testAsyncFunctions() throws Exception { + runTest("idea/testData/debugger/tinyApp/src/asyncStackTrace/asyncFunctions.kt"); + } + + @TestMetadata("asyncLambdas.kt") + public void testAsyncLambdas() throws Exception { + runTest("idea/testData/debugger/tinyApp/src/asyncStackTrace/asyncLambdas.kt"); + } + + @TestMetadata("asyncSimple.kt") + public void testAsyncSimple() throws Exception { + runTest("idea/testData/debugger/tinyApp/src/asyncStackTrace/asyncSimple.kt"); + } +}