[JS IR] Implement stepping tests for Kotlin/JS
We already have stepping tests for Kotlin/JVM. They are helpful for testing the correctness of the generated debugging information. They are better than line number tests in that they allow to test the _behavior_, not the generated data. The structure of the data may change even if the behavior stays the same. For that reason, stepping tests are more stable.
This commit is contained in:
+7
@@ -18,6 +18,13 @@
|
||||
</array>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="$PROJECT_DIR$/compiler/testData/debug">
|
||||
<value>
|
||||
<array>
|
||||
<option value="$PROJECT_DIR$/js/js.tests/build/{out,out-min,out-per-module,out-per-module-min}/debug/$TEST_DATA_FILE$_v5.js" />
|
||||
</array>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="$PROJECT_DIR$/js/js.translator/testData/box">
|
||||
<value>
|
||||
<array>
|
||||
|
||||
@@ -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.
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+12
-11
@@ -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
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// FILE: test.kt
|
||||
|
||||
fun box() {
|
||||
|
||||
+8
-7
@@ -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
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// FILE: test.kt
|
||||
|
||||
fun box() {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// FILE: test.kt
|
||||
|
||||
inline fun inlineFun(s: () -> Unit) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// FILE: test.kt
|
||||
inline fun foo(stringMaker: () -> String = { "OK" }): String {
|
||||
return stringMaker()
|
||||
|
||||
@@ -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
|
||||
// test.kt:6 box
|
||||
// test.kt:7 box
|
||||
// test.kt:12 box
|
||||
// test.kt:13 box
|
||||
// test.kt:8 box
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// FILE: test.kt
|
||||
|
||||
fun box() {
|
||||
|
||||
+10
-9
@@ -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
|
||||
// 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
|
||||
|
||||
+25
@@ -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
|
||||
@@ -1,3 +1,4 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// FILE: test.kt
|
||||
|
||||
fun box() {
|
||||
|
||||
@@ -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
|
||||
// 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
|
||||
|
||||
+7
-5
@@ -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
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// WITH_STDLIB
|
||||
// FILE: test.kt
|
||||
suspend fun foo(block: suspend Long.() -> String): String {
|
||||
|
||||
+2
-1
@@ -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
|
||||
// test.kt:38 box
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
@@ -3298,6 +3298,11 @@
|
||||
<sha256 value="5f767682f73d83f8ae7a795973e52b2be2dc7680323b4b20b0e3016efaa33222" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-client-cio" version="2.0.2">
|
||||
<artifact name="ktor-client-cio-metadata-2.0.2-all.jar">
|
||||
<sha256 value="7edefe01e89d1d828788ffabf140f26943ecc6838770fc03fd67d3347110ff6a" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-client-cio-jvm" version="1.4.0">
|
||||
<artifact name="ktor-client-cio-jvm-1.4.0.jar">
|
||||
<md5 value="d2ef7e6b734dbf26f896b77652d60005" origin="Generated by Gradle"/>
|
||||
@@ -3308,6 +3313,11 @@
|
||||
<sha256 value="a6dcc2f55bb9ba9526a5ad4d923404a210109c06746e85f6214c1eda77e14b31" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-client-cio-jvm" version="2.0.2">
|
||||
<artifact name="ktor-client-cio-jvm-2.0.2.jar">
|
||||
<sha256 value="09b215d74b8cbcf2439dc122d9dd50b2dd2ca7f00782a4d287d0a4d3be3acbef" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-client-cio-metadata" version="1.4.0">
|
||||
<artifact name="ktor-client-cio-metadata-1.4.0-all.jar">
|
||||
<md5 value="c8b3a5a119b997fa56238702fb05467d" origin="Generated by Gradle"/>
|
||||
@@ -3344,6 +3354,11 @@
|
||||
<sha256 value="06116f26a94ab0d97c0e4c031aa3d5e3fbd584c242c674d784d4c68bcf1aa844" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-client-core" version="2.0.2">
|
||||
<artifact name="ktor-client-core-metadata-2.0.2-all.jar">
|
||||
<sha256 value="f65236ca290b1dfee2153f80fc7a816295c5ec4aa1f4da43e3e35356d20dfc3a" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-client-core-jvm" version="1.1.5">
|
||||
<artifact name="ktor-client-core-jvm-1.1.5.jar">
|
||||
<md5 value="c38b51041557ac9bf08f92fa8de1ca24" origin="Generated by Gradle"/>
|
||||
@@ -3376,6 +3391,11 @@
|
||||
<sha256 value="375f7a9e23c2b3ad8183c4c77a5cf1fba4173f21239de122a2ecd36771aaafad" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-client-core-jvm" version="2.0.2">
|
||||
<artifact name="ktor-client-core-jvm-2.0.2.jar">
|
||||
<sha256 value="24e2b06aa74fcedae41cb0a18396df85bffda632369456053ea04dcab9ac1395" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-client-core-metadata" version="1.4.0">
|
||||
<artifact name="ktor-client-core-metadata-1.4.0-all.jar">
|
||||
<md5 value="8a47164fa1542ebbfacc8e2bd09b95d7" origin="Generated by Gradle"/>
|
||||
@@ -3414,6 +3434,26 @@
|
||||
<sha256 value="2754e2102c4b64f303e9d7c9a43e676a24a80d4df5bd824563452f306d7d7571" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-client-websockets" version="2.0.2">
|
||||
<artifact name="ktor-client-websockets-metadata-2.0.2-all.jar">
|
||||
<sha256 value="fa3a37277aeb4fcc705c174185550b80910a0734727781507a27ac6ac7d6ddf4" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-client-websockets-jvm" version="2.0.2">
|
||||
<artifact name="ktor-client-websockets-jvm-2.0.2.jar">
|
||||
<sha256 value="2c9d018f8417d54751f65d4ea6d5b22a0a63a2a1e689a82af78197649522ada2" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-events" version="2.0.2">
|
||||
<artifact name="ktor-events-metadata-2.0.2-all.jar">
|
||||
<sha256 value="c5d0c5aba94da03c340a2a54be949cb0f7b114f88afb62da3b39c62f159d11ce" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-events-jvm" version="2.0.2">
|
||||
<artifact name="ktor-events-jvm-2.0.2.jar">
|
||||
<sha256 value="11850bd763a96e6a056620abad0ff1d90b8e7fcbf9049fe4f03fe969e226d74f" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-http" version="1.2.1">
|
||||
<artifact name="ktor-http-1.2.1.jar">
|
||||
<md5 value="769d8aad609ce6770fe8fe06f1df7486" origin="Generated by Gradle"/>
|
||||
@@ -3436,6 +3476,11 @@
|
||||
<sha256 value="e59f1461a59fd344cb54622104b9ce375ba367bef869b59a7e891cffe6f238a7" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-http" version="2.0.2">
|
||||
<artifact name="ktor-http-metadata-2.0.2-all.jar">
|
||||
<sha256 value="f7e0492fc786f39eb4704c35d401887938710c5e11460212b422185f35c1af1a" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-http-cio" version="1.2.1">
|
||||
<artifact name="ktor-http-cio-1.2.1.jar">
|
||||
<md5 value="ef2fbb215004c76e31c695258570d231" origin="Generated by Gradle"/>
|
||||
@@ -3458,6 +3503,11 @@
|
||||
<sha256 value="794417960660de176f8f3e4a98a16ad6b901b82c9af0f450000a80e26707723f" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-http-cio" version="2.0.2">
|
||||
<artifact name="ktor-http-cio-metadata-2.0.2-all.jar">
|
||||
<sha256 value="aac4cda89ae216590e98a5c80850a25715122cdd93dad552296479aaa03a5606" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-http-cio-jvm" version="1.2.1">
|
||||
<artifact name="ktor-http-cio-jvm-1.2.1.jar">
|
||||
<md5 value="eb018202c561a46f81f59df1363a48f8" origin="Generated by Gradle"/>
|
||||
@@ -3484,6 +3534,11 @@
|
||||
<sha256 value="6180664707474b43da3dfa1e9482705e92eed303f18e10f5a67835d8816a6fd7" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-http-cio-jvm" version="2.0.2">
|
||||
<artifact name="ktor-http-cio-jvm-2.0.2.jar">
|
||||
<sha256 value="51c3651fe2a793319ca206a67f5600b50b02dbb494f363b873a8685c18765a55" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-http-cio-metadata" version="1.4.0">
|
||||
<artifact name="ktor-http-cio-metadata-1.4.0-all.jar">
|
||||
<md5 value="9fdc282a3e04d945387bcab878111993" origin="Generated by Gradle"/>
|
||||
@@ -3530,6 +3585,11 @@
|
||||
<sha256 value="4ef6ca6cd6c1f34db0983c4e98fd69c01e6633052f27a34d82a2247f6e7cd6ce" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-http-jvm" version="2.0.2">
|
||||
<artifact name="ktor-http-jvm-2.0.2.jar">
|
||||
<sha256 value="6de626b9e23e47da1c84b268aaa93c76ecf2a781c2b34ffd00903ef8fbc85b94" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-http-metadata" version="1.4.0">
|
||||
<artifact name="ktor-http-metadata-1.4.0-all.jar">
|
||||
<md5 value="023e077c7e623e3fb8e361c679c29a51" origin="Generated by Gradle"/>
|
||||
@@ -3556,6 +3616,11 @@
|
||||
<sha256 value="53f5419c84eec51a19f28e269b65ac440eb9e9abbb7fc914a62b7d50ab257263" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-io" version="2.0.2">
|
||||
<artifact name="ktor-io-metadata-2.0.2-all.jar">
|
||||
<sha256 value="a69af1a281a321ebbe3be956c0865ad8d56188690e9ce574f947fcef36bfaf5a" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-io-jvm" version="1.4.0">
|
||||
<artifact name="ktor-io-jvm-1.4.0.jar">
|
||||
<md5 value="b85950722eab178d0fbbcd2d3280d61b" origin="Generated by Gradle"/>
|
||||
@@ -3572,6 +3637,11 @@
|
||||
<sha256 value="2f9d03cc878c6c1617641dc2434c1011703530a4691b856531af314b3d10ba4d" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-io-jvm" version="2.0.2">
|
||||
<artifact name="ktor-io-jvm-2.0.2.jar">
|
||||
<sha256 value="344963db4b05029f28964fd0778ce5bdf2ba2de02b38e177f964b55c4cc962b5" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-io-metadata" version="1.4.0">
|
||||
<artifact name="ktor-io-metadata-1.4.0-all.jar">
|
||||
<md5 value="32e067a61d5551940472f259136371bd" origin="Generated by Gradle"/>
|
||||
@@ -3618,6 +3688,11 @@
|
||||
<sha256 value="143fe8322cf87193268473329459004bcc3bb4ac933b10e6c1a1b2e05ec431b2" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-network" version="2.0.2">
|
||||
<artifact name="ktor-network-metadata-2.0.2-all.jar">
|
||||
<sha256 value="5937115b5bb974936ab4f11c4acbfcefc0928c0443439eab5dfa1c2d66e8097e" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-network-jvm" version="1.4.0">
|
||||
<artifact name="ktor-network-jvm-1.4.0.jar">
|
||||
<md5 value="ef0bd5a5795a2b1e9c9e7087ecec3b35" origin="Generated by Gradle"/>
|
||||
@@ -3634,6 +3709,11 @@
|
||||
<sha256 value="e33accd7761df57cbc98183aecada06674c9b89c3a42e52a65551ea8875ac33f" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-network-jvm" version="2.0.2">
|
||||
<artifact name="ktor-network-jvm-2.0.2.jar">
|
||||
<sha256 value="dc300cf65e84f779fe93172e483d53533a64d5a8208e666432d7a34bfce246f5" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-network-metadata" version="1.4.0">
|
||||
<artifact name="ktor-network-metadata-1.4.0-all.jar">
|
||||
<md5 value="a5d3b2bc729df64ba8f9c879597d5f24" origin="Generated by Gradle"/>
|
||||
@@ -3670,6 +3750,11 @@
|
||||
<sha256 value="45b3129b4c2c67d9d6cc2d9a1216f24da8f974800c33a7630ecb06eea1ca7017" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-network-tls" version="2.0.2">
|
||||
<artifact name="ktor-network-tls-metadata-2.0.2-all.jar">
|
||||
<sha256 value="ab5ca967a397b985400a53d2e449027b99039a026db8c1077f386b9ed0ce7c03" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-network-tls-jvm" version="1.4.0">
|
||||
<artifact name="ktor-network-tls-jvm-1.4.0.jar">
|
||||
<md5 value="5cc593348b2d3b14afcdcb3a7cc54324" origin="Generated by Gradle"/>
|
||||
@@ -3680,6 +3765,11 @@
|
||||
<sha256 value="a0b2735d410b65451111e28138846dcc730b06b6af09c3dfb2bde7b39953eac2" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-network-tls-jvm" version="2.0.2">
|
||||
<artifact name="ktor-network-tls-jvm-2.0.2.jar">
|
||||
<sha256 value="d3c371e26a037691433f1ac3a13704f7239785e79481245e74b561548067cf59" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-network-tls-metadata" version="1.4.0">
|
||||
<artifact name="ktor-network-tls-metadata-1.4.0-all.jar">
|
||||
<md5 value="1ba382a8ceffbf62b980d10279e3d4e5" origin="Generated by Gradle"/>
|
||||
@@ -3694,6 +3784,16 @@
|
||||
<sha256 value="beaa8a61993acbe4da1c12dd8d9eff3b1604962224ef6995e6016192d9aa55de" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-serialization" version="2.0.2">
|
||||
<artifact name="ktor-serialization-metadata-2.0.2-all.jar">
|
||||
<sha256 value="ace4d7f24c6e20018ed4cebfe38d4b27338cf372568a469d53b9b7658d7f9d27" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-serialization-jvm" version="2.0.2">
|
||||
<artifact name="ktor-serialization-jvm-2.0.2.jar">
|
||||
<sha256 value="43fd805c4586f3830587a608fca28f79291e63c37751be467df95fe67cc640f6" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-server-core" version="1.6.7">
|
||||
<artifact name="ktor-server-core-jvm-1.6.7.jar">
|
||||
<md5 value="37d6440181540fce0bcf1595bee266e3" origin="Generated by Gradle"/>
|
||||
@@ -3774,6 +3874,11 @@
|
||||
<sha256 value="7e401d9327a631e0d85dfdc1a123d57a26998bfb8aa1fe132b53ba0211518b6c" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-utils" version="2.0.2">
|
||||
<artifact name="ktor-utils-metadata-2.0.2-all.jar">
|
||||
<sha256 value="0fd1f674dd2ebd157c4d0ecf0848868f1e9f4482a23d088cb30f97f3bec33cdc" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-utils-jvm" version="1.0.1">
|
||||
<artifact name="ktor-utils-jvm-1.0.1.jar">
|
||||
<md5 value="0e7ec05f9dee3341e2584ce033c27fb9" origin="Generated by Gradle"/>
|
||||
@@ -3816,6 +3921,11 @@
|
||||
<sha256 value="c037d1cb3064cd5d5cdb00fd388aa4b4dd0f4cf4f25e5cd9b0559c3f8043143c" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-utils-jvm" version="2.0.2">
|
||||
<artifact name="ktor-utils-jvm-2.0.2.jar">
|
||||
<sha256 value="bea04d78ace03ef097636fbe615d5f6d35e216a344b3eaf9dcc2ab9ae7d44bff" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-utils-metadata" version="1.4.0">
|
||||
<artifact name="ktor-utils-metadata-1.4.0-all.jar">
|
||||
<md5 value="55510e4e81261fa0fc1c8911aeab3cbd" origin="Generated by Gradle"/>
|
||||
@@ -3830,12 +3940,32 @@
|
||||
<sha256 value="addb052400bea983d75b391a219a5802c9536f722acf61e2065b0a0a0f06a8e1" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-websocket-serialization" version="2.0.2">
|
||||
<artifact name="ktor-websocket-serialization-metadata-2.0.2-all.jar">
|
||||
<sha256 value="bce2ba9c99337e7aa0771180210fcdce5e3a44a755b08904eaaf86afc92915d4" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-websocket-serialization-jvm" version="2.0.2">
|
||||
<artifact name="ktor-websocket-serialization-jvm-2.0.2.jar">
|
||||
<sha256 value="ad9173b667de4477a6c2d5b17b158a14b9ee3028217ba903b4c90c91fb68a44a" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-websockets" version="1.1.5">
|
||||
<artifact name="ktor-websockets-1.1.5.jar">
|
||||
<md5 value="eca3d7a4077aea9f9a1eecf283c9526b" origin="Generated by Gradle"/>
|
||||
<sha256 value="8c0a0f7fd09eb27edf53d909ffcb7348ffbf896b3de4f09a273ece29e37b214b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-websockets" version="2.0.2">
|
||||
<artifact name="ktor-websockets-metadata-2.0.2-all.jar">
|
||||
<sha256 value="8bbfa120ec9b14d433c880dd4d2422aa4ec2857afe7b839f12787ae1b2cc0782" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.ktor" name="ktor-websockets-jvm" version="2.0.2">
|
||||
<artifact name="ktor-websockets-jvm-2.0.2.jar">
|
||||
<sha256 value="a652dbb425230bcb0f5c6b706e55a0c61c7e4bc7bd780132e6b42356a16a2f73" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-buffer" version="4.1.52.Final">
|
||||
<artifact name="netty-buffer-4.1.52.Final.jar">
|
||||
<md5 value="adefee7023c7df6aa3ce1c991be1cc81" origin="Generated by Gradle"/>
|
||||
@@ -7797,6 +7927,11 @@
|
||||
<sha256 value="540250c45b0c88bcd139d537924bfba138f50932503a3aa333e81a9c07a2527b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-core" version="1.6.1">
|
||||
<artifact name="kotlinx-coroutines-core-metadata-1.6.1-all.jar">
|
||||
<sha256 value="67c807c236e19c32020fc5ba82b273fda1e76cad50693f0917efe6071f159db9" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-core-common" version="1.2.0">
|
||||
<artifact name="kotlinx-coroutines-core-common-1.2.0.jar">
|
||||
<md5 value="1dee15621c2e202dca85e5edebbba876" origin="Generated by Gradle"/>
|
||||
@@ -7841,6 +7976,11 @@
|
||||
<sha256 value="db754be65cd22e18c8861a141cc35cedc3b659292eacb7f9d2c78a5386795dec" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-core-jvm" version="1.6.1">
|
||||
<artifact name="kotlinx-coroutines-core-jvm-1.6.1.jar">
|
||||
<sha256 value="3a93ffd052844643c0fef950ae5578db47cccbe9e7176d681333182e232bb0f1" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-core-metadata" version="1.3.8-native-mt-1.4.0-rc">
|
||||
<artifact name="kotlinx-coroutines-core-metadata-1.3.8-native-mt-1.4.0-rc-all.jar">
|
||||
<md5 value="f3a7316b16f20ad0440625e9fb301c34" origin="Generated by Gradle"/>
|
||||
@@ -7939,6 +8079,16 @@
|
||||
<sha256 value="b1b5cfe4e59df8d21f98dee1449214e23ea7617cbfbd77099475762e186bf6a2" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-jdk8" version="1.6.1">
|
||||
<artifact name="kotlinx-coroutines-jdk8-1.6.1.jar">
|
||||
<sha256 value="634aad3c33567570c9bae7fefdadf3da1be57e050103d77d727f3bdb804c1f26" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-slf4j" version="1.6.1">
|
||||
<artifact name="kotlinx-coroutines-slf4j-1.6.1.jar">
|
||||
<sha256 value="1ed76fd7117c2e06b5c842343ab70c507fde1de8a328a81891e9a51ebf306652" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-datetime-jvm" version="0.3.1">
|
||||
<artifact name="kotlinx-datetime-jvm-0.3.1.jar">
|
||||
<md5 value="7fe856499ef7fcc60f4db75ccc753619" origin="Generated by Gradle"/>
|
||||
@@ -8093,6 +8243,11 @@
|
||||
<sha256 value="1aa4d3fc53000f8b2885c0c681d90d183b909e4db995a92b7dc0da30c0e085b4" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-serialization-core" version="1.3.3">
|
||||
<artifact name="kotlinx-serialization-core-metadata-1.3.3-all.jar">
|
||||
<sha256 value="abc222435fa7a892eda76a4a1623ad19792bf5c965426b630c38fb7fdf8a630d" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-serialization-core-jvm" version="1.0.0">
|
||||
<artifact name="kotlinx-serialization-core-jvm-1.0.0.jar">
|
||||
<md5 value="008b4a519c55ce7534359e19f099c274" origin="Generated by Gradle"/>
|
||||
@@ -8133,6 +8288,11 @@
|
||||
<sha256 value="5f220e8bef2b49febc2fd26357d846c7119c87f745491fbe3ab2159455c789bf" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-serialization-core-jvm" version="1.3.3">
|
||||
<artifact name="kotlinx-serialization-core-jvm-1.3.3.jar">
|
||||
<sha256 value="7ef62d1a0114052608d0f541c17b25f1faac17a270d5e26217ac4329ea164752" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-serialization-core-metadata" version="1.0.0">
|
||||
<artifact name="kotlinx-serialization-core-metadata-1.0.0-all.jar">
|
||||
<md5 value="3346a313a80ee63d222a5d2ba6f2b342" origin="Generated by Gradle"/>
|
||||
@@ -8191,6 +8351,11 @@
|
||||
<sha256 value="ec5c5b8be44b08ef62462782863f1fb3b1c6f86990afe07610ab5d6079ef4970" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-serialization-json" version="1.3.3">
|
||||
<artifact name="kotlinx-serialization-json-metadata-1.3.3-all.jar">
|
||||
<sha256 value="ec642685d370f3aff4ae3efb8e441296d90db9f28b688d9ed888b25db760b844" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-serialization-json-jvm" version="1.0.0">
|
||||
<artifact name="kotlinx-serialization-json-jvm-1.0.0.jar">
|
||||
<md5 value="a7a87f36305c8bef0b5225fd3ca6bccf" origin="Generated by Gradle"/>
|
||||
@@ -8217,6 +8382,11 @@
|
||||
<sha256 value="57b22b0342a639eebaa63308e0cb72d59ffffcbc9f63d324cb0733b8782a020b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-serialization-json-jvm" version="1.3.3">
|
||||
<artifact name="kotlinx-serialization-json-jvm-1.3.3.jar">
|
||||
<sha256 value="2a0dd7c162c3cc6d713610b81d809bd5976a140dc9efb1eb7152caf093509965" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-serialization-json-metadata" version="1.0.0">
|
||||
<artifact name="kotlinx-serialization-json-metadata-1.0.0-all.jar">
|
||||
<md5 value="5927717cf9292fde25f735dbbc18af5a" origin="Generated by Gradle"/>
|
||||
|
||||
@@ -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
|
||||
versions.v8=10.2.9
|
||||
|
||||
@@ -21,6 +21,9 @@ import java.io.*
|
||||
class SourceMap(val sourceContentResolver: (String) -> Reader?) {
|
||||
val groups = mutableListOf<SourceMapGroup>()
|
||||
|
||||
@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}:")
|
||||
|
||||
@@ -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"))
|
||||
|
||||
|
||||
@@ -149,6 +149,10 @@ fun main(args: Array<String>) {
|
||||
model("codegen/boxWasmJsInterop")
|
||||
}
|
||||
|
||||
testClass<AbstractIrJsSteppingTest> {
|
||||
model("debug/stepping")
|
||||
}
|
||||
|
||||
testClass<AbstractFir2IrJsTextTest>(
|
||||
suiteTestClassName = "Fir2IrJsTextTestGenerated"
|
||||
) {
|
||||
|
||||
@@ -41,6 +41,18 @@ abstract class AbstractJsBlackBoxCodegenTestBase<R : ResultingArtifact.FrontendO
|
||||
abstract val recompileFacade: Constructor<AbstractTestFacade<BinaryArtifacts.Js, BinaryArtifacts.Js>>
|
||||
|
||||
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<R : ResultingArtifact.FrontendO
|
||||
afterBackendFacade?.let { facadeStep(it) }
|
||||
facadeStep(recompileFacade)
|
||||
jsArtifactsHandlersStep {
|
||||
useHandlers(
|
||||
::NodeJsGeneratorHandler,
|
||||
::JsBoxRunner,
|
||||
::JsMinifierRunner,
|
||||
::JsAstHandler,
|
||||
::JsSourceMapPathRewriter
|
||||
)
|
||||
useHandlers(::JsSourceMapPathRewriter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,987 @@
|
||||
/*
|
||||
* 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 kotlinx.serialization.*
|
||||
import kotlinx.serialization.json.*
|
||||
|
||||
/**
|
||||
* Represents an incoming [Chrome DevTools protocol](https://chromedevtools.github.io/devtools-protocol/) message,
|
||||
* which can be:
|
||||
* - an event
|
||||
* - an method invocation result
|
||||
* - an error, if a method invocation failed.
|
||||
*/
|
||||
sealed class CDPResponse {
|
||||
|
||||
/**
|
||||
* Indicates that a CDP method was invoked successfully.
|
||||
*
|
||||
* [id] is a message id with which the method was invoked, [result] is the result of the invocation.
|
||||
*/
|
||||
class MethodInvocationResult(val id: Int, val result: CDPMethodInvocationResult) : CDPResponse()
|
||||
|
||||
/**
|
||||
* Indicates that a CDP method invocation failed.
|
||||
*
|
||||
* [id] is a message id with which the method was invoked, [error] is the failure information.
|
||||
*/
|
||||
class Error(val id: Int, val error: CDPError) : CDPResponse()
|
||||
|
||||
/**
|
||||
* Indicates an event received from CDP. Events are not tied to any method invocations.
|
||||
*/
|
||||
class Event(val event: CDPEvent) : CDPResponse()
|
||||
}
|
||||
|
||||
private val json = Json {
|
||||
ignoreUnknownKeys = true
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes an instance of [CDPResponse] from JSON-encoded [message].
|
||||
*
|
||||
* [serializerForMessageId] is used for retrieving additional information about how to decode the response for the given message id.
|
||||
*/
|
||||
fun decodeCDPResponse(
|
||||
message: String,
|
||||
serializerForMessageId: (Int) -> 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<out CDPMethodInvocationResult>
|
||||
) : 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 <reified Response : CDPMethodInvocationResult, reified Params : CDPRequestParams> encodeCDPMethodCall(
|
||||
messageId: Int,
|
||||
methodName: String,
|
||||
params: Params?
|
||||
): Pair<String, CDPMethodCallEncodingInfo> {
|
||||
val request = CDPRequest(messageId, methodName, params)
|
||||
return json.encodeToString(request) to
|
||||
CDPMethodCallEncodingInfoImpl(json.serializersModule.serializer<Response>())
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<Params : CDPRequestParams>(
|
||||
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<String, CDPMethodCallEncodingInfo>
|
||||
): CDPMethodInvocationResult
|
||||
}
|
||||
|
||||
private suspend inline fun <reified T : CDPMethodInvocationResult> CDPRequestEvaluator.evaluateRequest(
|
||||
noinline body: (Int) -> Pair<String, CDPMethodCallEncodingInfo>
|
||||
) = 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<CDPMethodInvocationResultUnit> { messageId ->
|
||||
encodeCDPMethodCall<CDPMethodInvocationResultUnit, CDPRequestParamsUnit>(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<CDPMethodInvocationResultUnit> { messageId ->
|
||||
encodeCDPMethodCall<CDPMethodInvocationResultUnit, CDPRequestParamsUnit>(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<EvaluationResult> { messageId ->
|
||||
encodeCDPMethodCall<EvaluationResult, EvaluateRequestParams>(
|
||||
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<CallFrame>,
|
||||
|
||||
/**
|
||||
* 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<CDPMethodInvocationResultUnit> { messageId ->
|
||||
encodeCDPMethodCall<CDPMethodInvocationResultUnit, CDPRequestParamsUnit>(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<CDPMethodInvocationResultUnit> { messageId ->
|
||||
encodeCDPMethodCall<CDPMethodInvocationResultUnit, ResumeRequestParams>(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<Location>
|
||||
) : 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<SetBreakpointByUrlResult> { messageId ->
|
||||
encodeCDPMethodCall<SetBreakpointByUrlResult, SetBreakpointByUrlRequestParams>(
|
||||
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<SetBreakpointResult> { messageId ->
|
||||
encodeCDPMethodCall<SetBreakpointResult, SetBreakpointRequestParams>(
|
||||
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<CDPMethodInvocationResultUnit> { messageId ->
|
||||
encodeCDPMethodCall<CDPMethodInvocationResultUnit, SetSkipAllPausesRequestParams>(
|
||||
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<CDPMethodInvocationResultUnit> { messageId ->
|
||||
encodeCDPMethodCall<CDPMethodInvocationResultUnit, CDPRequestParamsUnit>(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<Runtime.EvaluationResult> { messageId ->
|
||||
encodeCDPMethodCall<Runtime.EvaluationResult, EvaluateOnCallFrameRequestParams>(
|
||||
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<CallFrame>,
|
||||
/**
|
||||
* Pause reason.
|
||||
*/
|
||||
val reason: PauseReason,
|
||||
|
||||
/**
|
||||
* Hit breakpoints IDs
|
||||
*/
|
||||
val hitBreakpoints: List<BreakpointId> = 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()
|
||||
}
|
||||
}
|
||||
@@ -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<String>) {
|
||||
|
||||
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 <T> run(block: suspend NodeJsInspectorClientContext.() -> T): T = runBlocking {
|
||||
val context = NodeJsInspectorClientContextImpl(this@NodeJsInspectorClient)
|
||||
try {
|
||||
runWithContext(context, block)
|
||||
} finally {
|
||||
context.release()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun <T> runWithContext(
|
||||
context: NodeJsInspectorClientContextImpl,
|
||||
block: suspend NodeJsInspectorClientContext.() -> T
|
||||
): T {
|
||||
context.startWebsocketSession()
|
||||
|
||||
var blockResult: Result<T>? = null
|
||||
block.startCoroutine(context, object : Continuation<T> {
|
||||
override val context: CoroutineContext
|
||||
get() = EmptyCoroutineContext
|
||||
|
||||
override fun resumeWith(result: Result<T>) {
|
||||
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<Int, Pair<CDPMethodCallEncodingInfo, Continuation<CDPMethodInvocationResult>>>()
|
||||
|
||||
/**
|
||||
* See [waitForConditionToBecomeTrue].
|
||||
*/
|
||||
var waitingOnPredicate: Pair<(() -> Boolean), Continuation<Unit>>? = 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<CDPMethodInvocationResultPlainText>().string
|
||||
}
|
||||
|
||||
override suspend fun genericEvaluateRequest(
|
||||
encodeMethodCallWithMessageId: (Int) -> Pair<String, CDPMethodCallEncodingInfo>
|
||||
): 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("\"", "\\\"")}\"" }
|
||||
+45
@@ -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 <T> NodeJsInspectorClientContext.waitForValueToBecomeNonNull(crossinline test: () -> T?): T {
|
||||
var value: T? = null
|
||||
waitForConditionToBecomeTrue {
|
||||
test()?.also {
|
||||
value = it
|
||||
} != null
|
||||
}
|
||||
return value!!
|
||||
}
|
||||
@@ -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<SteppingTestLoggedData>()
|
||||
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.<anonymous>' 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<SteppingTestLoggedData>
|
||||
) {
|
||||
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<TestFile, Int> {
|
||||
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<Int, SourceMapSegment>? = 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<String, Int>? {
|
||||
|
||||
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<Runtime.ScriptId, String>()
|
||||
|
||||
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 <T> 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)
|
||||
@@ -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"
|
||||
|
||||
@@ -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<KtNamedFunction> { 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
|
||||
|
||||
+585
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
testsJar()
|
||||
|
||||
Reference in New Issue
Block a user