diff --git a/.idea/kotlinTestDataPluginTestDataPaths.xml b/.idea/kotlinTestDataPluginTestDataPaths.xml
index dfda4788d3c..2cdabc10bb9 100644
--- a/.idea/kotlinTestDataPluginTestDataPaths.xml
+++ b/.idea/kotlinTestDataPluginTestDataPaths.xml
@@ -18,6 +18,13 @@
+
+
+
+
+
+
+
diff --git a/buildSrc/src/main/kotlin/common-configuration.gradle.kts b/buildSrc/src/main/kotlin/common-configuration.gradle.kts
index ae0bfc79d3d..090d0292fa4 100644
--- a/buildSrc/src/main/kotlin/common-configuration.gradle.kts
+++ b/buildSrc/src/main/kotlin/common-configuration.gradle.kts
@@ -155,7 +155,9 @@ fun Project.configureKotlinCompilationOptions() {
// Requires serialization plugin
":wasm:wasm.ir",
// Uses multiplatform
- ":kotlin-stdlib-jvm-minimal-for-test"
+ ":kotlin-stdlib-jvm-minimal-for-test",
+ // Requires serialization plugin
+ ":js:js.tests",
)
// TODO: fix remaining warnings and remove this property.
diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirSteppingTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirSteppingTestGenerated.java
index 46aa4840735..16f6af770e7 100644
--- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirSteppingTestGenerated.java
+++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirSteppingTestGenerated.java
@@ -319,6 +319,12 @@ public class FirSteppingTestGenerated extends AbstractFirSteppingTest {
runTest("compiler/testData/debug/stepping/localProperty.kt");
}
+ @Test
+ @TestMetadata("multiModule.kt")
+ public void testMultiModule() throws Exception {
+ runTest("compiler/testData/debug/stepping/multiModule.kt");
+ }
+
@Test
@TestMetadata("multilineExpression.kt")
public void testMultilineExpression() throws Exception {
diff --git a/compiler/testData/debug/stepping/assertion.kt b/compiler/testData/debug/stepping/assertion.kt
index 9fbca68455c..ed6e2b5914d 100644
--- a/compiler/testData/debug/stepping/assertion.kt
+++ b/compiler/testData/debug/stepping/assertion.kt
@@ -1,3 +1,4 @@
+// IGNORE_BACKEND: JS_IR
// FILE: test.kt
public val MASSERTIONS_ENABLED: Boolean = true
@@ -30,17 +31,17 @@ fun box(): String {
}
// EXPECTATIONS
-// test.kt:24 box
-// test.kt:15 box
-// test.kt:16 box
-// test.kt:3 getMASSERTIONS_ENABLED
+// test.kt:25 box
// test.kt:16 box
// test.kt:17 box
-// test.kt:21 box
-// test.kt:25 box
-// test.kt:6 box
-// test.kt:3 getMASSERTIONS_ENABLED
-// test.kt:6 box
+// test.kt:4 getMASSERTIONS_ENABLED
+// test.kt:17 box
+// test.kt:18 box
+// test.kt:22 box
+// test.kt:26 box
// test.kt:7 box
-// test.kt:12 box
-// test.kt:29 box
+// test.kt:4 getMASSERTIONS_ENABLED
+// test.kt:7 box
+// test.kt:8 box
+// test.kt:13 box
+// test.kt:30 box
diff --git a/compiler/testData/debug/stepping/functionCallWithInlinedLambdaParam.kt b/compiler/testData/debug/stepping/functionCallWithInlinedLambdaParam.kt
index b88e09f2b3b..7093040ccc9 100644
--- a/compiler/testData/debug/stepping/functionCallWithInlinedLambdaParam.kt
+++ b/compiler/testData/debug/stepping/functionCallWithInlinedLambdaParam.kt
@@ -1,3 +1,4 @@
+// IGNORE_BACKEND: JS_IR
// FILE: test.kt
fun box() {
diff --git a/compiler/testData/debug/stepping/if.kt b/compiler/testData/debug/stepping/if.kt
index 5673b067ead..b8d2362b9fd 100644
--- a/compiler/testData/debug/stepping/if.kt
+++ b/compiler/testData/debug/stepping/if.kt
@@ -1,3 +1,4 @@
+// IGNORE_BACKEND: JS_IR
// FILE: test.kt
fun box(): Int {
if (
@@ -14,11 +15,11 @@ inline fun getB(): Int {
}
// EXPECTATIONS
-// test.kt:4 box
-// test.kt:13 box
// test.kt:5 box
-// test.kt:10 getA
-// test.kt:5 box
-// test.kt:7 box
-// test.kt:13 box
-// test.kt:7 box
+// test.kt:14 box
+// test.kt:6 box
+// test.kt:11 getA
+// test.kt:6 box
+// test.kt:8 box
+// test.kt:14 box
+// test.kt:8 box
diff --git a/compiler/testData/debug/stepping/inTheEndOfLambdaArgumentOfInlineCall.kt b/compiler/testData/debug/stepping/inTheEndOfLambdaArgumentOfInlineCall.kt
index c482f310249..de3c7af48f1 100644
--- a/compiler/testData/debug/stepping/inTheEndOfLambdaArgumentOfInlineCall.kt
+++ b/compiler/testData/debug/stepping/inTheEndOfLambdaArgumentOfInlineCall.kt
@@ -1,3 +1,4 @@
+// IGNORE_BACKEND: JS_IR
// FILE: test.kt
fun box() {
diff --git a/compiler/testData/debug/stepping/inlineSimpleCall.kt b/compiler/testData/debug/stepping/inlineSimpleCall.kt
index 96d1b46c153..fe3edd0c15b 100644
--- a/compiler/testData/debug/stepping/inlineSimpleCall.kt
+++ b/compiler/testData/debug/stepping/inlineSimpleCall.kt
@@ -1,3 +1,4 @@
+// IGNORE_BACKEND: JS_IR
// FILE: test.kt
inline fun inlineFun(s: () -> Unit) {
diff --git a/compiler/testData/debug/stepping/lambdaStepInlineWithDefaults.kt b/compiler/testData/debug/stepping/lambdaStepInlineWithDefaults.kt
index c94f68281df..2ca8ccb0cce 100644
--- a/compiler/testData/debug/stepping/lambdaStepInlineWithDefaults.kt
+++ b/compiler/testData/debug/stepping/lambdaStepInlineWithDefaults.kt
@@ -1,4 +1,4 @@
-
+// IGNORE_BACKEND: JS_IR
// FILE: test.kt
inline fun foo(stringMaker: () -> String = { "OK" }): String {
return stringMaker()
diff --git a/compiler/testData/debug/stepping/linenumberForOneParametersArgumentCall.kt b/compiler/testData/debug/stepping/linenumberForOneParametersArgumentCall.kt
index c6ce92c52c0..18db2f7f68f 100644
--- a/compiler/testData/debug/stepping/linenumberForOneParametersArgumentCall.kt
+++ b/compiler/testData/debug/stepping/linenumberForOneParametersArgumentCall.kt
@@ -1,3 +1,4 @@
+// IGNORE_BACKEND: JS_IR
// FILE: test.kt
fun box() {
@@ -12,11 +13,11 @@ inline fun lookAtMe(f: (String) -> Unit) {
}
// EXPECTATIONS
-// test.kt:4 box
-// test.kt:10 box
-// test.kt:11 box
// test.kt:5 box
-// test.kt:6 box
// test.kt:11 box
// test.kt:12 box
-// test.kt:7 box
\ No newline at end of file
+// test.kt:6 box
+// test.kt:7 box
+// test.kt:12 box
+// test.kt:13 box
+// test.kt:8 box
diff --git a/compiler/testData/debug/stepping/localFunction.kt b/compiler/testData/debug/stepping/localFunction.kt
index 69c17b35a51..2bd1bb937a8 100644
--- a/compiler/testData/debug/stepping/localFunction.kt
+++ b/compiler/testData/debug/stepping/localFunction.kt
@@ -1,3 +1,4 @@
+// IGNORE_BACKEND: JS_IR
// FILE: test.kt
fun box() {
diff --git a/compiler/testData/debug/stepping/localFunctionWIthOnelineExpressionBody.kt b/compiler/testData/debug/stepping/localFunctionWIthOnelineExpressionBody.kt
index 7be976c26ad..b934f39cc06 100644
--- a/compiler/testData/debug/stepping/localFunctionWIthOnelineExpressionBody.kt
+++ b/compiler/testData/debug/stepping/localFunctionWIthOnelineExpressionBody.kt
@@ -1,3 +1,4 @@
+// IGNORE_BACKEND: JS_IR
// FILE: test.kt
fun box() {
@@ -9,17 +10,17 @@ fun box() {
}
// EXPECTATIONS
-// test.kt:4 box
-// EXPECTATIONS JVM
// test.kt:5 box
-// EXPECTATIONS
-// test.kt:6 box
-// test.kt:7 box
// EXPECTATIONS JVM
-// test.kt:5 invoke
-// EXPECTATIONS JVM_IR
-// test.kt:5 box$bar
+// test.kt:6 box
// EXPECTATIONS
// test.kt:7 box
// test.kt:8 box
-// test.kt:9 box
\ No newline at end of file
+// EXPECTATIONS JVM
+// test.kt:6 invoke
+// EXPECTATIONS JVM_IR
+// test.kt:6 box$bar
+// EXPECTATIONS
+// test.kt:8 box
+// test.kt:9 box
+// test.kt:10 box
diff --git a/compiler/testData/debug/stepping/multiModule.kt b/compiler/testData/debug/stepping/multiModule.kt
new file mode 100644
index 00000000000..cd8d6ad14b5
--- /dev/null
+++ b/compiler/testData/debug/stepping/multiModule.kt
@@ -0,0 +1,25 @@
+// MODULE: lib
+// FILE: a.kt
+
+fun a() = "a"
+
+// FILE: b.kt
+
+fun b() = "b"
+
+// MODULE: main(lib)
+// FILE: test.kt
+
+fun box() {
+ a()
+ b()
+}
+
+// EXPECTATIONS
+// test.kt:14 box
+// a.kt:4 a
+// test.kt:14 box
+// test.kt:15 box
+// b.kt:8 b
+// test.kt:15 box
+// test.kt:16 box
diff --git a/compiler/testData/debug/stepping/noParametersArgumentCallInExpression.kt b/compiler/testData/debug/stepping/noParametersArgumentCallInExpression.kt
index dbe665cdc16..13d2d9939ef 100644
--- a/compiler/testData/debug/stepping/noParametersArgumentCallInExpression.kt
+++ b/compiler/testData/debug/stepping/noParametersArgumentCallInExpression.kt
@@ -1,3 +1,4 @@
+// IGNORE_BACKEND: JS_IR
// FILE: test.kt
fun box() {
diff --git a/compiler/testData/debug/stepping/simpleDefaultArgWithInline.kt b/compiler/testData/debug/stepping/simpleDefaultArgWithInline.kt
index a599b04f45b..07cf74c1522 100644
--- a/compiler/testData/debug/stepping/simpleDefaultArgWithInline.kt
+++ b/compiler/testData/debug/stepping/simpleDefaultArgWithInline.kt
@@ -1,3 +1,4 @@
+// IGNORE_BACKEND: JS_IR
// FILE: test.kt
inline fun ifoo(ok: String = "OK"): String {
@@ -15,11 +16,11 @@ fun box(): String {
// FORCE_STEP_INTO
// EXPECTATIONS
-// test.kt:12 box
-// test.kt:3 box
-// test.kt:4 box
// test.kt:13 box
-// test.kt:7 ifoo2$default (synthetic)
-// test.kt:8 ifoo2
-// test.kt:7 ifoo2$default (synthetic)
-// test.kt:13 box
\ No newline at end of file
+// test.kt:4 box
+// test.kt:5 box
+// test.kt:14 box
+// test.kt:8 ifoo2$default (synthetic)
+// test.kt:9 ifoo2
+// test.kt:8 ifoo2$default (synthetic)
+// test.kt:14 box
diff --git a/compiler/testData/debug/stepping/simpleSmap.kt b/compiler/testData/debug/stepping/simpleSmap.kt
index 3f7d151c408..c28d890bc24 100644
--- a/compiler/testData/debug/stepping/simpleSmap.kt
+++ b/compiler/testData/debug/stepping/simpleSmap.kt
@@ -1,3 +1,5 @@
+// IGNORE_BACKEND: JS_IR
+
// FILE: test.kt
inline fun inlineFun(s: () -> Unit) {
@@ -11,10 +13,10 @@ fun box() {
}
// EXPECTATIONS
-// test.kt:8 box
-// test.kt:4 box
-// test.kt:9 box
// test.kt:10 box
-// test.kt:4 box
-// test.kt:5 box
+// test.kt:6 box
// test.kt:11 box
+// test.kt:12 box
+// test.kt:6 box
+// test.kt:7 box
+// test.kt:13 box
diff --git a/compiler/testData/debug/stepping/suspendFunWithSuspendLambdaParameter.kt b/compiler/testData/debug/stepping/suspendFunWithSuspendLambdaParameter.kt
index a66e2f80001..2c176c557d8 100644
--- a/compiler/testData/debug/stepping/suspendFunWithSuspendLambdaParameter.kt
+++ b/compiler/testData/debug/stepping/suspendFunWithSuspendLambdaParameter.kt
@@ -1,3 +1,4 @@
+// IGNORE_BACKEND: JS_IR
// WITH_STDLIB
// FILE: test.kt
suspend fun foo(block: suspend Long.() -> String): String {
diff --git a/compiler/testData/debug/stepping/tryCatchFinally.kt b/compiler/testData/debug/stepping/tryCatchFinally.kt
index 6d481693b35..5fd8bcf8fad 100644
--- a/compiler/testData/debug/stepping/tryCatchFinally.kt
+++ b/compiler/testData/debug/stepping/tryCatchFinally.kt
@@ -1,3 +1,4 @@
+// IGNORE_BACKEND: JS_IR
// FILE: test.kt
fun foo() {
@@ -89,4 +90,4 @@ fun box() {
// test.kt:18 foo
// test.kt:12 foo
// test.kt:19 foo
-// test.kt:38 box
\ No newline at end of file
+// test.kt:38 box
diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrSteppingTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrSteppingTestGenerated.java
index 1774aeb42ce..c9aea3ab2c1 100644
--- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrSteppingTestGenerated.java
+++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrSteppingTestGenerated.java
@@ -319,6 +319,12 @@ public class IrSteppingTestGenerated extends AbstractIrSteppingTest {
runTest("compiler/testData/debug/stepping/localProperty.kt");
}
+ @Test
+ @TestMetadata("multiModule.kt")
+ public void testMultiModule() throws Exception {
+ runTest("compiler/testData/debug/stepping/multiModule.kt");
+ }
+
@Test
@TestMetadata("multilineExpression.kt")
public void testMultilineExpression() throws Exception {
diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/SteppingTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/SteppingTestGenerated.java
index 976666dc02e..b80c29af270 100644
--- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/SteppingTestGenerated.java
+++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/SteppingTestGenerated.java
@@ -319,6 +319,12 @@ public class SteppingTestGenerated extends AbstractSteppingTest {
runTest("compiler/testData/debug/stepping/localProperty.kt");
}
+ @Test
+ @TestMetadata("multiModule.kt")
+ public void testMultiModule() throws Exception {
+ runTest("compiler/testData/debug/stepping/multiModule.kt");
+ }
+
@Test
@TestMetadata("multilineExpression.kt")
public void testMultilineExpression() throws Exception {
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index b007f2ab5f3..69087c29f6d 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -3298,6 +3298,11 @@
+
+
+
+
+
@@ -3308,6 +3313,11 @@
+
+
+
+
+
@@ -3344,6 +3354,11 @@
+
+
+
+
+
@@ -3376,6 +3391,11 @@
+
+
+
+
+
@@ -3414,6 +3434,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -3436,6 +3476,11 @@
+
+
+
+
+
@@ -3458,6 +3503,11 @@
+
+
+
+
+
@@ -3484,6 +3534,11 @@
+
+
+
+
+
@@ -3530,6 +3585,11 @@
+
+
+
+
+
@@ -3556,6 +3616,11 @@
+
+
+
+
+
@@ -3572,6 +3637,11 @@
+
+
+
+
+
@@ -3618,6 +3688,11 @@
+
+
+
+
+
@@ -3634,6 +3709,11 @@
+
+
+
+
+
@@ -3670,6 +3750,11 @@
+
+
+
+
+
@@ -3680,6 +3765,11 @@
+
+
+
+
+
@@ -3694,6 +3784,16 @@
+
+
+
+
+
+
+
+
+
+
@@ -3774,6 +3874,11 @@
+
+
+
+
+
@@ -3816,6 +3921,11 @@
+
+
+
+
+
@@ -3830,12 +3940,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -7797,6 +7927,11 @@
+
+
+
+
+
@@ -7841,6 +7976,11 @@
+
+
+
+
+
@@ -7939,6 +8079,16 @@
+
+
+
+
+
+
+
+
+
+
@@ -8093,6 +8243,11 @@
+
+
+
+
+
@@ -8133,6 +8288,11 @@
+
+
+
+
+
@@ -8191,6 +8351,11 @@
+
+
+
+
+
@@ -8217,6 +8382,11 @@
+
+
+
+
+
diff --git a/gradle/versions.properties b/gradle/versions.properties
index eaa26878dbf..ef47994ea3b 100644
--- a/gradle/versions.properties
+++ b/gradle/versions.properties
@@ -43,14 +43,18 @@ versions.kotlinx-collections-immutable=0.3.1
versions.kotlinx-coroutines-core-jvm=1.5.0
versions.kotlinx-coroutines-core=1.5.0
versions.kotlinx-metadata-jvm=0.5.0
+versions.kotlinx-serialization-json=1.3.3
versions.ktor-network=1.0.1
versions.ktor-server-test-host=1.1.5
versions.ktor-server-core=1.6.7
versions.ktor-server-netty=1.6.7
versions.ktor-client-mock=1.6.7
+versions.ktor-client-core=2.0.2
+versions.ktor-client-cio=2.0.2
+versions.ktor-client-websockets=2.0.2
versions.native-platform=0.14
versions.protobuf=2.6.1
versions.r8=2.2.64
versions.robolectric=4.0
versions.nodejs=16.14.2
-versions.v8=10.2.9
\ No newline at end of file
+versions.v8=10.2.9
diff --git a/js/js.parser/src/org/jetbrains/kotlin/js/parser/sourcemaps/SourceMap.kt b/js/js.parser/src/org/jetbrains/kotlin/js/parser/sourcemaps/SourceMap.kt
index 33ac094893c..6ecedbf14da 100644
--- a/js/js.parser/src/org/jetbrains/kotlin/js/parser/sourcemaps/SourceMap.kt
+++ b/js/js.parser/src/org/jetbrains/kotlin/js/parser/sourcemaps/SourceMap.kt
@@ -21,6 +21,9 @@ import java.io.*
class SourceMap(val sourceContentResolver: (String) -> Reader?) {
val groups = mutableListOf()
+ @Suppress("UNUSED") // For use in the debugger
+ fun debugToString() = ByteArrayOutputStream().also { debug(PrintStream(it)) }.toString()
+
fun debug(writer: PrintStream = System.out) {
for ((index, group) in groups.withIndex()) {
writer.print("${index + 1}:")
diff --git a/js/js.tests/build.gradle.kts b/js/js.tests/build.gradle.kts
index f25e5d5fe91..592af9075f4 100644
--- a/js/js.tests/build.gradle.kts
+++ b/js/js.tests/build.gradle.kts
@@ -8,6 +8,7 @@ import org.jetbrains.kotlin.ideaExt.idea
plugins {
kotlin("jvm")
+ kotlin("plugin.serialization")
id("jps-compatible")
id("com.github.node-gradle.node") version "3.2.1"
id("de.undercouch.download")
@@ -78,6 +79,11 @@ dependencies {
antLauncherJar(toolsJar())
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:${commonDependencyVersion("org.junit", "junit-bom")}")
+
+ testImplementation(commonDependency("org.jetbrains.kotlinx", "kotlinx-serialization-json"))
+ testImplementation(commonDependency("io.ktor", "ktor-client-core"))
+ testImplementation(commonDependency("io.ktor", "ktor-client-cio"))
+ testImplementation(commonDependency("io.ktor", "ktor-client-websockets"))
}
val generationRoot = projectDir.resolve("tests-gen")
@@ -177,6 +183,18 @@ val generateTypeScriptTests = sequential(
.map { generateTypeScriptTestFor(it.name) }
)
+fun Test.setupNodeJs() {
+ systemProperty("javascript.engine.path.NodeJs", com.github.gradle.node.variant.VariantComputer()
+ .let { variantComputer ->
+ variantComputer
+ .computeNodeDir(node)
+ .let { variantComputer.computeNodeBinDir(it) }
+ .let { variantComputer.computeNodeExec(node, it) }
+ .get()
+ }
+ )
+}
+
fun Test.setupSpiderMonkey() {
dependsOn(unzipJsShell)
val jsShellExecutablePath = File(unzipJsShell.get().destinationDir, "js").absolutePath
@@ -195,6 +213,8 @@ fun Test.setupV8() {
fun Test.setUpJsBoxTests(jsEnabled: Boolean, jsIrEnabled: Boolean) {
setupV8()
+ if (jsIrEnabled)
+ setupNodeJs()
inputs.files(rootDir.resolve("js/js.engines/src/org/jetbrains/kotlin/js/engine/repl.js"))
diff --git a/js/js.tests/test/org/jetbrains/kotlin/generators/tests/GenerateJsTests.kt b/js/js.tests/test/org/jetbrains/kotlin/generators/tests/GenerateJsTests.kt
index 5658c9b6f12..eaf6212af17 100644
--- a/js/js.tests/test/org/jetbrains/kotlin/generators/tests/GenerateJsTests.kt
+++ b/js/js.tests/test/org/jetbrains/kotlin/generators/tests/GenerateJsTests.kt
@@ -149,6 +149,10 @@ fun main(args: Array) {
model("codegen/boxWasmJsInterop")
}
+ testClass {
+ model("debug/stepping")
+ }
+
testClass(
suiteTestClassName = "Fir2IrJsTextTestGenerated"
) {
diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/AbstractJsBlackBoxCodegenTestBase.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/AbstractJsBlackBoxCodegenTestBase.kt
index ea8e851e676..e8a1a802b08 100644
--- a/js/js.tests/test/org/jetbrains/kotlin/js/test/AbstractJsBlackBoxCodegenTestBase.kt
+++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/AbstractJsBlackBoxCodegenTestBase.kt
@@ -41,6 +41,18 @@ abstract class AbstractJsBlackBoxCodegenTestBase>
override fun TestConfigurationBuilder.configuration() {
+ commonConfigurationForJsBlackBoxCodegenTest()
+ jsArtifactsHandlersStep {
+ useHandlers(
+ ::NodeJsGeneratorHandler,
+ ::JsBoxRunner,
+ ::JsMinifierRunner,
+ ::JsAstHandler
+ )
+ }
+ }
+
+ protected fun TestConfigurationBuilder.commonConfigurationForJsBlackBoxCodegenTest() {
globalDefaults {
frontend = targetFrontend
targetPlatform = JsPlatforms.defaultJsPlatform
@@ -100,13 +112,7 @@ abstract class AbstractJsBlackBoxCodegenTestBase CDPMethodCallEncodingInfo
+): CDPResponse {
+ val jsonElement = json.parseToJsonElement(message)
+ return when (val id = jsonElement.jsonObject["id"]?.jsonPrimitive?.int) {
+ null -> CDPResponse.Event(decodeCDPEvent(jsonElement))
+ else -> {
+ val serializer = when (val encodingInfo = serializerForMessageId(id)) {
+ is CDPMethodCallEncodingInfoImpl -> encodingInfo.serializer
+ is CDPMethodCallEncodingInfoPlainText -> null
+ }
+ val result = jsonElement.jsonObject["result"]
+ val error = jsonElement.jsonObject["error"]
+ if (result != null) {
+ CDPResponse.MethodInvocationResult(
+ id,
+ serializer?.let { json.decodeFromJsonElement(it, result) } ?: CDPMethodInvocationResultPlainText(result.toString())
+ )
+ } else if (error != null) {
+ CDPResponse.Error(id, json.decodeFromJsonElement(error))
+ } else {
+ error("Missing 'result' or 'error' properties in JSON")
+ }
+ }
+ }
+}
+
+/**
+ * An opaque object that contains information about how to decode a method invocation result.
+ *
+ * Depending on the method, the type of the result may be different. So, when invoking a method,
+ * we save the information about how to decode its result, so that when the result is received,
+ * we could decode a value of the correct type.
+ */
+sealed interface CDPMethodCallEncodingInfo
+
+private class CDPMethodCallEncodingInfoImpl(
+ val serializer: DeserializationStrategy
+) : CDPMethodCallEncodingInfo
+
+/**
+ * Indicates that the result of the method call does not need any deserialization.
+ */
+object CDPMethodCallEncodingInfoPlainText : CDPMethodCallEncodingInfo
+
+/**
+ * Returns the JSON message for invoking the method, as well as the information about how to decode its result.
+ */
+private inline fun encodeCDPMethodCall(
+ messageId: Int,
+ methodName: String,
+ params: Params?
+): Pair {
+ val request = CDPRequest(messageId, methodName, params)
+ return json.encodeToString(request) to
+ CDPMethodCallEncodingInfoImpl(json.serializersModule.serializer())
+}
+
+/**
+ * A superclass for each kind of CDP method invocation result.
+ */
+@Serializable
+sealed class CDPMethodInvocationResult
+
+/**
+ * A special kind of method invocation result that indicates that the response contents should be discarded.
+ */
+@Serializable
+object CDPMethodInvocationResultUnit : CDPMethodInvocationResult()
+
+/**
+ * A special kind of method invocation result that indicates that the response contents should not be decoded from JSON,
+ * and instead should be returned as-is.
+ */
+class CDPMethodInvocationResultPlainText(val string: String) : CDPMethodInvocationResult()
+
+/**
+ * A superclass for each kind of CDP method invocation parameters.
+ */
+@Serializable
+sealed class CDPRequestParams
+
+/**
+ * A special kind of method invocation parameters that indicates that the method accepts no parameters.
+ */
+@Serializable
+internal object CDPRequestParamsUnit : CDPRequestParams()
+
+/**
+ * A representation of a [Chrome DevTools protocol](https://chromedevtools.github.io/devtools-protocol/) request.
+ */
+@Serializable
+class CDPRequest(
+ val id: Int,
+ val method: String,
+ val params: Params? = null
+)
+
+/**
+ * A representation of a [Chrome DevTools protocol](https://chromedevtools.github.io/devtools-protocol/) error.
+ */
+@Serializable
+class CDPError private constructor(val code: Int, val message: String? = null)
+
+/**
+ * A superclass for each kind of CDP event.
+ */
+@Serializable
+sealed class CDPEvent
+
+/**
+ * When we encounter a CDP event whose name we don't know, we return an instance of this class.
+ */
+class UnknownCDPEvent(val name: String) : CDPEvent()
+
+private fun decodeCDPEvent(element: JsonElement): CDPEvent {
+ val method = element.jsonObject["method"]!!.jsonPrimitive.content
+ val params = element.jsonObject["params"] ?: error("missing params")
+ return when (method) {
+ "Debugger.breakpointResolved" -> json.decodeFromJsonElement(Debugger.Event.BreakpointResolved.serializer(), params)
+ "Debugger.paused" -> json.decodeFromJsonElement(Debugger.Event.Paused.serializer(), params)
+ "Debugger.resumed" -> json.decodeFromJsonElement(Debugger.Event.Resumed.serializer(), params)
+ "Debugger.scriptFailedToParse" -> json.decodeFromJsonElement(Debugger.Event.ScriptFailedToParse.serializer(), params)
+ "Debugger.scriptParsed" -> json.decodeFromJsonElement(Debugger.Event.ScriptParsed.serializer(), params)
+ "Runtime.executionContextCreated" -> json.decodeFromJsonElement(Runtime.Event.ExecutionContextCreated.serializer(), params)
+ "Runtime.executionContextDestroyed" -> json.decodeFromJsonElement(Runtime.Event.ExecutionContextDestroyed.serializer(), params)
+ else -> UnknownCDPEvent(method)
+ }
+}
+
+/**
+ * Something capable of invoking a [Chrome DevTools protocol](https://chromedevtools.github.io/devtools-protocol/) method and returning
+ * the result of the invocation.
+ */
+interface CDPRequestEvaluator {
+
+ /**
+ * @param encodeMethodCallWithMessageId passed a message id, expected to return the JSON-encoded CDP message
+ * and some information about how to decode the response.
+ */
+ suspend fun genericEvaluateRequest(
+ encodeMethodCallWithMessageId: (Int) -> Pair
+ ): CDPMethodInvocationResult
+}
+
+private suspend inline fun CDPRequestEvaluator.evaluateRequest(
+ noinline body: (Int) -> Pair
+) = genericEvaluateRequest(body) as T
+
+/**
+ * The [`Runtime` domain](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/) of Chrome DevTools protocol.
+ */
+class Runtime(private val requestEvaluator: CDPRequestEvaluator) {
+
+ /**
+ * Enables reporting of execution contexts creation by means of [Runtime.Event.ExecutionContextCreated] event.
+ * When the reporting gets enabled the event will be sent immediately for each existing execution context.
+ *
+ * See [Runtime.enable](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-enable)
+ */
+ suspend fun enable() {
+ requestEvaluator.evaluateRequest { messageId ->
+ encodeCDPMethodCall(messageId, "Runtime.enable", null)
+ }
+ }
+
+ /**
+ * Tells inspected instance to run if it was waiting for debugger to attach.
+ *
+ * See [Runtime.runIfWaitingForDebugger](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-runIfWaitingForDebugger)
+ */
+ suspend fun runIfWaitingForDebugger() {
+ requestEvaluator.evaluateRequest { messageId ->
+ encodeCDPMethodCall(messageId, "Runtime.runIfWaitingForDebugger", null)
+ }
+ }
+
+ @Serializable
+ class EvaluationResult private constructor(
+ /**
+ * Evaluation result.
+ */
+ val result: RemoteObject,
+ /**
+ * Exception details.
+ */
+ val exceptionDetails: ExceptionDetails? = null
+ ) : CDPMethodInvocationResult()
+
+ @Serializable
+ class EvaluateRequestParams(val expression: String, val contextId: ExecutionContextId? = null) : CDPRequestParams()
+
+ /**
+ * Evaluates expression on global object.
+ *
+ * See [Runtime.evaluate](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-evaluate)
+ *
+ * @param expression Expression to evaluate.
+ * @param contextId Specifies in which execution context to perform evaluation.
+ * If the parameter is omitted the evaluation will be performed in the context of the inspected page.
+ */
+ suspend fun evaluate(expression: String, contextId: ExecutionContextId? = null) =
+ requestEvaluator.evaluateRequest { messageId ->
+ encodeCDPMethodCall(
+ messageId,
+ "Runtime.evaluate",
+ EvaluateRequestParams(expression, contextId)
+ )
+ }
+
+ /**
+ * Unique script identifier.
+ *
+ * See [Runtime.ScriptId](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-ScriptId)
+ */
+ @Serializable
+ @JvmInline
+ value class ScriptId(val value: String)
+
+ /**
+ * Id of an execution context.
+ *
+ * See [Runtime.ExecutionContextId](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-ExecutionContextId)
+ */
+ @Serializable
+ @JvmInline
+ value class ExecutionContextId(val value: Int)
+
+ /**
+ * Unique object identifier.
+ *
+ * See [Runtime.RemoteObjectId](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-RemoteObjectId)
+ */
+ @Serializable
+ @JvmInline
+ value class RemoteObjectId(val value: String)
+
+ /**
+ * Object type.
+ */
+ @Serializable
+ enum class ValueType {
+ @SerialName("object")
+ OBJECT,
+
+ @SerialName("function")
+ FUNCTION,
+
+ @SerialName("undefined")
+ UNDEFINED,
+
+ @SerialName("string")
+ STRING,
+
+ @SerialName("number")
+ NUMBER,
+
+ @SerialName("boolean")
+ BOOLEAN,
+
+ @SerialName("symbol")
+ SYMBOL,
+
+ @SerialName("bigint")
+ BIGINT,
+ }
+
+ /**
+ * Object subtype hint.
+ */
+ @Serializable
+ enum class ObjectSubtype {
+ @SerialName("array")
+ ARRAY,
+
+ @SerialName("null")
+ NULL,
+
+ @SerialName("node")
+ NODE,
+
+ @SerialName("regexp")
+ REGEXP,
+
+ @SerialName("date")
+ DATE,
+
+ @SerialName("map")
+ MAP,
+
+ @SerialName("set")
+ SET,
+
+ @SerialName("weakmap")
+ WEAKMAP,
+
+ @SerialName("weakset")
+ WEAKSET,
+
+ @SerialName("iterator")
+ ITERATOR,
+
+ @SerialName("generator")
+ GENERATOR,
+
+ @SerialName("error")
+ ERROR,
+
+ @SerialName("proxy")
+ PROXY,
+
+ @SerialName("promise")
+ PROMISE,
+
+ @SerialName("typedarray")
+ TYPEDARRAY,
+
+ @SerialName("arraybuffer")
+ ARRAYBUFFER,
+
+ @SerialName("dataview")
+ DATAVIEW,
+
+ @SerialName("webassemblymemory")
+ WEBASSEMBLYMEMORY,
+
+ @SerialName("wasmvalue")
+ WASMVALUE
+ }
+
+ /**
+ * Mirror object referencing original JavaScript object.
+ *
+ * See [Runtime.RemoteObject](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-RemoteObject)
+ */
+ @Serializable
+ class RemoteObject private constructor(
+ /**
+ * Object type.
+ */
+ val type: ValueType,
+ /**
+ * Object subtype hint. Specified for [ValueType.OBJECT] type values only.
+ */
+ val subtype: ObjectSubtype? = null,
+ /**
+ * Object class (constructor) name. Specified for [ValueType.OBJECT] type values only.
+ */
+ val className: String? = null,
+ /**
+ * String representation of the object.
+ */
+ val description: String? = null,
+ /**
+ * Unique object identifier (for non-primitive values).
+ */
+ val objectId: RemoteObjectId? = null
+ )
+
+ /**
+ * Stack entry for runtime errors and assertions.
+ *
+ * See [Runtime.CallFrame](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-CallFrame)
+ */
+ @Serializable
+ class CallFrame private constructor(
+ /**
+ * JavaScript function name.
+ */
+ val functionName: String,
+ /**
+ * JavaScript script id.
+ */
+ val scriptId: ScriptId,
+ /**
+ * JavaScript script name or url.
+ */
+ val url: String,
+ /**
+ * JavaScript script line number (0-based).
+ */
+ val lineNumber: Int,
+ /**
+ * JavaScript script column number (0-based).
+ */
+ val columnNumber: Int
+ )
+
+ /**
+ * Call frames for assertions or error messages.
+ *
+ * See [Runtime.StackTrace](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-StackTrace)
+ */
+ @Serializable
+ class StackTrace private constructor(
+ /**
+ * String label of this stack trace. For async traces this may be a name of the function that initiated the async call.
+ */
+ val description: String? = null,
+ /**
+ * A list of call frames in the stack trace.
+ */
+ val callFrames: List,
+
+ /**
+ * Asynchronous JavaScript stack trace that preceded this stack, if available.
+ */
+ val parent: StackTrace? = null
+ )
+
+ /**
+ * Detailed information about exception (or error) that was thrown during script compilation or execution.
+ *
+ * See [Runtime.ExceptionDetails](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-ExceptionDetails)
+ */
+ @Serializable
+ class ExceptionDetails private constructor(
+ /**
+ * Exception id.
+ */
+ val exceptionId: Int,
+ /**
+ * Exception text, which should be used together with exception object when available.
+ */
+ val text: String,
+ /**
+ * Line number of the exception location (0-based).
+ */
+ val lineNumber: Int,
+ /**
+ * Column number of the exception location (0-based).
+ */
+ val columnNumber: Int,
+ /**
+ * Script ID of the exception location.
+ */
+ val scriptId: ScriptId? = null,
+ /**
+ * URL of the exception location, to be used when the script was not reported.
+ */
+ val url: String? = null,
+ /**
+ * JavaScript stack trace if available.
+ */
+ val stackTrace: StackTrace? = null,
+ /**
+ * Exception object if available.
+ */
+ val exception: RemoteObject? = null,
+ /**
+ * Identifier of the context where exception happened.
+ */
+ val executionContextId: ExecutionContextId? = null,
+ )
+
+ /**
+ * Description of an isolated world.
+ *
+ * See [Runtime.ExecutionContextDescription](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-ExecutionContextDescription)
+ */
+ @Serializable
+ class ExecutionContextDescription(
+ /**
+ * Unique id of the execution context. It can be used to specify in which execution context script evaluation should be performed.
+ */
+ val id: ExecutionContextId,
+ /**
+ * Execution context origin.
+ */
+ val origin: String,
+ /**
+ * Human readable name describing given context.
+ */
+ val name: String,
+ )
+
+ /**
+ * An event in the `Runtime` CDP domain.
+ */
+ @Serializable
+ sealed class Event : CDPEvent() {
+ /**
+ * Issued when new execution context is created.
+ *
+ * See [Runtime.executionContextCreated](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#event-executionContextCreated)
+ */
+ @Serializable
+ class ExecutionContextCreated private constructor(
+ /**
+ * A newly created execution context.
+ */
+ val context: ExecutionContextDescription,
+ ) : Event()
+
+ /**
+ * Issued when execution context is destroyed.
+ *
+ * See [Runtime.executionContextDestroyed](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#event-executionContextDestroyed)
+ */
+ @Serializable
+ class ExecutionContextDestroyed private constructor(
+ /**
+ * Id of the destroyed context
+ */
+ val executionContextId: ExecutionContextId
+ ) : Event()
+ }
+}
+
+/**
+ * The [`Debugger` domain](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/) of Chrome DevTools protocol.
+ */
+class Debugger(private val requestEvaluator: CDPRequestEvaluator) {
+
+ /**
+ * Enables the debugger.
+ *
+ * See [Debugger.enable](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-enable)
+ */
+ suspend fun enable() {
+ requestEvaluator.evaluateRequest { messageId ->
+ encodeCDPMethodCall(messageId, "Debugger.enable", null)
+ }
+ }
+
+ @Serializable
+ class ResumeRequestParams private constructor(val terminateOnResume: Boolean = false) : CDPRequestParams()
+
+ /**
+ * Resumes JavaScript execution.
+ *
+ * See [Debugger.resume](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-resume)
+ */
+ suspend fun resume() {
+ requestEvaluator.evaluateRequest { messageId ->
+ encodeCDPMethodCall(messageId, "Debugger.resume", null)
+ }
+ }
+
+ @Serializable
+ class SetBreakpointByUrlResult private constructor(
+ /**
+ * Id of the created breakpoint for further reference.
+ */
+ val breakpointId: BreakpointId,
+
+ /**
+ * List of the locations this breakpoint resolved into upon addition.
+ */
+ val locations: List
+ ) : CDPMethodInvocationResult()
+
+ @Serializable
+ private class SetBreakpointByUrlRequestParams(
+ val lineNumber: Int,
+ val url: String,
+ val scriptHash: String? = null,
+ val columnNumber: Int? = null,
+ val condition: String? = null,
+ ) : CDPRequestParams()
+
+ /**
+ * Sets JavaScript breakpoint at given location specified either by URL or URL regex.
+ * Once this command is issued, all existing parsed scripts will have breakpoints resolved and returned in
+ * [SetBreakpointByUrlResult.locations] property.
+ * Further matching script parsing will result in subsequent [Debugger.Event.BreakpointResolved] events issued.
+ *
+ * See [Debugger.setBreakpointByUrl](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setBreakpointByUrl)
+ *
+ * @param lineNumber Line number to set breakpoint at.
+ * @param url URL of the resources to set breakpoint on.
+ * @param scriptHash Script hash of the resources to set breakpoint on.
+ * @param columnNumber Offset in the line to set breakpoint at.
+ * @param condition Expression to use as a breakpoint condition.
+ * When specified, debugger will only stop on the breakpoint if this expression evaluates to true.
+ */
+ suspend fun setBreakpointByUrl(
+ lineNumber: Int,
+ url: String,
+ scriptHash: String? = null,
+ columnNumber: Int? = null,
+ condition: String? = null
+ ) = requestEvaluator.evaluateRequest { messageId ->
+ encodeCDPMethodCall(
+ messageId,
+ "Debugger.setBreakpointByUrl",
+ SetBreakpointByUrlRequestParams(
+ lineNumber,
+ url,
+ scriptHash,
+ columnNumber,
+ condition
+ )
+ )
+ }
+
+ @Serializable
+ class SetBreakpointResult private constructor(
+ /**
+ * Id of the created breakpoint for further reference.
+ */
+ val breakpointId: BreakpointId,
+
+ /**
+ * Location this breakpoint resolved into.
+ */
+ val actualLocation: Location
+ ) : CDPMethodInvocationResult()
+
+ @Serializable
+ private class SetBreakpointRequestParams(
+ val location: Location,
+ val condition: String? = null,
+ ) : CDPRequestParams()
+
+ /**
+ * Sets JavaScript breakpoint at a given location.
+ *
+ * See [Debugger.setBreakpoint](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setBreakpoint)
+ *
+ * @param scriptId Script identifier as reported in the [Debugger.Event.ScriptParsed].
+ * @param lineNumber Line number in the script (0-based).
+ * @param columnNumber Column number in the script (0-based).
+ * @param condition Expression to use as a breakpoint condition.
+ * When specified, debugger will only stop on the breakpoint if this expression evaluates to true.
+ */
+ suspend fun setBreakpoint(
+ scriptId: Runtime.ScriptId,
+ lineNumber: Int,
+ columnNumber: Int? = null,
+ condition: String? = null
+ ) = requestEvaluator.evaluateRequest { messageId ->
+ encodeCDPMethodCall(
+ messageId,
+ "Debugger.setBreakpoint",
+ SetBreakpointRequestParams(Location(scriptId, lineNumber, columnNumber), condition)
+ )
+ }
+
+ @Serializable
+ private class SetSkipAllPausesRequestParams(val skip: Boolean) : CDPRequestParams()
+
+ /**
+ * Makes page not interrupt on any pauses (breakpoint, exception, dom exception etc).
+ *
+ * See [Debugger.setSkipAllPauses](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-setSkipAllPauses)
+ *
+ * @param skip New value for skip pauses state.
+ */
+ suspend fun setSkipAllPauses(skip: Boolean) {
+ requestEvaluator.evaluateRequest { messageId ->
+ encodeCDPMethodCall(
+ messageId,
+ "Debugger.setSkipAllPauses",
+ SetSkipAllPausesRequestParams(skip)
+ )
+ }
+ }
+
+ /**
+ * Steps into the function call.
+ *
+ * See [Debugger.stepInto](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-stepInto)
+ */
+ suspend fun stepInto() {
+ requestEvaluator.evaluateRequest { messageId ->
+ encodeCDPMethodCall(messageId, "Debugger.stepInto", null)
+ }
+ }
+
+ @Serializable
+ private class EvaluateOnCallFrameRequestParams(val callFrameId: CallFrameId, val expression: String) : CDPRequestParams()
+
+ /**
+ * Evaluates expression on a given call frame.
+ *
+ * See [Debugger.evaluateOnCallFrame](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-evaluateOnCallFrame)
+ *
+ * @param callFrameId Call frame identifier to evaluate on.
+ * @param expression Expression to evaluate.
+ */
+ suspend fun evaluateOnCallFrame(callFrameId: CallFrameId, expression: String) =
+ requestEvaluator.evaluateRequest { messageId ->
+ encodeCDPMethodCall(
+ messageId,
+ "Debugger.evaluateOnCallFrame",
+ EvaluateOnCallFrameRequestParams(callFrameId, expression)
+ )
+ }
+
+ /**
+ * Breakpoint identifier.
+ *
+ * See [Debugger.BreakpointId](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#type-BreakpointId)
+ */
+ @Serializable
+ @JvmInline
+ value class BreakpointId(val value: String)
+
+ /**
+ * Location in the source code.
+ *
+ * See [Debugger.Location](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#type-Location)
+ */
+ @Serializable
+ class Location internal constructor(
+ /**
+ * Script identifier as reported in the [Debugger.Event.ScriptParsed].
+ */
+ val scriptId: Runtime.ScriptId,
+ /**
+ * Line number in the script (0-based).
+ */
+ val lineNumber: Int,
+ /**
+ * Column number in the script (0-based).
+ */
+ val columnNumber: Int? = null
+ )
+
+ /**
+ * Call frame identifier.
+ *
+ * See [Debugger.CallFrameId](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#type-CallFrameId)
+ */
+ @Serializable
+ @JvmInline
+ value class CallFrameId(val value: String)
+
+ /**
+ * JavaScript call frame. Array of call frames form the call stack.
+ *
+ * See [Debugger.CallFrame](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#type-CallFrame)
+ */
+ @Serializable
+ class CallFrame private constructor(
+ /**
+ * Call frame identifier. This identifier is only valid while the virtual machine is paused.
+ */
+ val callFrameId: CallFrameId,
+
+ /**
+ * Name of the JavaScript function called on this call frame.
+ */
+ val functionName: String,
+
+ /**
+ * Function location in the source code.
+ */
+ val functionLocation: Location? = null,
+
+ /**
+ * Location in the source code.
+ */
+ val location: Location,
+
+ /**
+ * `this` object for this call frame.
+ */
+ val `this`: Runtime.RemoteObject,
+
+ /**
+ * The value being returned, if the function is at return point.
+ */
+ val returnValue: Runtime.RemoteObject? = null,
+ )
+
+ @Serializable
+ enum class PauseReason {
+
+ @SerialName("ambiguous")
+ AMBIGUOUS,
+
+ @SerialName("assert")
+ ASSERT,
+
+ @SerialName("CSPViolation")
+ CSP_VIOLATION,
+
+ @SerialName("debugCommand")
+ DEBUG_COMMAND,
+
+ @SerialName("DOM")
+ DOM,
+
+ @SerialName("EventListener")
+ EVENT_LISTENER,
+
+ @SerialName("exception")
+ EXCEPTION,
+
+ @SerialName("instrumentation")
+ INSTRUMENTATION,
+
+ @SerialName("OOM")
+ OOM,
+
+ @SerialName("other")
+ OTHER,
+
+ @SerialName("promiseRejection")
+ PROMISE_REJECTION,
+
+ @SerialName("XHR")
+ XHR,
+
+ @SerialName("Break on start")
+ BREAK_ON_START,
+ }
+
+ /**
+ * An event in the `Debugger` CDP domain.
+ */
+ @Serializable
+ sealed class Event : CDPEvent() {
+
+ /**
+ * Fired when breakpoint is resolved to an actual script and location.
+ *
+ * See [Debugger.breakpointResolved](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#event-breakpointResolved)
+ */
+ @Serializable
+ class BreakpointResolved private constructor(
+ /**
+ * Breakpoint unique identifier.
+ */
+ val breakpointId: BreakpointId,
+ /**
+ * Actual breakpoint location.
+ */
+ val location: Location
+ ) : Event()
+
+ /**
+ * Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria.
+ *
+ * See [Debugger.paused](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#event-paused)
+ */
+ @Serializable
+ class Paused private constructor(
+ /**
+ * Call stack the virtual machine stopped on.
+ */
+ val callFrames: List,
+ /**
+ * Pause reason.
+ */
+ val reason: PauseReason,
+
+ /**
+ * Hit breakpoints IDs
+ */
+ val hitBreakpoints: List = emptyList()
+ ) : Event()
+
+ /**
+ * Fired when the virtual machine resumed execution.
+ *
+ * See [Debugger.resumed](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#event-resumed)
+ */
+ @Serializable
+ object Resumed : Event()
+
+ /**
+ * Fired when virtual machine fails to parse the script.
+ *
+ * See [Debugger.scriptFailedToParse](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#event-scriptFailedToParse)
+ */
+ @Serializable
+ class ScriptFailedToParse private constructor(
+ /**
+ * Identifier of the script parsed.
+ */
+ val scriptId: Runtime.ScriptId,
+ /**
+ * URL or name of the script parsed (if any).
+ */
+ val url: String,
+ /**
+ * Line offset of the script within the resource with given URL (for script tags).
+ */
+ val startLine: Int,
+ /**
+ * Column offset of the script within the resource with given URL.
+ */
+ val startColumn: Int,
+ /**
+ * Last line of the script.
+ */
+ val endLine: Int,
+ /**
+ * Length of the last line of the script.
+ */
+ val endColumn: Int,
+ /**
+ * URL of source map associated with script (if any).
+ */
+ val sourceMapUrl: String? = null,
+ ) : Event()
+
+ /**
+ * Fired when virtual machine parses script. This event is also fired for all known and uncollected scripts upon enabling debugger.
+ *
+ * See [Debugger.scriptParsed](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#event-scriptParsed)
+ */
+ @Serializable
+ class ScriptParsed private constructor(
+ /**
+ * Identifier of the script parsed.
+ */
+ val scriptId: Runtime.ScriptId,
+ /**
+ * URL or name of the script parsed (if any).
+ */
+ val url: String,
+ /**
+ * Line offset of the script within the resource with given URL (for script tags).
+ */
+ val startLine: Int,
+ /**
+ * Column offset of the script within the resource with given URL.
+ */
+ val startColumn: Int,
+ /**
+ * Last line of the script.
+ */
+ val endLine: Int,
+ /**
+ * Length of the last line of the script.
+ */
+ val endColumn: Int,
+ /**
+ * URL of source map associated with script (if any).
+ */
+ val sourceMapUrl: String? = null,
+ ) : Event()
+ }
+}
diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/debugger/NodeJsInspectorClient.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/debugger/NodeJsInspectorClient.kt
new file mode 100644
index 00000000000..6df2580a43b
--- /dev/null
+++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/debugger/NodeJsInspectorClient.kt
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * 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.js.test.debugger
+
+import io.ktor.client.*
+import io.ktor.client.engine.cio.*
+import io.ktor.client.plugins.websocket.*
+import io.ktor.websocket.*
+import kotlinx.coroutines.runBlocking
+import kotlinx.serialization.SerializationException
+import kotlinx.serialization.encodeToString
+import kotlinx.serialization.json.Json
+import org.jetbrains.kotlin.utils.addToStdlib.cast
+import java.util.logging.Logger
+import kotlin.coroutines.*
+
+/**
+ * A client that fires up a Node.js instance in the inspector mode, and connects to it via websocket,
+ * allowing us to communicate with it using [Chrome DevTools protocol](https://chromedevtools.github.io/devtools-protocol/).
+ *
+ * @param scriptPath the script for Node to run.
+ * @param args the command line arguments passed to the script.
+ */
+class NodeJsInspectorClient(val scriptPath: String, val args: List) {
+
+ private var onDebuggerEventCallback: ((CDPEvent) -> Unit)? = null
+
+ /**
+ * Creates a Node process and provides a context for communicating with it.
+ * After [block] returns, the Node process is destroyed.
+ */
+ fun run(block: suspend NodeJsInspectorClientContext.() -> T): T = runBlocking {
+ val context = NodeJsInspectorClientContextImpl(this@NodeJsInspectorClient)
+ try {
+ runWithContext(context, block)
+ } finally {
+ context.release()
+ }
+ }
+
+ private suspend fun runWithContext(
+ context: NodeJsInspectorClientContextImpl,
+ block: suspend NodeJsInspectorClientContext.() -> T
+ ): T {
+ context.startWebsocketSession()
+
+ var blockResult: Result? = null
+ block.startCoroutine(context, object : Continuation {
+ override val context: CoroutineContext
+ get() = EmptyCoroutineContext
+
+ override fun resumeWith(result: Result) {
+ blockResult = result
+ }
+ })
+
+ context.listenForMessages { message ->
+ when (val response = decodeCDPResponse(message) { context.messageContinuations[it]!!.first }) {
+ is CDPResponse.Event -> onDebuggerEventCallback?.invoke(response.event)
+ is CDPResponse.MethodInvocationResult -> context.messageContinuations.remove(response.id)!!.second.resume(response.result)
+ is CDPResponse.Error -> context.messageContinuations[response.id]!!.second.resumeWithException(
+ IllegalStateException("error ${response.error.code}" + (response.error.message?.let { ": $it" } ?: ""))
+ )
+ }
+ context.waitingOnPredicate?.let { (predicate, continuation) ->
+ if (predicate()) {
+ context.waitingOnPredicate = null
+ continuation.resume(Unit)
+ }
+ }
+ blockResult != null
+ }
+
+ return blockResult!!.getOrThrow()
+ }
+
+ /**
+ * Installs a listener for Chrome DevTools Protocol events.
+ */
+ fun onEvent(receiveEvent: (CDPEvent) -> Unit) {
+ onDebuggerEventCallback = receiveEvent
+ }
+}
+
+private const val NODE_WS_DEBUG_URL_PREFIX = "Debugger listening on ws://"
+
+/**
+ * The actual implementation of the Node.js inspector client.
+ */
+private class NodeJsInspectorClientContextImpl(engine: NodeJsInspectorClient) : NodeJsInspectorClientContext, CDPRequestEvaluator {
+
+ private val logger = Logger.getLogger(this::class.java.name)
+
+ private val nodeProcess = ProcessBuilder(
+ System.getProperty("javascript.engine.path.NodeJs"),
+ "--inspect-brk=0",
+ engine.scriptPath,
+ *engine.args.toTypedArray()
+ ).also {
+ logger.fine(it::joinedCommand)
+ }.start()
+
+ /**
+ * The WebSocket address to connect to.
+ */
+ private val debugUrl: String = run {
+ val prompt = nodeProcess.errorStream.bufferedReader().readLine()
+ logger.fine(prompt)
+ if (prompt.startsWith(NODE_WS_DEBUG_URL_PREFIX)) {
+ val startIndexInLine = NODE_WS_DEBUG_URL_PREFIX.length - "ws://".length
+ prompt.substring(startIndexInLine).trim()
+ } else {
+ error(prompt)
+ }
+ }
+
+ private val webSocketClient = HttpClient(CIO) {
+ install(WebSockets)
+ }
+
+ private var webSocketSession: DefaultClientWebSocketSession? = null
+
+ val messageContinuations = mutableMapOf>>()
+
+ /**
+ * See [waitForConditionToBecomeTrue].
+ */
+ var waitingOnPredicate: Pair<(() -> Boolean), Continuation>? = null
+
+ private var nextMessageId = 0
+
+ suspend fun startWebsocketSession() {
+ webSocketSession = webSocketClient.webSocketSession(debugUrl)
+ }
+
+ private val loggingJsonPrettyPrinter by lazy { Json { prettyPrint = true } }
+
+ private fun prettyPrintJson(json: String): String {
+ val jsonElement = try {
+ Json.parseToJsonElement(json)
+ } catch (e: SerializationException) {
+ return json
+ }
+ return loggingJsonPrettyPrinter.encodeToString(jsonElement)
+ }
+
+ /**
+ * Starts a loop that waits for incoming Chrome DevTools Protocol messages and invokes [receiveMessage] when one is received.
+ * The loop stops as soon as at least one message is received *and* [receiveMessage] returns `true`.
+ */
+ suspend fun listenForMessages(receiveMessage: (String) -> Boolean) {
+ val session = webSocketSession ?: error("Session closed")
+ do {
+ val message = when (val frame = session.incoming.receive()) {
+ is Frame.Text -> frame.readText()
+ else -> error("Unexpected frame kind: $frame")
+ }
+ logger.fine {
+ "Received message:\n${prettyPrintJson(message)}"
+ }
+ } while (!receiveMessage(message))
+ }
+
+ override val debugger = Debugger(this)
+
+ override val runtime = Runtime(this)
+
+ override suspend fun waitForConditionToBecomeTrue(predicate: () -> Boolean) {
+ if (predicate()) return
+ suspendCoroutine { continuation ->
+ require(waitingOnPredicate == null) { "already waiting!" }
+ waitingOnPredicate = predicate to continuation
+ }
+ }
+
+ private suspend fun sendPlainTextMessage(message: String) {
+ val session = webSocketSession ?: error("Session closed")
+ logger.fine {
+ "Sent message:\n${prettyPrintJson(message)}"
+ }
+ session.send(message)
+ }
+
+ @Deprecated("Only for debugging purposes", level = DeprecationLevel.WARNING)
+ override suspend fun sendPlainTextMessage(methodName: String, paramsJson: String): String {
+ val messageId = nextMessageId++
+ sendPlainTextMessage("""{"id":$messageId,"method":$methodName,"params":$paramsJson}""")
+ return suspendCoroutine { continuation ->
+ messageContinuations[messageId] = CDPMethodCallEncodingInfoPlainText to continuation
+ }.cast().string
+ }
+
+ override suspend fun genericEvaluateRequest(
+ encodeMethodCallWithMessageId: (Int) -> Pair
+ ): CDPMethodInvocationResult {
+ val messageId = nextMessageId++
+ val (encodedMessage, encodingInfo) = encodeMethodCallWithMessageId(messageId)
+ sendPlainTextMessage(encodedMessage)
+ return suspendCoroutine { continuation ->
+ messageContinuations[messageId] = encodingInfo to continuation
+ }
+ }
+
+ /**
+ * Releases all the resources and destroys the Node.js process.
+ */
+ suspend fun release() {
+ webSocketSession?.close()
+ webSocketSession = null
+ webSocketClient.close()
+ nodeProcess.destroy()
+ }
+}
+
+private fun ProcessBuilder.joinedCommand(): String =
+ command().joinToString(" ") { "\"${it.replace("\"", "\\\"")}\"" }
diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/debugger/NodeJsInspectorClientContext.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/debugger/NodeJsInspectorClientContext.kt
new file mode 100644
index 00000000000..027709d88f4
--- /dev/null
+++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/debugger/NodeJsInspectorClientContext.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * 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.js.test.debugger
+
+/**
+ * An interface for invoking [Chrome DevTools protocol](https://chromedevtools.github.io/devtools-protocol/) methods.
+ *
+ * An instance of this interface is passed to the `block` closure in [NodeJsInspectorClient.run].
+ */
+interface NodeJsInspectorClientContext {
+
+ /**
+ * The [`Debugger` domain](https://chromedevtools.github.io/devtools-protocol/tot/Debugger/) of Chrome DevTools protocol.
+ */
+ val debugger: Debugger
+
+ /**
+ * The [`Runtime` domain](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/) of Chrome DevTools protocol.
+ */
+ val runtime: Runtime
+
+ @Deprecated("Only for debugging purposes", level = DeprecationLevel.WARNING)
+ suspend fun sendPlainTextMessage(methodName: String, paramsJson: String): String
+
+ /**
+ * On each incoming message invokes [predicate], and returns only when [predicate] returns `true`.
+ */
+ suspend fun waitForConditionToBecomeTrue(predicate: () -> Boolean)
+}
+
+/**
+ * On each incoming message checks whether [test] returns `null`, and returns only when [test] returns non-`null` value.
+ */
+suspend inline fun NodeJsInspectorClientContext.waitForValueToBecomeNonNull(crossinline test: () -> T?): T {
+ var value: T? = null
+ waitForConditionToBecomeTrue {
+ test()?.also {
+ value = it
+ } != null
+ }
+ return value!!
+}
diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/handlers/JsDebugRunner.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/handlers/JsDebugRunner.kt
new file mode 100644
index 00000000000..43c1574b578
--- /dev/null
+++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/handlers/JsDebugRunner.kt
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * 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.js.test.handlers
+
+import org.jetbrains.kotlin.KtPsiSourceFileLinesMapping
+import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.TranslationMode
+import org.jetbrains.kotlin.js.parser.sourcemaps.*
+import org.jetbrains.kotlin.js.test.debugger.*
+import org.jetbrains.kotlin.js.test.utils.getAllFilesForRunner
+import org.jetbrains.kotlin.js.test.utils.getBoxFunction
+import org.jetbrains.kotlin.psi.psiUtil.startOffset
+import org.jetbrains.kotlin.test.TargetBackend
+import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives
+import org.jetbrains.kotlin.test.model.TestFile
+import org.jetbrains.kotlin.test.model.TestModule
+import org.jetbrains.kotlin.test.services.TestServices
+import org.jetbrains.kotlin.test.services.configuration.JsEnvironmentConfigurator
+import org.jetbrains.kotlin.test.services.moduleStructure
+import org.jetbrains.kotlin.test.utils.SteppingTestLoggedData
+import org.jetbrains.kotlin.test.utils.checkSteppingTestResult
+import org.jetbrains.kotlin.test.utils.formatAsSteppingTestExpectation
+import java.io.File
+import java.net.URI
+import java.net.URISyntaxException
+
+/**
+ * This class is an analogue of the [DebugRunner][org.jetbrains.kotlin.test.backend.handlers.DebugRunner] from JVM stepping tests.
+ *
+ * It runs a generated JavaScript file under a debugger, sets a breakpoint in the beginning of the `box` function
+ * and performs the "step into" action until there is nothing more to step into. On each pause it records the source file name,
+ * the source line and the function name of the current call frame, and compares this data with the expectations written in the test file.
+ *
+ * It uses sourcemaps for mapping locations in the generated JS file to the corresponding locations in the source Kotlin file.
+ * Also, it assumes that the sourcemap contains absolute paths to source files. The relative paths are replaced with
+ * absolute paths earlier by [JsSourceMapPathRewriter].
+ *
+ * Stepping tests only work with the IR backend. The legacy backend is not supported.
+ *
+ * For simplicity, only the [FULL][org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.TranslationMode.FULL] translation mode is
+ * supported.
+ *
+ */
+class JsDebugRunner(testServices: TestServices) : AbstractJsArtifactsCollector(testServices) {
+ override fun processAfterAllModules(someAssertionWasFailed: Boolean) {
+ if (someAssertionWasFailed) return
+
+ val globalDirectives = testServices.moduleStructure.allDirectives
+ val esModules = JsEnvironmentConfigurationDirectives.ES_MODULES in globalDirectives
+
+ if (esModules) return
+
+ // This file generated in the FULL mode should be self-sufficient.
+ val jsFilePath = getAllFilesForRunner(testServices, modulesToArtifact)[TranslationMode.FULL]?.single()
+ ?: error("Only FULL translation mode is supported")
+
+ val mainModule = JsEnvironmentConfigurator.getMainModule(testServices)
+
+ val sourceMapFile = File("$jsFilePath.map")
+ val sourceMap = when (val parseResult = SourceMapParser.parse(sourceMapFile)) {
+ is SourceMapSuccess -> parseResult.value
+ is SourceMapError -> error(parseResult.message)
+ }
+
+ runGeneratedCode(jsFilePath, sourceMap, mainModule)
+ }
+
+ private fun runGeneratedCode(
+ jsFilePath: String,
+ sourceMap: SourceMap,
+ mainModule: TestModule,
+ ) {
+ val (testFileWithBoxFunction, boxFunctionStartLine) = getBoxFunctionStartLocation(mainModule)
+ val originalFileWithBoxFunction = testFileWithBoxFunction.originalFile
+
+ val boxFunctionLineInGeneratedFile =
+ sourceMap.breakpointLineInGeneratedFile(originalFileWithBoxFunction, boxFunctionStartLine)
+
+ if (boxFunctionLineInGeneratedFile < 0)
+ error("Could not find the location of the 'box' function in the generated file")
+
+ val debuggerFacade = NodeJsDebuggerFacade(jsFilePath)
+
+ val jsFileURI = File(jsFilePath).absoluteFile.toURI().withAuthority("")
+
+ val loggedItems = mutableListOf()
+ debuggerFacade.run {
+ with(debuggerFacade) {
+ val boxFunctionBreakpoint = debugger.setBreakpointByUrl(boxFunctionLineInGeneratedFile, jsFileURI.toString())
+ debugger.resume()
+ waitForResumeEvent()
+ waitForPauseEvent {
+ it.reason == Debugger.PauseReason.OTHER && it.hitBreakpoints.contains(boxFunctionBreakpoint.breakpointId)
+ }
+ while (true) {
+ val topMostCallFrame = waitForPauseEvent().callFrames[0]
+ try {
+ if (URI(scriptUrlByScriptId(topMostCallFrame.location.scriptId)) != jsFileURI) break
+ } catch (_: URISyntaxException) {
+ // Probably something like 'evalmachine.' brought us here. Ignore.
+ }
+ addCallFrameInfoToLoggedItems(sourceMap, topMostCallFrame, loggedItems)
+ debugger.stepInto()
+ waitForResumeEvent()
+ }
+ }
+ }
+ checkSteppingTestResult(
+ mainModule.frontendKind,
+ mainModule.targetBackend ?: TargetBackend.JS_IR,
+ originalFileWithBoxFunction,
+ loggedItems
+ )
+ }
+
+ private fun addCallFrameInfoToLoggedItems(
+ sourceMap: SourceMap,
+ topMostCallFrame: Debugger.CallFrame,
+ loggedItems: MutableList
+ ) {
+ sourceMap.getSourceLineForGeneratedLocation(topMostCallFrame.location)?.let { (sourceFile, sourceLine) ->
+ val testFileName = testFileNameFromMappedLocation(sourceFile, sourceLine) ?: return
+ val expectation =
+ formatAsSteppingTestExpectation(testFileName, sourceLine + 1, topMostCallFrame.functionName, false)
+ loggedItems.add(SteppingTestLoggedData(sourceLine + 1, false, expectation))
+ }
+ }
+
+ /**
+ * Returns the test file and the line number in that file where the body of the `box` function begins.
+ */
+ private fun getBoxFunctionStartLocation(mainModule: TestModule): Pair {
+ val boxFunction = getBoxFunction(testServices) ?: error("Missing 'box' function")
+ val file = boxFunction.containingKtFile
+ val mapping = KtPsiSourceFileLinesMapping(file)
+ val firstStatementOffset = boxFunction.bodyBlockExpression?.firstStatement?.startOffset
+ ?: boxFunction.bodyExpression?.startOffset
+ ?: boxFunction.startOffset
+ return mainModule.files.single { it.name == file.name } to mapping.getLineByOffset(firstStatementOffset)
+ }
+
+ /**
+ * Maps the location in the source file to the location in the generated file.
+ *
+ * The Node.js debugger is not sourcemap-aware, so we need to set a breakpoint in the `box` function in the generated JS file.
+ *
+ * We don't know where the generated `box` function is located, so we use the source map to figure it out.
+ *
+ * This is basically what Intellij IDEA's built-in JavaScript debugger does when you set a breakpoint in a source file: it tries
+ * to map the location of the breakpoint in the source file to a location in the generated file. Here we use a simplified
+ * algorithm for that.
+ */
+ private fun SourceMap.breakpointLineInGeneratedFile(sourceFile: File, sourceLine: Int): Int {
+ val sourceFileAbsolutePath = sourceFile.absoluteFile.normalize()
+ var candidateSegment: Pair? = null
+ for ((generatedLineNumber, group) in groups.withIndex()) {
+ for (segment in group.segments) {
+ if (segment.sourceFileName?.let { File(it).absoluteFile.normalize() } != sourceFileAbsolutePath ||
+ segment.sourceLineNumber != sourceLine)
+ continue
+ if (candidateSegment == null)
+ candidateSegment = generatedLineNumber to segment
+ // Find the segment that points to the earliest column in the source file
+ if (segment.sourceColumnNumber < candidateSegment.second.sourceColumnNumber)
+ candidateSegment = generatedLineNumber to segment
+ }
+ }
+ return candidateSegment?.first ?: -1
+ }
+
+ /**
+ * Maps [location] in the generated JavaScript file to the corresponding location in a source file.
+ * @return The source file path (as specified in the source map) and the line number in that source file.
+ */
+ private fun SourceMap.getSourceLineForGeneratedLocation(location: Debugger.Location): Pair? {
+
+ fun SourceMapSegment.sourceFileAndLine() = sourceFileName!! to sourceLineNumber
+
+ val group = groups.getOrNull(location.lineNumber)?.takeIf { it.segments.isNotEmpty() } ?: return null
+ val columnNumber = location.columnNumber ?: return group.segments[0].sourceFileAndLine()
+ val segment = if (columnNumber <= group.segments[0].generatedColumnNumber) {
+ group.segments[0]
+ } else {
+ group.segments.find {
+ columnNumber > it.generatedColumnNumber
+ }
+ }
+ return segment?.sourceFileAndLine()
+ }
+
+ /**
+ * An original test file may represent multiple source files (by using the `// FILE: myFile.kt` comments).
+ * Sourcemaps contain paths to original test files. However, in test expectations we write names as in the `// FILE:` comments.
+ * This function maps a location in the original test file to the name specified in a `// FILE:` comment.
+ */
+ private fun testFileNameFromMappedLocation(originalFilePath: String, originalFileLineNumber: Int): String? {
+ val originalFile = File(originalFilePath)
+ return testServices.moduleStructure.modules.flatMap { it.files }.findLast {
+ it.originalFile.absolutePath == originalFile.absolutePath && it.startLineNumberInOriginalFile <= originalFileLineNumber
+ }?.name
+ }
+}
+
+/**
+ * A wrapper around [NodeJsInspectorClient] that handles all the ceremony and allows us to only care about executing common debugging
+ * actions.
+ *
+ * @param jsFilePath the test file to execute and debug.
+ */
+private class NodeJsDebuggerFacade(jsFilePath: String) {
+
+ private val inspector = NodeJsInspectorClient("js/js.translator/testData/runIrTestInNode.js", listOf(jsFilePath))
+
+ private val scriptUrls = mutableMapOf()
+
+ private var pausedEvent: Debugger.Event.Paused? = null
+
+ init {
+ inspector.onEvent { event ->
+ when (event) {
+ is Debugger.Event.ScriptParsed -> {
+ scriptUrls[event.scriptId] = event.url
+ }
+ is Debugger.Event.Paused -> {
+ pausedEvent = event
+ }
+ is Debugger.Event.Resumed -> {
+ pausedEvent = null
+ }
+ else -> {}
+ }
+ }
+ }
+
+ /**
+ * By the time [body] is called, the execution is paused, no code is executed yet.
+ */
+ fun run(body: suspend NodeJsInspectorClientContext.() -> T) = inspector.run {
+ debugger.enable()
+ debugger.setSkipAllPauses(false)
+ runtime.runIfWaitingForDebugger()
+ waitForPauseEvent { it.reason == Debugger.PauseReason.BREAK_ON_START }
+
+ body()
+ }
+
+ fun scriptUrlByScriptId(scriptId: Runtime.ScriptId) = scriptUrls[scriptId] ?: error("unknown scriptId")
+
+ suspend fun NodeJsInspectorClientContext.waitForPauseEvent(suchThat: (Debugger.Event.Paused) -> Boolean = { true }) =
+ waitForValueToBecomeNonNull {
+ pausedEvent?.takeIf(suchThat)
+ }
+
+ suspend fun NodeJsInspectorClientContext.waitForResumeEvent() = waitForConditionToBecomeTrue { pausedEvent == null }
+}
+
+private fun URI.withAuthority(newAuthority: String?) =
+ URI(scheme, newAuthority, path, query, fragment)
diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/AbstractJsIrTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/AbstractJsIrTest.kt
index fcf34dad7d5..8579638b7b9 100644
--- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/AbstractJsIrTest.kt
+++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/AbstractJsIrTest.kt
@@ -153,6 +153,21 @@ open class AbstractJsIrLineNumberTest : AbstractJsIrTest(
}
}
+open class AbstractIrJsSteppingTest : AbstractJsIrTest(
+ pathToTestDir = "compiler/testData/debug/stepping/",
+ testGroupOutputDirPrefix = "debug/stepping/"
+) {
+ override fun TestConfigurationBuilder.configuration() {
+ commonConfigurationForJsBlackBoxCodegenTest()
+ defaultDirectives {
+ +JsEnvironmentConfigurationDirectives.NO_COMMON_FILES
+ }
+ jsArtifactsHandlersStep {
+ useHandlers(::JsDebugRunner)
+ }
+ }
+}
+
open class AbstractIrCodegenWasmJsInteropJsTest : AbstractJsIrTest(
pathToTestDir = "compiler/testData/codegen/wasmJsInterop",
testGroupOutputDirPrefix = "codegen/wasmJsInteropJs"
diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/RunnerUtils.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/RunnerUtils.kt
index eb09778ad7c..1dbfcdee00c 100644
--- a/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/RunnerUtils.kt
+++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/RunnerUtils.kt
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.js.test.JsAdditionalSourceProvider
import org.jetbrains.kotlin.js.test.converters.augmentWithModuleName
import org.jetbrains.kotlin.js.test.handlers.JsBoxRunner.Companion.TEST_FUNCTION
import org.jetbrains.kotlin.js.testOld.*
+import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.serialization.js.ModuleKind
import org.jetbrains.kotlin.test.TargetBackend
@@ -24,6 +25,7 @@ import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.*
import org.jetbrains.kotlin.test.services.configuration.JsEnvironmentConfigurator
import org.jetbrains.kotlin.test.services.configuration.JsEnvironmentConfigurator.Companion.getMainModule
+import org.jetbrains.kotlin.util.collectionUtils.filterIsInstanceAnd
import java.io.File
private const val MODULE_EMULATION_FILE = "${JsEnvironmentConfigurator.TEST_DATA_DIR_PATH}/moduleEmulation.js"
@@ -192,7 +194,7 @@ fun getTestModuleName(testServices: TestServices): String? {
return getMainModule(testServices).name
}
-fun extractTestPackage(testServices: TestServices): String? {
+fun getBoxFunction(testServices: TestServices): KtNamedFunction? {
val runPlainBoxFunction = RUN_PLAIN_BOX_FUNCTION in testServices.moduleStructure.allDirectives
if (runPlainBoxFunction) return null
val ktFiles = testServices.moduleStructure.modules.flatMap { module ->
@@ -204,12 +206,14 @@ fun extractTestPackage(testServices: TestServices): String? {
}
}
- return ktFiles.singleOrNull { ktFile ->
- val boxFunction = ktFile.declarations.find { it is KtNamedFunction && it.name == TEST_FUNCTION }
- boxFunction != null
- }?.packageFqName?.asString()?.takeIf { it.isNotEmpty() }
+ return ktFiles.mapNotNull { ktFile ->
+ ktFile.declarations.filterIsInstanceAnd { it.name == TEST_FUNCTION }.firstOrNull()
+ }.singleOrNull()
}
+fun extractTestPackage(testServices: TestServices): String? =
+ getBoxFunction(testServices)?.containingKtFile?.packageFqName?.asString()?.takeIf { it.isNotEmpty() }
+
fun getTestChecker(testServices: TestServices): AbstractJsTestChecker {
val runTestInNashorn = java.lang.Boolean.getBoolean("kotlin.js.useNashorn")
val targetBackend = testServices.defaultsProvider.defaultTargetBackend ?: TargetBackend.JS
diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsSteppingTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsSteppingTestGenerated.java
new file mode 100644
index 00000000000..aa444e05c0d
--- /dev/null
+++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsSteppingTestGenerated.java
@@ -0,0 +1,585 @@
+/*
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ * 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.js.test.ir;
+
+import com.intellij.testFramework.TestDataPath;
+import org.jetbrains.kotlin.test.util.KtTestUtil;
+import org.jetbrains.kotlin.test.TargetBackend;
+import org.jetbrains.kotlin.test.TestMetadata;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.util.regex.Pattern;
+
+/** This class is generated by {@link GenerateNewCompilerTests.kt}. DO NOT MODIFY MANUALLY */
+@SuppressWarnings("all")
+@TestMetadata("compiler/testData/debug/stepping")
+@TestDataPath("$PROJECT_ROOT")
+public class IrJsSteppingTestGenerated extends AbstractIrJsSteppingTest {
+ @Test
+ public void testAllFilesPresentInStepping() throws Exception {
+ KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/debug/stepping"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
+ }
+
+ @Test
+ @TestMetadata("anonymousFunction.kt")
+ public void testAnonymousFunction() throws Exception {
+ runTest("compiler/testData/debug/stepping/anonymousFunction.kt");
+ }
+
+ @Test
+ @TestMetadata("anonymousFunctionDirect.kt")
+ public void testAnonymousFunctionDirect() throws Exception {
+ runTest("compiler/testData/debug/stepping/anonymousFunctionDirect.kt");
+ }
+
+ @Test
+ @TestMetadata("assertion.kt")
+ public void testAssertion() throws Exception {
+ runTest("compiler/testData/debug/stepping/assertion.kt");
+ }
+
+ @Test
+ @TestMetadata("beforeGotoToWhileStart.kt")
+ public void testBeforeGotoToWhileStart() throws Exception {
+ runTest("compiler/testData/debug/stepping/beforeGotoToWhileStart.kt");
+ }
+
+ @Test
+ @TestMetadata("callWithCallInArguments.kt")
+ public void testCallWithCallInArguments() throws Exception {
+ runTest("compiler/testData/debug/stepping/callWithCallInArguments.kt");
+ }
+
+ @Test
+ @TestMetadata("callWithReceiver.kt")
+ public void testCallWithReceiver() throws Exception {
+ runTest("compiler/testData/debug/stepping/callWithReceiver.kt");
+ }
+
+ @Test
+ @TestMetadata("callableReference.kt")
+ public void testCallableReference() throws Exception {
+ runTest("compiler/testData/debug/stepping/callableReference.kt");
+ }
+
+ @Test
+ @TestMetadata("chainCall.kt")
+ public void testChainCall() throws Exception {
+ runTest("compiler/testData/debug/stepping/chainCall.kt");
+ }
+
+ @Test
+ @TestMetadata("class.kt")
+ public void testClass() throws Exception {
+ runTest("compiler/testData/debug/stepping/class.kt");
+ }
+
+ @Test
+ @TestMetadata("classObject.kt")
+ public void testClassObject() throws Exception {
+ runTest("compiler/testData/debug/stepping/classObject.kt");
+ }
+
+ @Test
+ @TestMetadata("compileTimeConstant.kt")
+ public void testCompileTimeConstant() throws Exception {
+ runTest("compiler/testData/debug/stepping/compileTimeConstant.kt");
+ }
+
+ @Test
+ @TestMetadata("conjunction.kt")
+ public void testConjunction() throws Exception {
+ runTest("compiler/testData/debug/stepping/conjunction.kt");
+ }
+
+ @Test
+ @TestMetadata("constantConditions.kt")
+ public void testConstantConditions() throws Exception {
+ runTest("compiler/testData/debug/stepping/constantConditions.kt");
+ }
+
+ @Test
+ @TestMetadata("constructorCall.kt")
+ public void testConstructorCall() throws Exception {
+ runTest("compiler/testData/debug/stepping/constructorCall.kt");
+ }
+
+ @Test
+ @TestMetadata("constructors.kt")
+ public void testConstructors() throws Exception {
+ runTest("compiler/testData/debug/stepping/constructors.kt");
+ }
+
+ @Test
+ @TestMetadata("dataClass.kt")
+ public void testDataClass() throws Exception {
+ runTest("compiler/testData/debug/stepping/dataClass.kt");
+ }
+
+ @Test
+ @TestMetadata("defaultParameter.kt")
+ public void testDefaultParameter() throws Exception {
+ runTest("compiler/testData/debug/stepping/defaultParameter.kt");
+ }
+
+ @Test
+ @TestMetadata("enum.kt")
+ public void testEnum() throws Exception {
+ runTest("compiler/testData/debug/stepping/enum.kt");
+ }
+
+ @Test
+ @TestMetadata("for.kt")
+ public void testFor() throws Exception {
+ runTest("compiler/testData/debug/stepping/for.kt");
+ }
+
+ @Test
+ @TestMetadata("functionCallWithDefault.kt")
+ public void testFunctionCallWithDefault() throws Exception {
+ runTest("compiler/testData/debug/stepping/functionCallWithDefault.kt");
+ }
+
+ @Test
+ @TestMetadata("functionCallWithInlinedLambdaParam.kt")
+ public void testFunctionCallWithInlinedLambdaParam() throws Exception {
+ runTest("compiler/testData/debug/stepping/functionCallWithInlinedLambdaParam.kt");
+ }
+
+ @Test
+ @TestMetadata("functionCallWithLambdaParam.kt")
+ public void testFunctionCallWithLambdaParam() throws Exception {
+ runTest("compiler/testData/debug/stepping/functionCallWithLambdaParam.kt");
+ }
+
+ @Test
+ @TestMetadata("functionInAnotherFile.kt")
+ public void testFunctionInAnotherFile() throws Exception {
+ runTest("compiler/testData/debug/stepping/functionInAnotherFile.kt");
+ }
+
+ @Test
+ @TestMetadata("if.kt")
+ public void testIf() throws Exception {
+ runTest("compiler/testData/debug/stepping/if.kt");
+ }
+
+ @Test
+ @TestMetadata("if2.kt")
+ public void testIf2() throws Exception {
+ runTest("compiler/testData/debug/stepping/if2.kt");
+ }
+
+ @Test
+ @TestMetadata("ifThen.kt")
+ public void testIfThen() throws Exception {
+ runTest("compiler/testData/debug/stepping/ifThen.kt");
+ }
+
+ @Test
+ @TestMetadata("ifThenElse.kt")
+ public void testIfThenElse() throws Exception {
+ runTest("compiler/testData/debug/stepping/ifThenElse.kt");
+ }
+
+ @Test
+ @TestMetadata("ifThenElseFalse.kt")
+ public void testIfThenElseFalse() throws Exception {
+ runTest("compiler/testData/debug/stepping/ifThenElseFalse.kt");
+ }
+
+ @Test
+ @TestMetadata("IfTrueThenFalse.kt")
+ public void testIfTrueThenFalse() throws Exception {
+ runTest("compiler/testData/debug/stepping/IfTrueThenFalse.kt");
+ }
+
+ @Test
+ @TestMetadata("ifWithInlineInCondition.kt")
+ public void testIfWithInlineInCondition() throws Exception {
+ runTest("compiler/testData/debug/stepping/ifWithInlineInCondition.kt");
+ }
+
+ @Test
+ @TestMetadata("iincStepping.kt")
+ public void testIincStepping() throws Exception {
+ runTest("compiler/testData/debug/stepping/iincStepping.kt");
+ }
+
+ @Test
+ @TestMetadata("inTheEndOfLambdaArgumentOfInlineCall.kt")
+ public void testInTheEndOfLambdaArgumentOfInlineCall() throws Exception {
+ runTest("compiler/testData/debug/stepping/inTheEndOfLambdaArgumentOfInlineCall.kt");
+ }
+
+ @Test
+ @TestMetadata("initBlocks.kt")
+ public void testInitBlocks() throws Exception {
+ runTest("compiler/testData/debug/stepping/initBlocks.kt");
+ }
+
+ @Test
+ @TestMetadata("initBlocksCompanion.kt")
+ public void testInitBlocksCompanion() throws Exception {
+ runTest("compiler/testData/debug/stepping/initBlocksCompanion.kt");
+ }
+
+ @Test
+ @TestMetadata("inlineCallableReference.kt")
+ public void testInlineCallableReference() throws Exception {
+ runTest("compiler/testData/debug/stepping/inlineCallableReference.kt");
+ }
+
+ @Test
+ @TestMetadata("inlineNamedCallableReference.kt")
+ public void testInlineNamedCallableReference() throws Exception {
+ runTest("compiler/testData/debug/stepping/inlineNamedCallableReference.kt");
+ }
+
+ @Test
+ @TestMetadata("inlineSimpleCall.kt")
+ public void testInlineSimpleCall() throws Exception {
+ runTest("compiler/testData/debug/stepping/inlineSimpleCall.kt");
+ }
+
+ @Test
+ @TestMetadata("kt15259.kt")
+ public void testKt15259() throws Exception {
+ runTest("compiler/testData/debug/stepping/kt15259.kt");
+ }
+
+ @Test
+ @TestMetadata("kt29179.kt")
+ public void testKt29179() throws Exception {
+ runTest("compiler/testData/debug/stepping/kt29179.kt");
+ }
+
+ @Test
+ @TestMetadata("kt42208.kt")
+ public void testKt42208() throws Exception {
+ runTest("compiler/testData/debug/stepping/kt42208.kt");
+ }
+
+ @Test
+ @TestMetadata("kt42208b.kt")
+ public void testKt42208b() throws Exception {
+ runTest("compiler/testData/debug/stepping/kt42208b.kt");
+ }
+
+ @Test
+ @TestMetadata("kt42208c.kt")
+ public void testKt42208c() throws Exception {
+ runTest("compiler/testData/debug/stepping/kt42208c.kt");
+ }
+
+ @Test
+ @TestMetadata("lambdaStepInline.kt")
+ public void testLambdaStepInline() throws Exception {
+ runTest("compiler/testData/debug/stepping/lambdaStepInline.kt");
+ }
+
+ @Test
+ @TestMetadata("lambdaStepInlineWithDefaults.kt")
+ public void testLambdaStepInlineWithDefaults() throws Exception {
+ runTest("compiler/testData/debug/stepping/lambdaStepInlineWithDefaults.kt");
+ }
+
+ @Test
+ @TestMetadata("lineNumberAfterInline.kt")
+ public void testLineNumberAfterInline() throws Exception {
+ runTest("compiler/testData/debug/stepping/lineNumberAfterInline.kt");
+ }
+
+ @Test
+ @TestMetadata("linenumberForOneParametersArgumentCall.kt")
+ public void testLinenumberForOneParametersArgumentCall() throws Exception {
+ runTest("compiler/testData/debug/stepping/linenumberForOneParametersArgumentCall.kt");
+ }
+
+ @Test
+ @TestMetadata("localFunction.kt")
+ public void testLocalFunction() throws Exception {
+ runTest("compiler/testData/debug/stepping/localFunction.kt");
+ }
+
+ @Test
+ @TestMetadata("localFunctionWIthOnelineExpressionBody.kt")
+ public void testLocalFunctionWIthOnelineExpressionBody() throws Exception {
+ runTest("compiler/testData/debug/stepping/localFunctionWIthOnelineExpressionBody.kt");
+ }
+
+ @Test
+ @TestMetadata("localProperty.kt")
+ public void testLocalProperty() throws Exception {
+ runTest("compiler/testData/debug/stepping/localProperty.kt");
+ }
+
+ @Test
+ @TestMetadata("multiModule.kt")
+ public void testMultiModule() throws Exception {
+ runTest("compiler/testData/debug/stepping/multiModule.kt");
+ }
+
+ @Test
+ @TestMetadata("multilineExpression.kt")
+ public void testMultilineExpression() throws Exception {
+ runTest("compiler/testData/debug/stepping/multilineExpression.kt");
+ }
+
+ @Test
+ @TestMetadata("multilineFunctionCall.kt")
+ public void testMultilineFunctionCall() throws Exception {
+ runTest("compiler/testData/debug/stepping/multilineFunctionCall.kt");
+ }
+
+ @Test
+ @TestMetadata("multilineInfixCall.kt")
+ public void testMultilineInfixCall() throws Exception {
+ runTest("compiler/testData/debug/stepping/multilineInfixCall.kt");
+ }
+
+ @Test
+ @TestMetadata("namedCallableReference.kt")
+ public void testNamedCallableReference() throws Exception {
+ runTest("compiler/testData/debug/stepping/namedCallableReference.kt");
+ }
+
+ @Test
+ @TestMetadata("nestedInline.kt")
+ public void testNestedInline() throws Exception {
+ runTest("compiler/testData/debug/stepping/nestedInline.kt");
+ }
+
+ @Test
+ @TestMetadata("noParametersArgumentCallInExpression.kt")
+ public void testNoParametersArgumentCallInExpression() throws Exception {
+ runTest("compiler/testData/debug/stepping/noParametersArgumentCallInExpression.kt");
+ }
+
+ @Test
+ @TestMetadata("nullcheck.kt")
+ public void testNullcheck() throws Exception {
+ runTest("compiler/testData/debug/stepping/nullcheck.kt");
+ }
+
+ @Test
+ @TestMetadata("primitiveNullChecks.kt")
+ public void testPrimitiveNullChecks() throws Exception {
+ runTest("compiler/testData/debug/stepping/primitiveNullChecks.kt");
+ }
+
+ @Test
+ @TestMetadata("propertyAccessor.kt")
+ public void testPropertyAccessor() throws Exception {
+ runTest("compiler/testData/debug/stepping/propertyAccessor.kt");
+ }
+
+ @Test
+ @TestMetadata("psvm.kt")
+ public void testPsvm() throws Exception {
+ runTest("compiler/testData/debug/stepping/psvm.kt");
+ }
+
+ @Test
+ @TestMetadata("recursion.kt")
+ public void testRecursion() throws Exception {
+ runTest("compiler/testData/debug/stepping/recursion.kt");
+ }
+
+ @Test
+ @TestMetadata("simpleDefaultArg.kt")
+ public void testSimpleDefaultArg() throws Exception {
+ runTest("compiler/testData/debug/stepping/simpleDefaultArg.kt");
+ }
+
+ @Test
+ @TestMetadata("simpleDefaultArgWithInline.kt")
+ public void testSimpleDefaultArgWithInline() throws Exception {
+ runTest("compiler/testData/debug/stepping/simpleDefaultArgWithInline.kt");
+ }
+
+ @Test
+ @TestMetadata("simpleInlineDefaultArg.kt")
+ public void testSimpleInlineDefaultArg() throws Exception {
+ runTest("compiler/testData/debug/stepping/simpleInlineDefaultArg.kt");
+ }
+
+ @Test
+ @TestMetadata("simpleSmap.kt")
+ public void testSimpleSmap() throws Exception {
+ runTest("compiler/testData/debug/stepping/simpleSmap.kt");
+ }
+
+ @Test
+ @TestMetadata("smapInlineAsArgument.kt")
+ public void testSmapInlineAsArgument() throws Exception {
+ runTest("compiler/testData/debug/stepping/smapInlineAsArgument.kt");
+ }
+
+ @Test
+ @TestMetadata("smapInlineAsInfixArgument.kt")
+ public void testSmapInlineAsInfixArgument() throws Exception {
+ runTest("compiler/testData/debug/stepping/smapInlineAsInfixArgument.kt");
+ }
+
+ @Test
+ @TestMetadata("smapInlineAsInlineArgument.kt")
+ public void testSmapInlineAsInlineArgument() throws Exception {
+ runTest("compiler/testData/debug/stepping/smapInlineAsInlineArgument.kt");
+ }
+
+ @Test
+ @TestMetadata("smapInlineInIntrinsicArgument.kt")
+ public void testSmapInlineInIntrinsicArgument() throws Exception {
+ runTest("compiler/testData/debug/stepping/smapInlineInIntrinsicArgument.kt");
+ }
+
+ @Test
+ @TestMetadata("stringSwitches.kt")
+ public void testStringSwitches() throws Exception {
+ runTest("compiler/testData/debug/stepping/stringSwitches.kt");
+ }
+
+ @Test
+ @TestMetadata("stringSwitchesSmall.kt")
+ public void testStringSwitchesSmall() throws Exception {
+ runTest("compiler/testData/debug/stepping/stringSwitchesSmall.kt");
+ }
+
+ @Test
+ @TestMetadata("suspendFunWithLambdaParameter.kt")
+ public void testSuspendFunWithLambdaParameter() throws Exception {
+ runTest("compiler/testData/debug/stepping/suspendFunWithLambdaParameter.kt");
+ }
+
+ @Test
+ @TestMetadata("suspendFunWithSuspendLambdaParameter.kt")
+ public void testSuspendFunWithSuspendLambdaParameter() throws Exception {
+ runTest("compiler/testData/debug/stepping/suspendFunWithSuspendLambdaParameter.kt");
+ }
+
+ @Test
+ @TestMetadata("throwException.kt")
+ public void testThrowException() throws Exception {
+ runTest("compiler/testData/debug/stepping/throwException.kt");
+ }
+
+ @Test
+ @TestMetadata("topLevel.kt")
+ public void testTopLevel() throws Exception {
+ runTest("compiler/testData/debug/stepping/topLevel.kt");
+ }
+
+ @Test
+ @TestMetadata("trait.kt")
+ public void testTrait() throws Exception {
+ runTest("compiler/testData/debug/stepping/trait.kt");
+ }
+
+ @Test
+ @TestMetadata("tryCatch.kt")
+ public void testTryCatch() throws Exception {
+ runTest("compiler/testData/debug/stepping/tryCatch.kt");
+ }
+
+ @Test
+ @TestMetadata("tryCatchExpression.kt")
+ public void testTryCatchExpression() throws Exception {
+ runTest("compiler/testData/debug/stepping/tryCatchExpression.kt");
+ }
+
+ @Test
+ @TestMetadata("tryCatchFinally.kt")
+ public void testTryCatchFinally() throws Exception {
+ runTest("compiler/testData/debug/stepping/tryCatchFinally.kt");
+ }
+
+ @Test
+ @TestMetadata("tryFinally.kt")
+ public void testTryFinally() throws Exception {
+ runTest("compiler/testData/debug/stepping/tryFinally.kt");
+ }
+
+ @Test
+ @TestMetadata("variablesWithoutInitializer.kt")
+ public void testVariablesWithoutInitializer() throws Exception {
+ runTest("compiler/testData/debug/stepping/variablesWithoutInitializer.kt");
+ }
+
+ @Test
+ @TestMetadata("voidLambdaStepInline.kt")
+ public void testVoidLambdaStepInline() throws Exception {
+ runTest("compiler/testData/debug/stepping/voidLambdaStepInline.kt");
+ }
+
+ @Test
+ @TestMetadata("when.kt")
+ public void testWhen() throws Exception {
+ runTest("compiler/testData/debug/stepping/when.kt");
+ }
+
+ @Test
+ @TestMetadata("whenComplicatedSubject.kt")
+ public void testWhenComplicatedSubject() throws Exception {
+ runTest("compiler/testData/debug/stepping/whenComplicatedSubject.kt");
+ }
+
+ @Test
+ @TestMetadata("whenConstant.kt")
+ public void testWhenConstant() throws Exception {
+ runTest("compiler/testData/debug/stepping/whenConstant.kt");
+ }
+
+ @Test
+ @TestMetadata("whenIsChecks.kt")
+ public void testWhenIsChecks() throws Exception {
+ runTest("compiler/testData/debug/stepping/whenIsChecks.kt");
+ }
+
+ @Test
+ @TestMetadata("whenMultiLine.kt")
+ public void testWhenMultiLine() throws Exception {
+ runTest("compiler/testData/debug/stepping/whenMultiLine.kt");
+ }
+
+ @Test
+ @TestMetadata("whenMultiLineSubject.kt")
+ public void testWhenMultiLineSubject() throws Exception {
+ runTest("compiler/testData/debug/stepping/whenMultiLineSubject.kt");
+ }
+
+ @Test
+ @TestMetadata("whenNullalbeSubject.kt")
+ public void testWhenNullalbeSubject() throws Exception {
+ runTest("compiler/testData/debug/stepping/whenNullalbeSubject.kt");
+ }
+
+ @Test
+ @TestMetadata("whenSubject.kt")
+ public void testWhenSubject() throws Exception {
+ runTest("compiler/testData/debug/stepping/whenSubject.kt");
+ }
+
+ @Test
+ @TestMetadata("whenSubject2.kt")
+ public void testWhenSubject2() throws Exception {
+ runTest("compiler/testData/debug/stepping/whenSubject2.kt");
+ }
+
+ @Test
+ @TestMetadata("whenWithInlineInCondition.kt")
+ public void testWhenWithInlineInCondition() throws Exception {
+ runTest("compiler/testData/debug/stepping/whenWithInlineInCondition.kt");
+ }
+
+ @Test
+ @TestMetadata("while.kt")
+ public void testWhile() throws Exception {
+ runTest("compiler/testData/debug/stepping/while.kt");
+ }
+}
diff --git a/wasm/wasm.ir/build.gradle.kts b/wasm/wasm.ir/build.gradle.kts
index aaf870d2c59..5730731b91f 100644
--- a/wasm/wasm.ir/build.gradle.kts
+++ b/wasm/wasm.ir/build.gradle.kts
@@ -11,7 +11,7 @@ dependencies {
testCompileOnly(project(":kotlin-test:kotlin-test-jvm"))
testCompileOnly(project(":kotlin-test:kotlin-test-junit"))
testImplementation(projectTests(":compiler:tests-common"))
- testImplementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.0")
+ testImplementation(commonDependency("org.jetbrains.kotlinx", "kotlinx-serialization-json"))
}
@@ -74,4 +74,4 @@ projectTest("test", true) {
workingDir = projectDir
}
-testsJar()
\ No newline at end of file
+testsJar()