From e19ecdfb3d7598e6b1f99c4a58995a0a55e6479e Mon Sep 17 00:00:00 2001 From: pyos Date: Tue, 13 Oct 2020 10:54:13 +0200 Subject: [PATCH] [Tests] Suspend Codegen Tests Improvements This commit enables the execution of suspend box tests in a separate test. It's a QoL improvement in the existing bb tests but, motivating these changes, enables the new debugger stepping tests to step coroutine code. --- .../coroutines/spilling/booleanParameter.txt | 68 ------------------- .../spilling/booleanParameter_ir.txt | 68 ------------------- .../debug/localVariables/suspend/simple.kt | 10 +++ .../test/clientserver/TestProcessServer.kt | 37 +++++++--- .../jetbrains/kotlin/TestHelperGenerator.kt | 36 ++++------ .../codegen/AbstractBytecodeListingTest.kt | 8 ++- .../kotlin/codegen/CodegenTestCase.java | 3 +- .../IrLocalVariableTestGenerated.java | 20 ++++++ .../LocalVariableTestGenerated.java | 20 ++++++ 9 files changed, 98 insertions(+), 172 deletions(-) create mode 100644 compiler/testData/debug/localVariables/suspend/simple.kt diff --git a/compiler/testData/codegen/bytecodeListing/coroutines/spilling/booleanParameter.txt b/compiler/testData/codegen/bytecodeListing/coroutines/spilling/booleanParameter.txt index 97f10529900..98a92fb6a55 100644 --- a/compiler/testData/codegen/bytecodeListing/coroutines/spilling/booleanParameter.txt +++ b/compiler/testData/codegen/bytecodeListing/coroutines/spilling/booleanParameter.txt @@ -36,71 +36,3 @@ public final class BooleanParameterKt { public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void } - -@kotlin.Metadata -public abstract class helpers/ContinuationAdapter { - // source: 'CoroutineUtil.kt' - private final @org.jetbrains.annotations.NotNull field context: kotlin.coroutines.CoroutineContext - public method (): void - public @org.jetbrains.annotations.NotNull method getContext(): kotlin.coroutines.CoroutineContext - public abstract method resume(p0: java.lang.Object): void - public method resumeWith(@org.jetbrains.annotations.NotNull p0: java.lang.Object): void - public abstract method resumeWithException(@org.jetbrains.annotations.NotNull p0: java.lang.Throwable): void -} - -@kotlin.Metadata -public final class helpers/CoroutineUtilKt$handleExceptionContinuation$1 { - // source: 'CoroutineUtil.kt' - enclosing method helpers/CoroutineUtilKt.handleExceptionContinuation(Lkotlin/jvm/functions/Function1;)Lkotlin/coroutines/Continuation; - synthetic final field $x: kotlin.jvm.functions.Function1 - private final @org.jetbrains.annotations.NotNull field context: kotlin.coroutines.EmptyCoroutineContext - inner (anonymous) class helpers/CoroutineUtilKt$handleExceptionContinuation$1 - method (p0: kotlin.jvm.functions.Function1): void - public synthetic bridge method getContext(): kotlin.coroutines.CoroutineContext - public @org.jetbrains.annotations.NotNull method getContext(): kotlin.coroutines.EmptyCoroutineContext - public method resumeWith(@org.jetbrains.annotations.NotNull p0: java.lang.Object): void -} - -@kotlin.Metadata -public final class helpers/CoroutineUtilKt$handleResultContinuation$1 { - // source: 'CoroutineUtil.kt' - enclosing method helpers/CoroutineUtilKt.handleResultContinuation(Lkotlin/jvm/functions/Function1;)Lkotlin/coroutines/Continuation; - synthetic final field $x: kotlin.jvm.functions.Function1 - private final @org.jetbrains.annotations.NotNull field context: kotlin.coroutines.EmptyCoroutineContext - inner (anonymous) class helpers/CoroutineUtilKt$handleResultContinuation$1 - method (p0: kotlin.jvm.functions.Function1): void - public synthetic bridge method getContext(): kotlin.coroutines.CoroutineContext - public @org.jetbrains.annotations.NotNull method getContext(): kotlin.coroutines.EmptyCoroutineContext - public method resumeWith(@org.jetbrains.annotations.NotNull p0: java.lang.Object): void -} - -@kotlin.Metadata -public final class helpers/CoroutineUtilKt { - // source: 'CoroutineUtil.kt' - inner (anonymous) class helpers/CoroutineUtilKt$handleExceptionContinuation$1 - inner (anonymous) class helpers/CoroutineUtilKt$handleResultContinuation$1 - public final static @org.jetbrains.annotations.NotNull method handleExceptionContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.Continuation - public final static @org.jetbrains.annotations.NotNull method handleResultContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.Continuation -} - -@kotlin.Metadata -public final class helpers/EmptyContinuation$Companion { - // source: 'CoroutineUtil.kt' - private method (): void - public synthetic method (p0: kotlin.jvm.internal.DefaultConstructorMarker): void - public final inner class helpers/EmptyContinuation$Companion -} - -@kotlin.Metadata -public class helpers/EmptyContinuation { - // source: 'CoroutineUtil.kt' - public final static @org.jetbrains.annotations.NotNull field Companion: helpers.EmptyContinuation$Companion - private final @org.jetbrains.annotations.NotNull field context: kotlin.coroutines.CoroutineContext - static method (): void - public method (): void - public method (@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.CoroutineContext): void - public synthetic method (p0: kotlin.coroutines.CoroutineContext, p1: int, p2: kotlin.jvm.internal.DefaultConstructorMarker): void - public @org.jetbrains.annotations.NotNull method getContext(): kotlin.coroutines.CoroutineContext - public method resumeWith(@org.jetbrains.annotations.NotNull p0: java.lang.Object): void - public final inner class helpers/EmptyContinuation$Companion -} diff --git a/compiler/testData/codegen/bytecodeListing/coroutines/spilling/booleanParameter_ir.txt b/compiler/testData/codegen/bytecodeListing/coroutines/spilling/booleanParameter_ir.txt index 4316e698960..ddeea63203d 100644 --- a/compiler/testData/codegen/bytecodeListing/coroutines/spilling/booleanParameter_ir.txt +++ b/compiler/testData/codegen/bytecodeListing/coroutines/spilling/booleanParameter_ir.txt @@ -36,71 +36,3 @@ public final class BooleanParameterKt { public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void } - -@kotlin.Metadata -public abstract class helpers/ContinuationAdapter { - // source: 'CoroutineUtil.kt' - private final @org.jetbrains.annotations.NotNull field context: kotlin.coroutines.CoroutineContext - public method (): void - public @org.jetbrains.annotations.NotNull method getContext(): kotlin.coroutines.CoroutineContext - public abstract method resume(p0: java.lang.Object): void - public method resumeWith(@org.jetbrains.annotations.NotNull p0: java.lang.Object): void - public abstract method resumeWithException(@org.jetbrains.annotations.NotNull p0: java.lang.Throwable): void -} - -@kotlin.Metadata -public final class helpers/CoroutineUtilKt$handleExceptionContinuation$1 { - // source: 'CoroutineUtil.kt' - enclosing method helpers/CoroutineUtilKt.handleExceptionContinuation(Lkotlin/jvm/functions/Function1;)Lkotlin/coroutines/Continuation; - synthetic final field $x: kotlin.jvm.functions.Function1 - private final @org.jetbrains.annotations.NotNull field context: kotlin.coroutines.EmptyCoroutineContext - inner (anonymous) class helpers/CoroutineUtilKt$handleExceptionContinuation$1 - method (p0: kotlin.jvm.functions.Function1): void - public synthetic bridge method getContext(): kotlin.coroutines.CoroutineContext - public @org.jetbrains.annotations.NotNull method getContext(): kotlin.coroutines.EmptyCoroutineContext - public method resumeWith(@org.jetbrains.annotations.NotNull p0: java.lang.Object): void -} - -@kotlin.Metadata -public final class helpers/CoroutineUtilKt$handleResultContinuation$1 { - // source: 'CoroutineUtil.kt' - enclosing method helpers/CoroutineUtilKt.handleResultContinuation(Lkotlin/jvm/functions/Function1;)Lkotlin/coroutines/Continuation; - synthetic final field $x: kotlin.jvm.functions.Function1 - private final @org.jetbrains.annotations.NotNull field context: kotlin.coroutines.EmptyCoroutineContext - inner (anonymous) class helpers/CoroutineUtilKt$handleResultContinuation$1 - method (p0: kotlin.jvm.functions.Function1): void - public synthetic bridge method getContext(): kotlin.coroutines.CoroutineContext - public @org.jetbrains.annotations.NotNull method getContext(): kotlin.coroutines.EmptyCoroutineContext - public method resumeWith(@org.jetbrains.annotations.NotNull p0: java.lang.Object): void -} - -@kotlin.Metadata -public final class helpers/CoroutineUtilKt { - // source: 'CoroutineUtil.kt' - inner (anonymous) class helpers/CoroutineUtilKt$handleExceptionContinuation$1 - inner (anonymous) class helpers/CoroutineUtilKt$handleResultContinuation$1 - public final static @org.jetbrains.annotations.NotNull method handleExceptionContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.Continuation - public final static @org.jetbrains.annotations.NotNull method handleResultContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.Continuation -} - -@kotlin.Metadata -public final class helpers/EmptyContinuation$Companion { - // source: 'CoroutineUtil.kt' - private method (): void - public synthetic method (p0: kotlin.jvm.internal.DefaultConstructorMarker): void - public final inner class helpers/EmptyContinuation$Companion -} - -@kotlin.Metadata -public class helpers/EmptyContinuation { - // source: 'CoroutineUtil.kt' - public final static @org.jetbrains.annotations.NotNull field Companion: helpers.EmptyContinuation$Companion - private final @org.jetbrains.annotations.NotNull field context: kotlin.coroutines.CoroutineContext - static method (): void - public method (): void - public method (@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.CoroutineContext): void - public synthetic method (p0: kotlin.coroutines.CoroutineContext, p1: int, p2: kotlin.jvm.internal.DefaultConstructorMarker): void - public @org.jetbrains.annotations.NotNull method getContext(): kotlin.coroutines.CoroutineContext - public method resumeWith(@org.jetbrains.annotations.NotNull p0: java.lang.Object): void - public final inner class helpers/EmptyContinuation$Companion -} diff --git a/compiler/testData/debug/localVariables/suspend/simple.kt b/compiler/testData/debug/localVariables/suspend/simple.kt new file mode 100644 index 00000000000..272cf66b631 --- /dev/null +++ b/compiler/testData/debug/localVariables/suspend/simple.kt @@ -0,0 +1,10 @@ +// WITH_COROUTINES +// FILE: test.kt + +suspend fun box() { + var x = 1 +} + +// LOCAL VARIABLES +// TestKt:5: $completion:kotlin.coroutines.Continuation=helpers.ResultContinuation +// TestKt:6: $completion:kotlin.coroutines.Continuation=helpers.ResultContinuation, x:int=1:int \ No newline at end of file diff --git a/compiler/tests-common-jvm6/tests/org/jetbrains/kotlin/test/clientserver/TestProcessServer.kt b/compiler/tests-common-jvm6/tests/org/jetbrains/kotlin/test/clientserver/TestProcessServer.kt index 747a0cecd14..65e30610c21 100644 --- a/compiler/tests-common-jvm6/tests/org/jetbrains/kotlin/test/clientserver/TestProcessServer.kt +++ b/compiler/tests-common-jvm6/tests/org/jetbrains/kotlin/test/clientserver/TestProcessServer.kt @@ -28,22 +28,37 @@ import java.util.concurrent.Executors import java.util.concurrent.ScheduledFuture import java.util.concurrent.TimeUnit -fun getGeneratedClass(classLoader: ClassLoader, className: String): Class<*> { +private fun ClassLoader.loadClassOrNull(name: String): Class<*>? = try { - return classLoader.loadClass(className) + loadClass(name) + } catch (e: ClassNotFoundException) { + null } - catch (e: ClassNotFoundException) { - error("No class file was generated for: " + className) - } -} -fun getBoxMethodOrNull(aClass: Class<*>): Method? { +private fun Class<*>.getMethodOrNull(name: String, vararg parameterTypes: Class<*>): Method? = try { - return aClass.getMethod("box") + getMethod(name, *parameterTypes) + } catch (e: NoSuchMethodException) { + null } - catch (e: NoSuchMethodException) { - return null + +fun getGeneratedClass(classLoader: ClassLoader, className: String): Class<*> = + classLoader.loadClassOrNull(className) ?: error("No class file was generated for: $className") + +fun getBoxMethodOrNull(aClass: Class<*>): Method? = + aClass.getMethodOrNull("box") + ?: aClass.classLoader.loadClassOrNull("kotlin.coroutines.Continuation")?.let { aClass.getMethodOrNull("box", it) } + ?: aClass.classLoader.loadClassOrNull("kotlin.coroutines.experimental.Continuation")?.let { aClass.getMethodOrNull("box", it) } + +fun runBoxMethod(method: Method): String? { + if (method.parameterTypes.isEmpty()) { + return method.invoke(null) as? String } + val emptyContinuationClass = method.declaringClass.classLoader.loadClass("helpers.ResultContinuation") + val emptyContinuation = emptyContinuationClass.declaredConstructors.single().newInstance() + val result = method.invoke(null, emptyContinuation) + val resultAfterSuspend = emptyContinuationClass.getField("result").get(emptyContinuation) + return (resultAfterSuspend ?: result) as? String } //Use only JDK 1.6 compatible api @@ -149,7 +164,7 @@ private class ServerTest(val clientSocket: Socket, val suppressOutput: Boolean) fun executeTest(classLoader: ClassLoader): String { val clazz = getGeneratedClass(classLoader, className) - return getBoxMethodOrNull(clazz)!!.invoke(null) as String + return runBoxMethod(getBoxMethodOrNull(clazz)!!) as String } companion object { diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/TestHelperGenerator.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/TestHelperGenerator.kt index 7f9b7982256..32884eee355 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/TestHelperGenerator.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/TestHelperGenerator.kt @@ -16,35 +16,19 @@ fun createTextForCoroutineHelpers(isReleaseCoroutines: Boolean, checkStateMachin else StandardNames.COROUTINES_PACKAGE_FQ_NAME_EXPERIMENTAL.asString() - val emptyContinuationBody = + fun continuationBody(t: String, useResult: (String) -> String) = if (isReleaseCoroutines) """ - |override fun resumeWith(result: Result) { - | result.getOrThrow() + |override fun resumeWith(result: Result<$t>) { + | ${useResult("result.getOrThrow()")} |} """.trimMargin() else """ - |override fun resume(data: Any?) {} + |override fun resume(data: $t) { ${useResult("data")} } |override fun resumeWithException(exception: Throwable) { throw exception } """.trimMargin() - val handleResultContinuationBody = - if (isReleaseCoroutines) - """ - |override fun resumeWith(result: Result) { - | x(result.getOrThrow()) - |} - """.trimMargin() - else - """ - |override fun resumeWithException(exception: Throwable) { - | throw exception - |} - | - |override fun resume(data: T) = x(data) - """.trimMargin() - val handleExceptionContinuationBody = if (isReleaseCoroutines) """ @@ -167,10 +151,9 @@ fun createTextForCoroutineHelpers(isReleaseCoroutines: Boolean, checkStateMachin | |fun handleResultContinuation(x: (T) -> Unit): Continuation = object: Continuation { | override val context = EmptyCoroutineContext - | $handleResultContinuationBody + | ${continuationBody("T") { "x($it)" }} |} | - | |fun handleExceptionContinuation(x: (Throwable) -> Unit): Continuation = object: Continuation { | override val context = EmptyCoroutineContext | $handleExceptionContinuationBody @@ -178,7 +161,14 @@ fun createTextForCoroutineHelpers(isReleaseCoroutines: Boolean, checkStateMachin | |open class EmptyContinuation(override val context: CoroutineContext = EmptyCoroutineContext) : Continuation { | companion object : EmptyContinuation() - | $emptyContinuationBody + | ${continuationBody("Any?") { it }} + |} + | + |class ResultContinuation : Continuation { + | override val context = EmptyCoroutineContext + | ${continuationBody("Any?") { "this.result = $it" }} + | + | var result: Any? = null |} | |abstract class ContinuationAdapter : Continuation { diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractBytecodeListingTest.kt b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractBytecodeListingTest.kt index 10d5d6ee3d0..a5f74ca9ffd 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractBytecodeListingTest.kt +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractBytecodeListingTest.kt @@ -21,7 +21,13 @@ abstract class AbstractBytecodeListingTest : CodegenTestCase() { val actualTxt = BytecodeListingTextCollectingVisitor.getText( classFileFactory, withSignatures = isWithSignatures(wholeFile), - withAnnotations = isWithAnnotations(wholeFile) + withAnnotations = isWithAnnotations(wholeFile), + filter = object : BytecodeListingTextCollectingVisitor.Filter { + override fun shouldWriteClass(access: Int, name: String): Boolean = !name.startsWith("helpers/") + override fun shouldWriteMethod(access: Int, name: String, desc: String): Boolean = true + override fun shouldWriteField(access: Int, name: String, desc: String): Boolean = true + override fun shouldWriteInnerClass(name: String): Boolean = true + } ) val prefixes = when { diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/CodegenTestCase.java b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/CodegenTestCase.java index 77ba16e36e0..5e1fddd142e 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/CodegenTestCase.java +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/CodegenTestCase.java @@ -65,6 +65,7 @@ import static org.jetbrains.kotlin.codegen.TestUtilsKt.extractUrls; import static org.jetbrains.kotlin.test.KotlinTestUtils.getAnnotationsJar; import static org.jetbrains.kotlin.test.clientserver.TestProcessServerKt.getBoxMethodOrNull; import static org.jetbrains.kotlin.test.clientserver.TestProcessServerKt.getGeneratedClass; +import static org.jetbrains.kotlin.test.clientserver.TestProcessServerKt.runBoxMethod; public abstract class CodegenTestCase extends KotlinBaseTest { private static final String DEFAULT_TEST_FILE_NAME = "a_test"; @@ -679,7 +680,7 @@ public abstract class CodegenTestCase extends KotlinBaseTest