From c2b13349bf981315a1b51b902b9b8ddbc909dfa0 Mon Sep 17 00:00:00 2001 From: Iban Eguia Moraza Date: Wed, 12 Apr 2023 11:29:04 +0200 Subject: [PATCH] Added a Boa runtime --- Cargo.lock | 240 +++++++------ Cargo.toml | 4 + boa_cli/Cargo.toml | 3 +- boa_cli/src/main.rs | 13 + boa_engine/Cargo.toml | 3 - boa_engine/src/builtins/function/tests.rs | 3 +- boa_engine/src/builtins/mod.rs | 20 -- boa_engine/src/context/mod.rs | 17 +- boa_engine/src/lib.rs | 45 +-- boa_engine/src/value/display.rs | 90 ++--- boa_engine/src/value/tests.rs | 10 - boa_engine/src/vm/call_frame/mod.rs | 9 + boa_engine/src/vm/code_block.rs | 12 +- boa_examples/Cargo.toml | 3 +- boa_examples/src/bin/classes.rs | 19 +- boa_examples/src/bin/futures.rs | 22 +- boa_examples/src/bin/loadstring.rs | 2 +- boa_runtime/Cargo.toml | 21 ++ .../src/console/mod.rs | 188 +++++----- .../src/console/tests.rs | 24 +- boa_runtime/src/lib.rs | 73 ++++ boa_testing/Cargo.toml | 15 + boa_testing/src/lib.rs | 327 ++++++++++++++++++ boa_wasm/Cargo.toml | 2 +- 24 files changed, 844 insertions(+), 321 deletions(-) create mode 100644 boa_runtime/Cargo.toml rename {boa_engine => boa_runtime}/src/console/mod.rs (85%) rename {boa_engine => boa_runtime}/src/console/tests.rs (77%) create mode 100644 boa_runtime/src/lib.rs create mode 100644 boa_testing/Cargo.toml create mode 100644 boa_testing/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 010bbc8dc9c..c594b741af2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -134,9 +134,9 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b" +checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" dependencies = [ "async-lock", "async-task", @@ -201,9 +201,9 @@ dependencies = [ [[package]] name = "async-process" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6381ead98388605d0d9ff86371043b5aa922a3905824244de40dc263a14fcba4" +checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" dependencies = [ "async-io", "async-lock", @@ -212,9 +212,9 @@ dependencies = [ "cfg-if 1.0.0", "event-listener", "futures-lite", - "libc", + "rustix", "signal-hook", - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] @@ -243,9 +243,9 @@ dependencies = [ [[package]] name = "atomic-waker" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "debc29dde2e69f9e47506b525f639ed42300fc014a3e007832592448fa8e4599" +checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" [[package]] name = "atty" @@ -335,9 +335,9 @@ dependencies = [ [[package]] name = "blocking" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c67b173a56acffd6d2326fb7ab938ba0b00a71480e14902b2591c87bc5741e8" +checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" dependencies = [ "async-channel", "async-lock", @@ -345,6 +345,7 @@ dependencies = [ "atomic-waker", "fastrand", "futures-lite", + "log", ] [[package]] @@ -369,6 +370,7 @@ dependencies = [ "boa_gc", "boa_interner", "boa_parser", + "boa_runtime", "clap 4.2.1", "colored", "jemallocator", @@ -439,6 +441,7 @@ dependencies = [ "boa_gc", "boa_interner", "boa_parser", + "boa_runtime", "futures-util", "smol", ] @@ -486,7 +489,7 @@ version = "0.16.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.14", "synstructure 0.13.0", ] @@ -525,6 +528,17 @@ dependencies = [ "rustc-hash", ] +[[package]] +name = "boa_runtime" +version = "0.16.0" +dependencies = [ + "boa_engine", + "boa_gc", + "boa_testing", + "indoc", + "rustc-hash", +] + [[package]] name = "boa_tester" version = "0.16.0" @@ -548,6 +562,14 @@ dependencies = [ "toml 0.7.3", ] +[[package]] +name = "boa_testing" +version = "0.16.0" +dependencies = [ + "boa_engine", + "textwrap 0.16.0", +] + [[package]] name = "boa_unicode" version = "0.16.0" @@ -775,7 +797,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.14", ] [[package]] @@ -887,9 +909,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c278839b831783b70278b14df4d45e1beb1aad306c07bb796637de9a0e323e8e" +checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" dependencies = [ "crossbeam-utils", ] @@ -924,9 +946,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "corosensei" @@ -1009,9 +1031,9 @@ checksum = "8bd03467c8d43067f6aa5a46bc69bf170debf585f790e9fbe7dfcc03f5b70b29" [[package]] name = "crossbeam-channel" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", @@ -1109,7 +1131,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.13", + "syn 2.0.14", ] [[package]] @@ -1126,7 +1148,7 @@ checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.14", ] [[package]] @@ -1318,9 +1340,9 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "elsa" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f74077c3c3aedb99a2683919698285596662518ea13e5eedcf8bdd43b0d0453b" +checksum = "b5e0aca8dce8856e420195bd13b6a64de3334235ccc9214e824b86b12bf26283" dependencies = [ "stable_deref_trait", ] @@ -1398,13 +1420,13 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -1466,25 +1488,25 @@ dependencies = [ [[package]] name = "fd-lock" -version = "3.0.11" +version = "3.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9799aefb4a2e4a01cc47610b1dd47c18ab13d991f27bbcaed9296f5a53d5cbad" +checksum = "39ae6b3d9530211fb3b12a95374b8b0823be812f53d09e18c5675c0146b09642" dependencies = [ "cfg-if 1.0.0", "rustix", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "filetime" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" +checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" dependencies = [ "cfg-if 1.0.0", "libc", "redox_syscall 0.2.16", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -1559,9 +1581,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", ] @@ -1580,9 +1602,9 @@ checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-lite" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" dependencies = [ "fastrand", "futures-core", @@ -1601,14 +1623,14 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.14", ] [[package]] name = "futures-sink" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" @@ -1874,9 +1896,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.55" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "716f12fbcfac6ffab0a5e9ec51d0a0ff70503742bb2dc7b99396394c9dc323f0" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2390,13 +2412,13 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" dependencies = [ "hermit-abi 0.3.1", "libc", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -2407,14 +2429,14 @@ checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" [[package]] name = "is-terminal" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256017f749ab3117e93acb91063009e1f1bb56d03965b14c2c8df4eb02c524d8" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", "rustix", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -2475,9 +2497,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.140" +version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" [[package]] name = "libloading" @@ -2506,9 +2528,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd550e73688e6d578f0ac2119e32b797a327631a42f9433e59d02e139c8df60d" +checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" [[package]] name = "litemap" @@ -2798,7 +2820,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.14", ] [[package]] @@ -2855,9 +2877,9 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "openssl" -version = "0.10.48" +version = "0.10.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "518915b97df115dd36109bfa429a48b8f737bd05508cf9588977b599648926d2" +checksum = "7e30d8bc91859781f0a943411186324d580f2bbeb71b452fe91ae344806af3f1" dependencies = [ "bitflags 1.3.2", "cfg-if 1.0.0", @@ -2870,13 +2892,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.14", ] [[package]] @@ -2887,11 +2909,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.83" +version = "0.9.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666416d899cf077260dac8698d60a60b435a46d57e82acb1be3d0dad87284e5b" +checksum = "0d3d193fb1488ad46ffe3aaabc912cc931d02ee8518fe2959aea8ef52718b0c0" dependencies = [ - "autocfg", "cc", "libc", "pkg-config", @@ -2912,9 +2933,9 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" [[package]] name = "parking" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" +checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" [[package]] name = "parking_lot" @@ -3069,9 +3090,9 @@ dependencies = [ [[package]] name = "polling" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e1f879b2998099c2d69ab9605d145d5b661195627eccc680002c4918a7fb6fa" +checksum = "4be1c66a6add46bff50935c313dae30a5030cf8385c5206e8a95e9e9def974aa" dependencies = [ "autocfg", "bitflags 1.3.2", @@ -3080,7 +3101,7 @@ dependencies = [ "libc", "log", "pin-project-lite", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -3378,9 +3399,9 @@ dependencies = [ [[package]] name = "rkyv" -version = "0.7.40" +version = "0.7.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c30f1d45d9aa61cbc8cd1eb87705470892289bb2d01943e7803b873a57404dc3" +checksum = "21499ed91807f07ae081880aabb2ccc0235e9d88011867d984525e9a4c3cfa3e" dependencies = [ "bytecheck", "hashbrown 0.12.3", @@ -3392,9 +3413,9 @@ dependencies = [ [[package]] name = "rkyv_derive" -version = "0.7.40" +version = "0.7.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff26ed6c7c4dfc2aa9480b86a60e3c7233543a270a680e10758a507c5a4ce476" +checksum = "ac1c672430eb41556291981f45ca900a0239ad007242d1cb4b4167af842db666" dependencies = [ "proc-macro2", "quote", @@ -3442,16 +3463,16 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.5" +version = "0.37.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e78cc525325c06b4a7ff02db283472f3c042b7ff0c391f96c6d5ac6f4f91b75" +checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77" dependencies = [ "bitflags 1.3.2", "errno", "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -3634,7 +3655,7 @@ checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.14", ] [[package]] @@ -3656,7 +3677,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.14", ] [[package]] @@ -3980,9 +4001,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.13" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" +checksum = "fcf316d5356ed6847742d036f8a39c3b8435cac10bd528a4bd461928a6ab34d5" dependencies = [ "proc-macro2", "quote", @@ -4009,7 +4030,7 @@ checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.14", "unicode-xid", ] @@ -4121,7 +4142,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.14", ] [[package]] @@ -4483,9 +4504,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "unsafe-libyaml" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad2024452afd3874bf539695e04af6732ba06517424dbf958fdb16a01f3bef6c" +checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6" [[package]] name = "url" @@ -4974,11 +4995,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.47.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2649ff315bee4c98757f15dac226efe3d81927adbb6e882084bb1ee3e0c330a7" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets 0.47.0", + "windows-targets 0.48.0", ] [[package]] @@ -5018,6 +5039,15 @@ dependencies = [ "windows-targets 0.42.2", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -5035,17 +5065,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.47.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f8996d3f43b4b2d44327cd71b7b0efd1284ab60e6e9d0e8b630e18555d87d3e" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ - "windows_aarch64_gnullvm 0.47.0", - "windows_aarch64_msvc 0.47.0", - "windows_i686_gnu 0.47.0", - "windows_i686_msvc 0.47.0", - "windows_x86_64_gnu 0.47.0", - "windows_x86_64_gnullvm 0.47.0", - "windows_x86_64_msvc 0.47.0", + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] @@ -5056,9 +5086,9 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.47.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "831d567d53d4f3cb1db332b68e6e2b6260228eb4d99a777d8b2e8ed794027c90" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" @@ -5074,9 +5104,9 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" -version = "0.47.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a42d54a417c60ce4f0e31661eed628f0fa5aca73448c093ec4d45fab4c51cdf" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" @@ -5092,9 +5122,9 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.47.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1925beafdbb22201a53a483db861a5644123157c1c3cee83323a2ed565d71e3" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" @@ -5110,9 +5140,9 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.47.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8ef8f2f1711b223947d9b69b596cf5a4e452c930fb58b6fc3fdae7d0ec6b31" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" @@ -5128,9 +5158,9 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.47.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7acaa0c2cf0d2ef99b61c308a0c3dbae430a51b7345dedec470bd8f53f5a3642" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" @@ -5140,9 +5170,9 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" -version = "0.47.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a0628f71be1d11e17ca4a0e9e15b3a5180f6fbf1c2d55e3ba3f850378052c1" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" @@ -5158,9 +5188,9 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.47.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6e62c256dc6d40b8c8707df17df8d774e60e39db723675241e7c15e910bce7" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" diff --git a/Cargo.toml b/Cargo.toml index cad08fa89b6..06c6c4ad02f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,9 @@ members = [ "boa_macros_tests", "boa_parser", "boa_profiler", + "boa_runtime", "boa_tester", + "boa_testing", "boa_unicode", "boa_wasm", ] @@ -35,6 +37,8 @@ boa_macros = { version = "0.16.0", path = "boa_macros" } boa_ast = { version = "0.16.0", path = "boa_ast" } boa_parser = { version = "0.16.0", path = "boa_parser" } boa_icu_provider = { version = "0.16.0", path = "boa_icu_provider" } +boa_runtime = { version = "0.16.0", path = "boa_runtime" } +boa_testing = { version = "0.16.0", path = "boa_testing" } [workspace.metadata.workspaces] allow_branch = "main" diff --git a/boa_cli/Cargo.toml b/boa_cli/Cargo.toml index ebb5c68ee7a..964b0c94e27 100644 --- a/boa_cli/Cargo.toml +++ b/boa_cli/Cargo.toml @@ -12,11 +12,12 @@ repository.workspace = true rust-version.workspace = true [dependencies] -boa_engine = { workspace = true, features = ["deser", "console", "flowgraph", "trace", "annex-b"] } +boa_engine = { workspace = true, features = ["deser", "flowgraph", "trace", "annex-b"] } boa_ast = { workspace = true, features = ["serde"] } boa_parser.workspace = true boa_gc.workspace = true boa_interner.workspace = true +boa_runtime.workspace = true rustyline = { version = "11.0.0", features = ["derive"]} clap = { version = "4.2.1", features = ["derive"] } serde_json = "1.0.95" diff --git a/boa_cli/src/main.rs b/boa_cli/src/main.rs index 355492723b7..2086f10fe7a 100644 --- a/boa_cli/src/main.rs +++ b/boa_cli/src/main.rs @@ -66,9 +66,11 @@ use boa_engine::{ context::ContextBuilder, job::{FutureJob, JobQueue, NativeJob}, optimizer::OptimizerOptions, + property::Attribute, vm::flowgraph::{Direction, Graph}, Context, JsResult, Source, }; +use boa_runtime::Console; use clap::{Parser, ValueEnum, ValueHint}; use colored::{Color, Colorize}; use debug::init_boa_debug_object; @@ -311,6 +313,9 @@ fn main() -> Result<(), io::Error> { // Strict mode context.strict(args.strict); + // Add `console`. + add_runtime(&mut context); + // Trace Output context.set_trace(args.trace); @@ -404,6 +409,14 @@ fn main() -> Result<(), io::Error> { Ok(()) } +/// Adds the CLI runtime to the context. +fn add_runtime(context: &mut Context<'_>) { + let console = Console::init(context); + context + .register_global_property(Console::NAME, console, Attribute::all()) + .expect("the console object shouldn't exist"); +} + #[derive(Default)] struct Jobs(RefCell>); diff --git a/boa_engine/Cargo.toml b/boa_engine/Cargo.toml index 5d9eab8a3dc..ddb64364e3c 100644 --- a/boa_engine/Cargo.toml +++ b/boa_engine/Cargo.toml @@ -36,9 +36,6 @@ flowgraph = [] # Enable Boa's VM instruction tracing. trace = [] -# Enable Boa's WHATWG console object implementation. -console = [] - # Enable Boa's additional ECMAScript features for web browsers. annex-b = [] diff --git a/boa_engine/src/builtins/function/tests.rs b/boa_engine/src/builtins/function/tests.rs index 9aece8f73fa..ef77c4bb91b 100644 --- a/boa_engine/src/builtins/function/tests.rs +++ b/boa_engine/src/builtins/function/tests.rs @@ -1,5 +1,3 @@ -use indoc::indoc; - use crate::{ builtins::error::ErrorKind, error::JsNativeError, @@ -9,6 +7,7 @@ use crate::{ property::{Attribute, PropertyDescriptor}, run_test_actions, JsValue, TestAction, }; +use indoc::indoc; #[allow(clippy::float_cmp)] #[test] diff --git a/boa_engine/src/builtins/mod.rs b/boa_engine/src/builtins/mod.rs index 3855d8eab61..9c1e9d63547 100644 --- a/boa_engine/src/builtins/mod.rs +++ b/boa_engine/src/builtins/mod.rs @@ -1,6 +1,4 @@ //! Boa's ECMAScript built-in object implementations, e.g. Object, String, Math, Array, etc. -//! -//! This module also contains a JavaScript Console implementation. pub mod array; pub mod array_buffer; @@ -105,9 +103,6 @@ use crate::{ Context, JsResult, JsString, JsValue, }; -#[cfg(feature = "console")] -use crate::console::Console; - /// A [Well-Known Intrinsic Object]. /// /// Well-known intrinsics are built-in objects that are explicitly referenced by the algorithms of @@ -370,21 +365,6 @@ pub(crate) fn set_default_global_bindings(context: &mut Context<'_>) -> JsResult #[cfg(feature = "intl")] global_binding::(context)?; - #[cfg(feature = "console")] - { - let object = Console::init(context); - let global_object = context.global_object(); - global_object.define_property_or_throw( - utf16!("console"), - PropertyDescriptor::builder() - .value(object) - .writable(true) - .enumerable(true) - .configurable(true), - context, - )?; - } - Ok(()) } diff --git a/boa_engine/src/context/mod.rs b/boa_engine/src/context/mod.rs index dfa7bcecf5f..7582e903c7f 100644 --- a/boa_engine/src/context/mod.rs +++ b/boa_engine/src/context/mod.rs @@ -300,6 +300,8 @@ impl<'host> Context<'host> { /// Register a global property. /// + /// It will return an error if the property is already defined. + /// /// # Example /// ``` /// use boa_engine::{ @@ -310,13 +312,17 @@ impl<'host> Context<'host> { /// /// let mut context = Context::default(); /// - /// context.register_global_property("myPrimitiveProperty", 10, Attribute::all()); + /// context + /// .register_global_property("myPrimitiveProperty", 10, Attribute::all()) + /// .expect("property shouldn't exist"); /// /// let object = ObjectInitializer::new(&mut context) /// .property("x", 0, Attribute::all()) /// .property("y", 1, Attribute::all()) /// .build(); - /// context.register_global_property("myObjectProperty", object, Attribute::all()); + /// context + /// .register_global_property("myObjectProperty", object, Attribute::all()) + /// .expect("property shouldn't exist"); /// ``` pub fn register_global_property( &mut self, @@ -411,6 +417,8 @@ impl<'host> Context<'host> { /// Register a global class of type `T`, where `T` implements `Class`. /// + /// It will return an error if the global property is already defined. + /// /// # Example /// ```ignore /// #[derive(Debug, Trace, Finalize)] @@ -515,6 +523,11 @@ impl<'host> Context<'host> { self.kept_alive.clear(); } + /// Retrieves the current stack trace of the context. + pub fn stack_trace(&mut self) -> impl Iterator { + self.vm.frames.iter().rev() + } + /// Replaces the currently active realm with `realm`, and returns the old realm. pub fn enter_realm(&mut self, realm: Realm) -> Realm { self.vm diff --git a/boa_engine/src/lib.rs b/boa_engine/src/lib.rs index 307302d23c7..6515fe3a85c 100644 --- a/boa_engine/src/lib.rs +++ b/boa_engine/src/lib.rs @@ -42,7 +42,6 @@ //! # Crate Features //! //! - **serde** - Enables serialization and deserialization of the AST (Abstract Syntax Tree). -//! - **console** - Enables `boa`'s [WHATWG `console`][whatwg] object implementation. //! - **profiler** - Enables profiling with measureme (this is mostly internal). //! - **intl** - Enables `boa`'s [ECMA-402 Internationalization API][ecma-402] (`Intl` object) //! @@ -56,7 +55,6 @@ //! - **`boa_unicode`** - Boa's Unicode identifier. //! - **`boa_icu_provider`** - Boa's ICU4X data provider. //! -//! [whatwg]: https://console.spec.whatwg.org //! [ecma-402]: https://tc39.es/ecma402 //! [boa-conformance]: https://boajs.dev/boa/test262/ //! [boa-web]: https://boajs.dev/ @@ -151,21 +149,16 @@ pub mod error; pub mod job; pub mod native_function; pub mod object; +pub mod optimizer; pub mod property; pub mod realm; pub mod string; pub mod symbol; -pub mod value; -pub mod vm; - -pub mod optimizer; - -#[cfg(feature = "console")] -pub mod console; - -pub(crate) mod tagged; +mod tagged; #[cfg(test)] mod tests; +pub mod value; +pub mod vm; /// A convenience module that re-exports the most commonly-used Boa APIs pub mod prelude { @@ -225,7 +218,7 @@ use std::borrow::Cow; /// A test action executed in a test function. #[cfg(test)] #[derive(Clone)] -pub(crate) struct TestAction(Inner); +struct TestAction(Inner); #[cfg(test)] #[derive(Clone)] @@ -265,12 +258,12 @@ enum Inner { #[cfg(test)] impl TestAction { /// Evaluates some utility functions used in tests. - pub(crate) const fn run_harness() -> Self { + const fn run_harness() -> Self { Self(Inner::RunHarness) } /// Runs `source`, panicking if the execution throws. - pub(crate) fn run(source: impl Into>) -> Self { + fn run(source: impl Into>) -> Self { Self(Inner::Run { source: source.into(), }) @@ -279,22 +272,19 @@ impl TestAction { /// Executes `op` with the currently active context. /// /// Useful to make custom assertions that must be done from Rust code. - pub(crate) fn inspect_context(op: fn(&mut Context<'_>)) -> Self { + fn inspect_context(op: fn(&mut Context<'_>)) -> Self { Self(Inner::InspectContext { op }) } /// Asserts that evaluating `source` returns the `true` value. - pub(crate) fn assert(source: impl Into>) -> Self { + fn assert(source: impl Into>) -> Self { Self(Inner::Assert { source: source.into(), }) } /// Asserts that the script returns `expected` when evaluating `source`. - pub(crate) fn assert_eq( - source: impl Into>, - expected: impl Into, - ) -> Self { + fn assert_eq(source: impl Into>, expected: impl Into) -> Self { Self(Inner::AssertEq { source: source.into(), expected: expected.into(), @@ -304,7 +294,7 @@ impl TestAction { /// Asserts that calling `op` with the value obtained from evaluating `source` returns `true`. /// /// Useful to check properties of the obtained value that cannot be checked from JS code. - pub(crate) fn assert_with_op( + fn assert_with_op( source: impl Into>, op: fn(JsValue, &mut Context<'_>) -> bool, ) -> Self { @@ -315,7 +305,7 @@ impl TestAction { } /// Asserts that evaluating `source` throws the opaque error `value`. - pub(crate) fn assert_opaque_error( + fn assert_opaque_error( source: impl Into>, value: impl Into, ) -> Self { @@ -326,7 +316,7 @@ impl TestAction { } /// Asserts that evaluating `source` throws a native error of `kind` and `message`. - pub(crate) fn assert_native_error( + fn assert_native_error( source: impl Into>, kind: builtins::error::ErrorKind, message: &'static str, @@ -339,7 +329,7 @@ impl TestAction { } /// Asserts that calling `op` with the currently executing context returns `true`. - pub(crate) fn assert_context(op: fn(&mut Context<'_>) -> bool) -> Self { + fn assert_context(op: fn(&mut Context<'_>) -> bool) -> Self { Self(Inner::AssertContext { op }) } } @@ -347,7 +337,7 @@ impl TestAction { /// Executes a list of test actions on a new, default context. #[cfg(test)] #[track_caller] -pub(crate) fn run_test_actions(actions: impl IntoIterator) { +fn run_test_actions(actions: impl IntoIterator) { let context = &mut Context::default(); run_test_actions_with(actions, context); } @@ -355,10 +345,7 @@ pub(crate) fn run_test_actions(actions: impl IntoIterator) { /// Executes a list of test actions on the provided context. #[cfg(test)] #[track_caller] -pub(crate) fn run_test_actions_with( - actions: impl IntoIterator, - context: &mut Context<'_>, -) { +fn run_test_actions_with(actions: impl IntoIterator, context: &mut Context<'_>) { #[track_caller] fn forward_val(context: &mut Context<'_>, source: &str) -> JsResult { context.eval_script(Source::from_bytes(source)) diff --git a/boa_engine/src/value/display.rs b/boa_engine/src/value/display.rs index ea54b0ffc1c..7050198852e 100644 --- a/boa_engine/src/value/display.rs +++ b/boa_engine/src/value/display.rs @@ -248,67 +248,69 @@ pub(crate) fn log_string_from(x: &JsValue, print_internals: bool, print_children } ) } - _ => display_obj(x, print_internals), + _ => x.display_obj(print_internals), } } _ => x.display().to_string(), } } -/// A helper function for specifically printing object values -pub(crate) fn display_obj(v: &JsValue, print_internals: bool) -> String { - // A simple helper for getting the address of a value - // TODO: Find a more general place for this, as it can be used in other situations as well - fn address_of(t: &T) -> usize { - let my_ptr: *const T = t; - my_ptr as usize - } +impl JsValue { + /// A helper function for specifically printing object values + pub fn display_obj(&self, print_internals: bool) -> String { + // A simple helper for getting the address of a value + // TODO: Find a more general place for this, as it can be used in other situations as well + fn address_of(t: &T) -> usize { + let my_ptr: *const T = t; + my_ptr as usize + } - fn display_obj_internal( - data: &JsValue, - encounters: &mut HashSet, - indent: usize, - print_internals: bool, - ) -> String { - if let JsValue::Object(ref v) = *data { - // The in-memory address of the current object - let addr = address_of(v.as_ref()); + fn display_obj_internal( + data: &JsValue, + encounters: &mut HashSet, + indent: usize, + print_internals: bool, + ) -> String { + if let JsValue::Object(ref v) = *data { + // The in-memory address of the current object + let addr = address_of(v.as_ref()); - // We need not continue if this object has already been - // printed up the current chain - if encounters.contains(&addr) { - return String::from("[Cycle]"); - } + // We need not continue if this object has already been + // printed up the current chain + if encounters.contains(&addr) { + return String::from("[Cycle]"); + } - // Mark the current object as encountered - encounters.insert(addr); + // Mark the current object as encountered + encounters.insert(addr); - let result = if print_internals { - print_obj_value!(all of v, display_obj_internal, indent, encounters).join(",\n") - } else { - print_obj_value!(props of v, display_obj_internal, indent, encounters, print_internals) + let result = if print_internals { + print_obj_value!(all of v, display_obj_internal, indent, encounters).join(",\n") + } else { + print_obj_value!(props of v, display_obj_internal, indent, encounters, print_internals) .join(",\n") - }; + }; - // If the current object is referenced in a different branch, - // it will not cause an infinite printing loop, so it is safe to be printed again - encounters.remove(&addr); + // If the current object is referenced in a different branch, + // it will not cause an infinite printing loop, so it is safe to be printed again + encounters.remove(&addr); - let closing_indent = String::from_utf8(vec![b' '; indent.wrapping_sub(4)]) - .expect("Could not create the closing brace's indentation string"); + let closing_indent = String::from_utf8(vec![b' '; indent.wrapping_sub(4)]) + .expect("Could not create the closing brace's indentation string"); - format!("{{\n{result}\n{closing_indent}}}") - } else { - // Every other type of data is printed with the display method - data.display().to_string() + format!("{{\n{result}\n{closing_indent}}}") + } else { + // Every other type of data is printed with the display method + data.display().to_string() + } } - } - // We keep track of which objects we have encountered by keeping their - // in-memory address in this set - let mut encounters = HashSet::new(); + // We keep track of which objects we have encountered by keeping their + // in-memory address in this set + let mut encounters = HashSet::new(); - display_obj_internal(v, &mut encounters, 4, print_internals) + display_obj_internal(self, &mut encounters, 4, print_internals) + } } impl Display for ValueDisplay<'_> { diff --git a/boa_engine/src/value/tests.rs b/boa_engine/src/value/tests.rs index 823edfc2ff1..b714661efb6 100644 --- a/boa_engine/src/value/tests.rs +++ b/boa_engine/src/value/tests.rs @@ -788,16 +788,6 @@ mod cyclic_conversions { 0.0, )]); } - - #[test] - fn console_log_cyclic() { - run_test_actions([TestAction::run(indoc! {r#" - let a = [1]; - a[1] = a; - console.log(a); - "#})]); - // Should not stack overflow - } } mod abstract_relational_comparison { diff --git a/boa_engine/src/vm/call_frame/mod.rs b/boa_engine/src/vm/call_frame/mod.rs index 7e69fae3fcc..0073917ebf4 100644 --- a/boa_engine/src/vm/call_frame/mod.rs +++ b/boa_engine/src/vm/call_frame/mod.rs @@ -40,6 +40,15 @@ pub struct CallFrame { pub(crate) iterators: ThinVec<(JsObject, bool)>, } +/// ---- `CallFrame` public API ---- +impl CallFrame { + /// Retrieves the [`CodeBlock`] of this call frame. + #[inline] + pub const fn code_block(&self) -> &Gc { + &self.code_block + } +} + /// ---- `CallFrame` creation methods ---- impl CallFrame { /// Creates a new `CallFrame` with the provided `CodeBlock`. diff --git a/boa_engine/src/vm/code_block.rs b/boa_engine/src/vm/code_block.rs index f2c80013420..550d2b2306e 100644 --- a/boa_engine/src/vm/code_block.rs +++ b/boa_engine/src/vm/code_block.rs @@ -139,8 +139,9 @@ pub struct CodeBlock { pub(crate) trace: std::cell::Cell, } +/// ---- `CodeBlock` public API ---- impl CodeBlock { - /// Constructs a new `CodeBlock`. + /// Retrieves the name associated with this code block. #[must_use] pub fn new(name: Sym, length: u32, strict: bool) -> Self { Self { @@ -168,13 +169,22 @@ impl CodeBlock { } } + /// Gets the name of the code block. + #[must_use] + pub const fn name(&self) -> Sym { + self.name + } + /// Enable or disable instruction tracing to `stdout`. #[cfg(feature = "trace")] #[inline] pub fn set_trace(&self, value: bool) { self.trace.set(value); } +} +/// ---- `CodeBlock` private API ---- +impl CodeBlock { /// Read type T from code. /// /// # Safety diff --git a/boa_examples/Cargo.toml b/boa_examples/Cargo.toml index a30bc691d34..fde260b8c0a 100644 --- a/boa_examples/Cargo.toml +++ b/boa_examples/Cargo.toml @@ -10,10 +10,11 @@ repository.workspace = true rust-version.workspace = true [dependencies] -boa_engine = { workspace = true, features = ["console"] } +boa_engine.workspace = true boa_ast.workspace = true boa_interner.workspace = true boa_gc.workspace = true boa_parser.workspace = true +boa_runtime.workspace = true smol = "1.3.0" futures-util = "0.3.28" diff --git a/boa_examples/src/bin/classes.rs b/boa_examples/src/bin/classes.rs index 3782ba87d94..54f6aeca8df 100644 --- a/boa_examples/src/bin/classes.rs +++ b/boa_examples/src/bin/classes.rs @@ -8,6 +8,7 @@ use boa_engine::{ }; use boa_gc::{Finalize, Trace}; +use boa_runtime::Console; // We create a new struct that is going to represent a person. // @@ -124,12 +125,26 @@ impl Class for Person { } } +/// Adds the custom runtime to the context. +fn add_runtime(context: &mut Context<'_>) { + // We first add the `console` object, to be able to call `console.log()`. + let console = Console::init(context); + context + .register_global_property(Console::NAME, console, Attribute::all()) + .expect("the console builtin shouldn't exist"); + + // Then we need to register the global class `Person` inside `context`. + context + .register_global_class::() + .expect("the Person builtin shouldn't exist"); +} + fn main() { // First we need to create a Javascript context. let mut context = Context::default(); - // Then we need to register the global class `Person` inside `context`. - context.register_global_class::().unwrap(); + // Then, we add our custom runtime. + add_runtime(&mut context); // Having done all of that, we can execute Javascript code with `eval`, // and access the `Person` class defined in Rust! diff --git a/boa_examples/src/bin/futures.rs b/boa_examples/src/bin/futures.rs index bbd9b04c2b0..b5cf8b8f592 100644 --- a/boa_examples/src/bin/futures.rs +++ b/boa_examples/src/bin/futures.rs @@ -8,8 +8,10 @@ use boa_engine::{ context::ContextBuilder, job::{FutureJob, JobQueue, NativeJob}, native_function::NativeFunction, + property::Attribute, Context, JsArgs, JsResult, JsValue, Source, }; +use boa_runtime::Console; use futures_util::{stream::FuturesUnordered, Future}; use smol::{future, stream::StreamExt, LocalExecutor}; @@ -128,16 +130,28 @@ fn delay( } } +/// Adds the custom runtime to the context. +fn add_runtime(context: &mut Context<'_>) { + // First add the `console` object, to be able to call `console.log()`. + let console = Console::init(context); + context + .register_global_property(Console::NAME, console, Attribute::all()) + .expect("the console builtin shouldn't exist"); + + // Then, bind the defined async function to the ECMAScript function "delay". + context + .register_global_builtin_callable("delay", 1, NativeFunction::from_async_fn(delay)) + .expect("the delay builtin shouldn't exist"); +} + fn main() { // Initialize the required executors and the context let executor = LocalExecutor::new(); let queue = Queue::new(executor); let context = &mut ContextBuilder::new().job_queue(&queue).build().unwrap(); - // Bind the defined async function to the ECMAScript function "delay". - context - .register_global_builtin_callable("delay", 1, NativeFunction::from_async_fn(delay)) - .unwrap(); + // Then, add a custom runtime. + add_runtime(context); // Multiple calls to multiple async timers. let script = r#" diff --git a/boa_examples/src/bin/loadstring.rs b/boa_examples/src/bin/loadstring.rs index cd394df6f8a..52fc116a6ee 100644 --- a/boa_examples/src/bin/loadstring.rs +++ b/boa_examples/src/bin/loadstring.rs @@ -3,7 +3,7 @@ use boa_engine::{Context, Source}; fn main() { - let js_code = "console.log('Hello World from a JS code string!')"; + let js_code = "'Hello World ' + 'from a JS code ' + 'string!'"; // Instantiate the execution context let mut context = Context::default(); diff --git a/boa_runtime/Cargo.toml b/boa_runtime/Cargo.toml new file mode 100644 index 00000000000..963a4a88241 --- /dev/null +++ b/boa_runtime/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "boa_runtime" +description = "Example runtime for the Boa JavaScript engine." +keywords = ["javascript", "js", "runtime"] +categories = ["command-line-utilities"] +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + + +[dependencies] +boa_engine.workspace = true +boa_gc.workspace = true +rustc-hash = "1.1.0" + +[dev-dependencies] +boa_testing.workspace = true +indoc = "2.0.1" diff --git a/boa_engine/src/console/mod.rs b/boa_runtime/src/console/mod.rs similarity index 85% rename from boa_engine/src/console/mod.rs rename to boa_runtime/src/console/mod.rs index 62db4a88fdb..a699506e1a3 100644 --- a/boa_engine/src/console/mod.rs +++ b/boa_runtime/src/console/mod.rs @@ -16,14 +16,14 @@ #[cfg(test)] mod tests; -use crate::{ +use boa_engine::{ native_function::NativeFunction, object::{JsObject, ObjectInitializer}, - value::{display::display_obj, JsValue, Numeric}, + value::{JsValue, Numeric}, Context, JsArgs, JsResult, JsString, }; use boa_gc::{Finalize, Trace}; -use boa_profiler::Profiler; +// use boa_profiler::Profiler; use rustc_hash::FxHashMap; use std::{cell::RefCell, rc::Rc, time::SystemTime}; @@ -51,7 +51,7 @@ fn logger(msg: LogMessage, console_state: &Console) { } /// This represents the `console` formatter. -pub fn formatter(data: &[JsValue], context: &mut Context<'_>) -> JsResult { +fn formatter(data: &[JsValue], context: &mut Context<'_>) -> JsResult { match data { [] => Ok(String::new()), [val] => Ok(val.to_string(context)?.to_std_string_escaped()), @@ -124,16 +124,18 @@ pub fn formatter(data: &[JsValue], context: &mut Context<'_>) -> JsResult, timer_map: FxHashMap, groups: Vec, } impl Console { - const NAME: &'static str = "console"; + /// Name of the built-in `console` property. + pub const NAME: &'static str = "console"; - pub(crate) fn init(context: &mut Context<'_>) -> JsObject { + /// Initializes the `console` built-in object. + pub fn init(context: &mut Context<'_>) -> JsObject { fn console_method( f: fn(&JsValue, &[JsValue], &Console, &mut Context<'_>) -> JsResult, state: Rc>, @@ -156,11 +158,11 @@ impl Console { }) } } - let _timer = Profiler::global().start_event(Self::NAME, "init"); + // let _timer = Profiler::global().start_event(Self::NAME, "init"); - let state = Rc::new(RefCell::new(Console::default())); + let state = Rc::new(RefCell::new(Self::default())); - ObjectInitializer::with_native(Console::default(), context) + ObjectInitializer::with_native(Self::default(), context) .function(console_method(Self::assert, state.clone()), "assert", 0) .function(console_method_mut(Self::clear, state.clone()), "clear", 0) .function(console_method(Self::debug, state.clone()), "debug", 0) @@ -209,10 +211,10 @@ impl Console { /// /// [spec]: https://console.spec.whatwg.org/#assert /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/assert - pub(crate) fn assert( + fn assert( _: &JsValue, args: &[JsValue], - console: &Console, + console: &Self, context: &mut Context<'_>, ) -> JsResult { let assertion = args.get(0).map_or(false, JsValue::to_boolean); @@ -246,10 +248,10 @@ impl Console { /// [spec]: https://console.spec.whatwg.org/#clear /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/clear #[allow(clippy::unnecessary_wraps)] - pub(crate) fn clear( + fn clear( _: &JsValue, _: &[JsValue], - console: &mut Console, + console: &mut Self, _: &mut Context<'_>, ) -> JsResult { console.groups.clear(); @@ -266,10 +268,10 @@ impl Console { /// /// [spec]: https://console.spec.whatwg.org/#debug /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/debug - pub(crate) fn debug( + fn debug( _: &JsValue, args: &[JsValue], - console: &Console, + console: &Self, context: &mut Context<'_>, ) -> JsResult { logger(LogMessage::Log(formatter(args, context)?), console); @@ -286,10 +288,10 @@ impl Console { /// /// [spec]: https://console.spec.whatwg.org/#error /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/error - pub(crate) fn error( + fn error( _: &JsValue, args: &[JsValue], - console: &Console, + console: &Self, context: &mut Context<'_>, ) -> JsResult { logger(LogMessage::Error(formatter(args, context)?), console); @@ -306,10 +308,10 @@ impl Console { /// /// [spec]: https://console.spec.whatwg.org/#info /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/info - pub(crate) fn info( + fn info( _: &JsValue, args: &[JsValue], - console: &Console, + console: &Self, context: &mut Context<'_>, ) -> JsResult { logger(LogMessage::Info(formatter(args, context)?), console); @@ -326,31 +328,16 @@ impl Console { /// /// [spec]: https://console.spec.whatwg.org/#log /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/log - pub(crate) fn log( + fn log( _: &JsValue, args: &[JsValue], - console: &Console, + console: &Self, context: &mut Context<'_>, ) -> JsResult { logger(LogMessage::Log(formatter(args, context)?), console); Ok(JsValue::undefined()) } - fn get_stack_trace(context: &mut Context<'_>) -> Vec { - let mut stack_trace: Vec = vec![]; - - for frame in context.vm.frames.iter().rev() { - stack_trace.push( - context - .interner() - .resolve_expect(frame.code_block.name) - .to_string(), - ); - } - - stack_trace - } - /// `console.trace(...data)` /// /// Prints a stack trace with "trace" logLevel, optionally labelled by data. @@ -361,16 +348,23 @@ impl Console { /// /// [spec]: https://console.spec.whatwg.org/#trace /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/trace - pub(crate) fn trace( + fn trace( _: &JsValue, args: &[JsValue], - console: &Console, + console: &Self, context: &mut Context<'_>, ) -> JsResult { if !args.is_empty() { logger(LogMessage::Log(formatter(args, context)?), console); - let stack_trace_dump = Self::get_stack_trace(context).join("\n"); + let stack_trace_dump = context + .stack_trace() + .map(|frame| frame.code_block().name()) + .collect::>() + .into_iter() + .map(|s| context.interner().resolve_expect(s).to_string()) + .collect::>() + .join("\n"); logger(LogMessage::Log(stack_trace_dump), console); } @@ -387,10 +381,10 @@ impl Console { /// /// [spec]: https://console.spec.whatwg.org/#warn /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/warn - pub(crate) fn warn( + fn warn( _: &JsValue, args: &[JsValue], - console: &Console, + console: &Self, context: &mut Context<'_>, ) -> JsResult { logger(LogMessage::Warn(formatter(args, context)?), console); @@ -407,10 +401,10 @@ impl Console { /// /// [spec]: https://console.spec.whatwg.org/#count /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/count - pub(crate) fn count( + fn count( _: &JsValue, args: &[JsValue], - console: &mut Console, + console: &mut Self, context: &mut Context<'_>, ) -> JsResult { let label = match args.get(0) { @@ -436,10 +430,10 @@ impl Console { /// /// [spec]: https://console.spec.whatwg.org/#countreset /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/countReset - pub(crate) fn count_reset( + fn count_reset( _: &JsValue, args: &[JsValue], - console: &mut Console, + console: &mut Self, context: &mut Context<'_>, ) -> JsResult { let label = match args.get(0) { @@ -475,10 +469,10 @@ impl Console { /// /// [spec]: https://console.spec.whatwg.org/#time /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/time - pub(crate) fn time( + fn time( _: &JsValue, args: &[JsValue], - console: &mut Console, + console: &mut Self, context: &mut Context<'_>, ) -> JsResult { let label = match args.get(0) { @@ -512,10 +506,10 @@ impl Console { /// /// [spec]: https://console.spec.whatwg.org/#timelog /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/timeLog - pub(crate) fn time_log( + fn time_log( _: &JsValue, args: &[JsValue], - console: &Console, + console: &Self, context: &mut Context<'_>, ) -> JsResult { let label = match args.get(0) { @@ -523,22 +517,25 @@ impl Console { None => "default".into(), }; - if let Some(t) = console.timer_map.get(&label) { - let time = Self::system_time_in_ms(); - let mut concat = format!("{}: {} ms", label.to_std_string_escaped(), time - t); - for msg in args.iter().skip(1) { - concat = concat + " " + &msg.display().to_string(); - } - logger(LogMessage::Log(concat), console); - } else { - logger( - LogMessage::Warn(format!( - "Timer '{}' doesn't exist", - label.to_std_string_escaped() - )), - console, - ); - } + console.timer_map.get(&label).map_or_else( + || { + logger( + LogMessage::Warn(format!( + "Timer '{}' doesn't exist", + label.to_std_string_escaped() + )), + console, + ); + }, + |t| { + let time = Self::system_time_in_ms(); + let mut concat = format!("{}: {} ms", label.to_std_string_escaped(), time - t); + for msg in args.iter().skip(1) { + concat = concat + " " + &msg.display().to_string(); + } + logger(LogMessage::Log(concat), console); + }, + ); Ok(JsValue::undefined()) } @@ -553,10 +550,10 @@ impl Console { /// /// [spec]: https://console.spec.whatwg.org/#timeend /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/timeEnd - pub(crate) fn time_end( + fn time_end( _: &JsValue, args: &[JsValue], - console: &mut Console, + console: &mut Self, context: &mut Context<'_>, ) -> JsResult { let label = match args.get(0) { @@ -564,25 +561,28 @@ impl Console { None => "default".into(), }; - if let Some(t) = console.timer_map.remove(&label) { - let time = Self::system_time_in_ms(); - logger( - LogMessage::Info(format!( - "{}: {} ms - timer removed", - label.to_std_string_escaped(), - time - t - )), - console, - ); - } else { - logger( - LogMessage::Warn(format!( - "Timer '{}' doesn't exist", - label.to_std_string_escaped() - )), - console, - ); - } + console.timer_map.remove(&label).map_or_else( + || { + logger( + LogMessage::Warn(format!( + "Timer '{}' doesn't exist", + label.to_std_string_escaped() + )), + console, + ); + }, + |t| { + let time = Self::system_time_in_ms(); + logger( + LogMessage::Info(format!( + "{}: {} ms - timer removed", + label.to_std_string_escaped(), + time - t + )), + console, + ); + }, + ); Ok(JsValue::undefined()) } @@ -597,10 +597,10 @@ impl Console { /// /// [spec]: https://console.spec.whatwg.org/#group /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/group - pub(crate) fn group( + fn group( _: &JsValue, args: &[JsValue], - console: &mut Console, + console: &mut Self, context: &mut Context<'_>, ) -> JsResult { let group_label = formatter(args, context)?; @@ -622,10 +622,10 @@ impl Console { /// [spec]: https://console.spec.whatwg.org/#groupend /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/groupEnd #[allow(clippy::unnecessary_wraps)] - pub(crate) fn group_end( + fn group_end( _: &JsValue, _: &[JsValue], - console: &mut Console, + console: &mut Self, _: &mut Context<'_>, ) -> JsResult { console.groups.pop(); @@ -644,14 +644,14 @@ impl Console { /// [spec]: https://console.spec.whatwg.org/#dir /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/dir #[allow(clippy::unnecessary_wraps)] - pub(crate) fn dir( + fn dir( _: &JsValue, args: &[JsValue], - console: &Console, + console: &Self, _: &mut Context<'_>, ) -> JsResult { logger( - LogMessage::Info(display_obj(args.get_or_undefined(0), true)), + LogMessage::Info(args.get_or_undefined(0).display_obj(true)), console, ); Ok(JsValue::undefined()) diff --git a/boa_engine/src/console/tests.rs b/boa_runtime/src/console/tests.rs similarity index 77% rename from boa_engine/src/console/tests.rs rename to boa_runtime/src/console/tests.rs index d490e8de587..b24014254a5 100644 --- a/boa_engine/src/console/tests.rs +++ b/boa_runtime/src/console/tests.rs @@ -1,4 +1,7 @@ -use crate::{console::formatter, run_test_actions, JsValue, TestAction}; +use super::{formatter, Console}; +use boa_engine::{property::Attribute, Context, JsValue}; +use boa_testing::{run_test_actions, run_test_actions_with, TestAction}; +use indoc::indoc; #[test] fn formatter_no_args_is_empty_string() { @@ -81,3 +84,22 @@ fn formatter_float_format_works() { ); })]); } + +#[test] +fn console_log_cyclic() { + let mut context = Context::default(); + let console = Console::init(&mut context); + context + .register_global_property(Console::NAME, console, Attribute::all()) + .unwrap(); + + run_test_actions_with( + [TestAction::run(indoc! {r#" + let a = [1]; + a[1] = a; + console.log(a); + "#})], + &mut context, + ); + // Should not stack overflow +} diff --git a/boa_runtime/src/lib.rs b/boa_runtime/src/lib.rs new file mode 100644 index 00000000000..9169b487b07 --- /dev/null +++ b/boa_runtime/src/lib.rs @@ -0,0 +1,73 @@ +//! Example runtime for Boa +//! +//! This crate contains an example runtime for the `boa_engine` crate, so that it can be used as a +//! template for runtime implementors. It contains some basic functionality that can be used by +//! other crates. + +#![doc( + html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg" +)] +#![cfg_attr(not(test), forbid(clippy::unwrap_used))] +#![warn(missing_docs, clippy::dbg_macro)] +#![deny( + // rustc lint groups https://doc.rust-lang.org/rustc/lints/groups.html + warnings, + future_incompatible, + let_underscore, + nonstandard_style, + rust_2018_compatibility, + rust_2018_idioms, + rust_2021_compatibility, + unused, + + // rustc allowed-by-default lints https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html + macro_use_extern_crate, + meta_variable_misuse, + missing_abi, + missing_copy_implementations, + missing_debug_implementations, + non_ascii_idents, + noop_method_call, + single_use_lifetimes, + trivial_casts, + trivial_numeric_casts, + unreachable_pub, + unsafe_op_in_unsafe_fn, + unused_crate_dependencies, + unused_import_braces, + unused_lifetimes, + unused_qualifications, + unused_tuple_struct_fields, + variant_size_differences, + + // rustdoc lints https://doc.rust-lang.org/rustdoc/lints.html + rustdoc::broken_intra_doc_links, + rustdoc::private_intra_doc_links, + rustdoc::missing_crate_level_docs, + rustdoc::private_doc_tests, + rustdoc::invalid_codeblock_attributes, + rustdoc::invalid_rust_codeblocks, + rustdoc::bare_urls, + + // clippy categories https://doc.rust-lang.org/clippy/ + clippy::all, + clippy::correctness, + clippy::suspicious, + clippy::style, + clippy::complexity, + clippy::perf, + clippy::pedantic, + clippy::nursery, + clippy::undocumented_unsafe_blocks +)] +#![allow( + clippy::module_name_repetitions, + clippy::redundant_pub_crate, + clippy::let_unit_value +)] + +mod console; + +#[doc(inline)] +pub use console::Console; diff --git a/boa_testing/Cargo.toml b/boa_testing/Cargo.toml new file mode 100644 index 00000000000..12b4bf7a143 --- /dev/null +++ b/boa_testing/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "boa_testing" +description = "Helper crate for Boa testing." +keywords = ["javascript", "js", "testing"] +publich = false +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +boa_engine.workspace = true +textwrap = "0.16.0" diff --git a/boa_testing/src/lib.rs b/boa_testing/src/lib.rs new file mode 100644 index 00000000000..07f53eded50 --- /dev/null +++ b/boa_testing/src/lib.rs @@ -0,0 +1,327 @@ +//! Helper crate used for testing Boa + +#![doc( + html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg" +)] +#![cfg_attr(not(test), forbid(clippy::unwrap_used))] +#![warn(missing_docs, clippy::dbg_macro)] +#![deny( + // rustc lint groups https://doc.rust-lang.org/rustc/lints/groups.html + warnings, + future_incompatible, + let_underscore, + nonstandard_style, + rust_2018_compatibility, + rust_2018_idioms, + rust_2021_compatibility, + unused, + + // rustc allowed-by-default lints https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html + macro_use_extern_crate, + meta_variable_misuse, + missing_abi, + missing_copy_implementations, + missing_debug_implementations, + non_ascii_idents, + noop_method_call, + single_use_lifetimes, + trivial_casts, + trivial_numeric_casts, + unreachable_pub, + unsafe_op_in_unsafe_fn, + unused_crate_dependencies, + unused_import_braces, + unused_lifetimes, + unused_qualifications, + unused_tuple_struct_fields, + variant_size_differences, + + // rustdoc lints https://doc.rust-lang.org/rustdoc/lints.html + rustdoc::broken_intra_doc_links, + rustdoc::private_intra_doc_links, + rustdoc::missing_crate_level_docs, + rustdoc::private_doc_tests, + rustdoc::invalid_codeblock_attributes, + rustdoc::invalid_rust_codeblocks, + rustdoc::bare_urls, + + // clippy categories https://doc.rust-lang.org/clippy/ + clippy::all, + clippy::correctness, + clippy::suspicious, + clippy::style, + clippy::complexity, + clippy::perf, + clippy::pedantic, + clippy::nursery, + clippy::undocumented_unsafe_blocks +)] +#![allow( + clippy::module_name_repetitions, + clippy::redundant_pub_crate, + clippy::let_unit_value +)] + +use boa_engine::{builtins, Context, JsResult, JsValue, Source}; +use std::borrow::Cow; + +/// A test action executed in a test function. +#[allow(missing_debug_implementations)] +#[derive(Clone)] +pub struct TestAction(Inner); + +#[derive(Clone)] +#[allow(dead_code)] +enum Inner { + RunHarness, + Run { + source: Cow<'static, str>, + }, + InspectContext { + op: fn(&mut Context<'_>), + }, + Assert { + source: Cow<'static, str>, + }, + AssertEq { + source: Cow<'static, str>, + expected: JsValue, + }, + AssertWithOp { + source: Cow<'static, str>, + op: fn(JsValue, &mut Context<'_>) -> bool, + }, + AssertOpaqueError { + source: Cow<'static, str>, + expected: JsValue, + }, + AssertNativeError { + source: Cow<'static, str>, + kind: builtins::error::ErrorKind, + message: &'static str, + }, + AssertContext { + op: fn(&mut Context<'_>) -> bool, + }, +} + +impl TestAction { + /// Evaluates some utility functions used in tests. + pub const fn run_harness() -> Self { + Self(Inner::RunHarness) + } + + /// Runs `source`, panicking if the execution throws. + pub fn run(source: impl Into>) -> Self { + Self(Inner::Run { + source: source.into(), + }) + } + + /// Executes `op` with the currently active context. + /// + /// Useful to make custom assertions that must be done from Rust code. + pub fn inspect_context(op: fn(&mut Context<'_>)) -> Self { + Self(Inner::InspectContext { op }) + } + + /// Asserts that evaluating `source` returns the `true` value. + pub fn assert(source: impl Into>) -> Self { + Self(Inner::Assert { + source: source.into(), + }) + } + + /// Asserts that the script returns `expected` when evaluating `source`. + pub fn assert_eq(source: impl Into>, expected: impl Into) -> Self { + Self(Inner::AssertEq { + source: source.into(), + expected: expected.into(), + }) + } + + /// Asserts that calling `op` with the value obtained from evaluating `source` returns `true`. + /// + /// Useful to check properties of the obtained value that cannot be checked from JS code. + pub fn assert_with_op( + source: impl Into>, + op: fn(JsValue, &mut Context<'_>) -> bool, + ) -> Self { + Self(Inner::AssertWithOp { + source: source.into(), + op, + }) + } + + /// Asserts that evaluating `source` throws the opaque error `value`. + pub fn assert_opaque_error( + source: impl Into>, + value: impl Into, + ) -> Self { + Self(Inner::AssertOpaqueError { + source: source.into(), + expected: value.into(), + }) + } + + /// Asserts that evaluating `source` throws a native error of `kind` and `message`. + pub fn assert_native_error( + source: impl Into>, + kind: builtins::error::ErrorKind, + message: &'static str, + ) -> Self { + Self(Inner::AssertNativeError { + source: source.into(), + kind, + message, + }) + } + + /// Asserts that calling `op` with the currently executing context returns `true`. + pub fn assert_context(op: fn(&mut Context<'_>) -> bool) -> Self { + Self(Inner::AssertContext { op }) + } +} + +/// Executes a list of test actions on a new, default context. +#[track_caller] +pub fn run_test_actions(actions: impl IntoIterator) { + let context = &mut Context::default(); + run_test_actions_with(actions, context); +} + +/// Executes a list of test actions on the provided context. +#[track_caller] +#[allow(clippy::too_many_lines, clippy::missing_panics_doc)] +pub fn run_test_actions_with( + actions: impl IntoIterator, + context: &mut Context<'_>, +) { + #[track_caller] + fn forward_val(context: &mut Context<'_>, source: &str) -> JsResult { + context.eval_script(Source::from_bytes(source)) + } + + #[track_caller] + fn fmt_test(source: &str, test: usize) -> String { + format!( + "\n\nTest case {test}: \n```\n{}\n```", + textwrap::indent(source, " ") + ) + } + + // Some unwrapping patterns look weird because they're replaceable + // by simpler patterns like `unwrap_or_else` or `unwrap_err + let mut i = 1; + for action in actions.into_iter().map(|a| a.0) { + match action { + Inner::RunHarness => { + // add utility functions for testing + // TODO: extract to a file + forward_val( + context, + r#" + function equals(a, b) { + if (Array.isArray(a) && Array.isArray(b)) { + return arrayEquals(a, b); + } + return a === b; + } + function arrayEquals(a, b) { + return Array.isArray(a) && + Array.isArray(b) && + a.length === b.length && + a.every((val, index) => equals(val, b[index])); + } + "#, + ) + .expect("failed to evaluate test harness"); + } + Inner::Run { source } => { + if let Err(e) = forward_val(context, &source) { + panic!("{}\nUncaught {e}", fmt_test(&source, i)); + } + } + Inner::InspectContext { op } => { + op(context); + } + Inner::Assert { source } => { + let val = match forward_val(context, &source) { + Err(e) => panic!("{}\nUncaught {e}", fmt_test(&source, i)), + Ok(v) => v, + }; + let Some(val) = val.as_boolean() else { + panic!( + "{}\nTried to assert with the non-boolean value `{}`", + fmt_test(&source, i), + val.display() + ) + }; + assert!(val, "{}", fmt_test(&source, i)); + i += 1; + } + Inner::AssertEq { source, expected } => { + let val = match forward_val(context, &source) { + Err(e) => panic!("{}\nUncaught {e}", fmt_test(&source, i)), + Ok(v) => v, + }; + assert_eq!(val, expected, "{}", fmt_test(&source, i)); + i += 1; + } + Inner::AssertWithOp { source, op } => { + let val = match forward_val(context, &source) { + Err(e) => panic!("{}\nUncaught {e}", fmt_test(&source, i)), + Ok(v) => v, + }; + assert!(op(val, context), "{}", fmt_test(&source, i)); + i += 1; + } + Inner::AssertOpaqueError { source, expected } => { + let err = match forward_val(context, &source) { + Ok(v) => panic!( + "{}\nExpected error, got value `{}`", + fmt_test(&source, i), + v.display() + ), + Err(e) => e, + }; + let Some(err) = err.as_opaque() else { + panic!("{}\nExpected opaque error, got native error `{}`", fmt_test(&source, i), err) + }; + + assert_eq!(err, &expected, "{}", fmt_test(&source, i)); + i += 1; + } + Inner::AssertNativeError { + source, + kind, + message, + } => { + let err = match forward_val(context, &source) { + Ok(v) => panic!( + "{}\nExpected error, got value `{}`", + fmt_test(&source, i), + v.display() + ), + Err(e) => e, + }; + let native = match err.try_native(context) { + Ok(err) => err, + Err(e) => panic!( + "{}\nCouldn't obtain a native error: {e}", + fmt_test(&source, i) + ), + }; + + assert_eq!(&native.kind, &kind, "{}", fmt_test(&source, i)); + assert_eq!(native.message(), message, "{}", fmt_test(&source, i)); + i += 1; + } + Inner::AssertContext { op } => { + assert!(op(context), "Test case {i}"); + i += 1; + } + } + } +} diff --git a/boa_wasm/Cargo.toml b/boa_wasm/Cargo.toml index df5afbb704e..5cc5dbb6489 100644 --- a/boa_wasm/Cargo.toml +++ b/boa_wasm/Cargo.toml @@ -12,7 +12,7 @@ repository.workspace = true rust-version.workspace = true [dependencies] -boa_engine = { workspace = true, features = ["console", "annex-b"] } +boa_engine = { workspace = true, features = ["annex-b"] } wasm-bindgen = "0.2.84" getrandom = { version = "0.2.9", features = ["js"] } chrono = { version = "0.4.24", features = ["clock", "std", "wasmbind"] }