diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 27fe2d8d89..e8611d21e4 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -22,14 +22,18 @@ jobs: toolchain: stable override: true profile: minimal + - uses: actions-rs/install@v0.1 + with: + crate: cargo-tarpaulin + version: latest - uses: Swatinem/rust-cache@v2 with: key: tarpaulin - - name: Run cargo-tarpaulin - uses: actions-rs/tarpaulin@v0.1 + - name: Run tarpaulin + uses: actions-rs/cargo@v1 with: - version: 0.22.0 - args: --features intl --ignore-tests --engine llvm + command: tarpaulin + args: --workspace --features intl --ignore-tests --engine llvm --out Xml - name: Upload to codecov.io uses: codecov/codecov-action@v3 diff --git a/Cargo.lock b/Cargo.lock index aba2e8a35a..fb46210f29 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -160,22 +160,22 @@ dependencies = [ [[package]] name = "async-io" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c374dda1ed3e7d8f0d9ba58715f924862c63eae6849c92d3a18e7fbde9e2794" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ "async-lock", "autocfg", + "cfg-if 1.0.0", "concurrent-queue", "futures-lite", - "libc", "log", "parking", "polling", + "rustix", "slab", "socket2", "waker-fn", - "windows-sys 0.42.0", ] [[package]] @@ -219,9 +219,9 @@ dependencies = [ [[package]] name = "async-task" -version = "4.3.0" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" +checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" [[package]] name = "atomic-polyfill" @@ -291,6 +291,15 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +[[package]] +name = "basic-toml" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c0de75129aa8d0cceaf750b89013f0e08804d6ec61416da787b35ad0d7cddf1" +dependencies = [ + "serde", +] + [[package]] name = "bincode" version = "1.3.3" @@ -479,6 +488,15 @@ dependencies = [ "synstructure 0.13.0", ] +[[package]] +name = "boa_macros_tests" +version = "0.16.0" +dependencies = [ + "boa_engine", + "boa_macros", + "trybuild", +] + [[package]] name = "boa_parser" version = "0.16.0" @@ -741,7 +759,7 @@ dependencies = [ "anstream", "anstyle", "bitflags 1.3.2", - "clap_lex 0.4.0", + "clap_lex 0.4.1", "strsim 0.10.0", ] @@ -768,9 +786,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f0807fb6f644c83f3e4ec014fec9858c1c8b26a7db8eb5f0bde5817df9c1df7" +checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" [[package]] name = "clipboard-win" @@ -922,9 +940,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" dependencies = [ "libc", ] @@ -1066,9 +1084,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c00419335c41018365ddf7e4d5f1c12ee3659ddcf3e01974650ba1de73d038" +checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" dependencies = [ "cc", "cxxbridge-flags", @@ -1078,9 +1096,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb8307ad413a98fff033c8545ecf133e3257747b3bae935e7602aab8aa92d4ca" +checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" dependencies = [ "cc", "codespan-reporting", @@ -1093,15 +1111,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc52e2eb08915cb12596d29d55f0b5384f00d697a646dbd269b6ecb0fbd9d31" +checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" [[package]] name = "cxxbridge-macro" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "631569015d0d8d54e6c241733f944042623ab6df7bc3be7466874b05fcdb1c5f" +checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" dependencies = [ "proc-macro2", "quote", @@ -1377,13 +1395,13 @@ dependencies = [ [[package]] name = "errno" -version = "0.2.8" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" dependencies = [ "errno-dragonfly", "libc", - "winapi", + "windows-sys 0.45.0", ] [[package]] @@ -1445,9 +1463,9 @@ dependencies = [ [[package]] name = "fd-lock" -version = "3.0.10" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ef1a30ae415c3a691a4f41afddc2dbcd6d70baf338368d85ebc1e8ed92cedb9" +checksum = "9799aefb4a2e4a01cc47610b1dd47c18ab13d991f27bbcaed9296f5a53d5cbad" dependencies = [ "cfg-if 1.0.0", "rustix", @@ -1462,7 +1480,7 @@ checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "windows-sys 0.45.0", ] @@ -1631,9 +1649,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -1853,16 +1871,16 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "716f12fbcfac6ffab0a5e9ec51d0a0ff70503742bb2dc7b99396394c9dc323f0" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi", + "windows", ] [[package]] @@ -2380,15 +2398,15 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" [[package]] name = "is-terminal" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8687c819457e979cc940d09cb16e42a1bf70aa6b60a549de6d3a62a0ee90c69e" +checksum = "256017f749ab3117e93acb91063009e1f1bb56d03965b14c2c8df4eb02c524d8" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", @@ -2485,9 +2503,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.1.4" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "cd550e73688e6d578f0ac2119e32b797a327631a42f9433e59d02e139c8df60d" [[package]] name = "litemap" @@ -2925,7 +2943,7 @@ dependencies = [ "cfg-if 1.0.0", "instant", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "winapi", ] @@ -2938,7 +2956,7 @@ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "windows-sys 0.45.0", ] @@ -3240,6 +3258,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -3247,7 +3274,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom", - "redox_syscall", + "redox_syscall 0.2.16", "thiserror", ] @@ -3311,9 +3338,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.15" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ba30cc2c0cd02af1222ed216ba659cdb2f879dfe3181852fe7c50b1d0005949" +checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" dependencies = [ "base64", "bytes", @@ -3382,9 +3409,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b" [[package]] name = "rustc-hash" @@ -3412,9 +3439,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.11" +version = "0.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" +checksum = "0e78cc525325c06b4a7ff02db283472f3c042b7ff0c391f96c6d5ac6f4f91b75" dependencies = [ "bitflags 1.3.2", "errno", @@ -3817,9 +3844,9 @@ dependencies = [ [[package]] name = "spin" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5d6e0250b93c8427a177b849d144a96d5acc57006149479403d7861ab721e34" +checksum = "c0959fd6f767df20b231736396e4f602171e00d95205676286e79d4a4eb67bef" dependencies = [ "lock_api", ] @@ -4031,15 +4058,15 @@ checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" [[package]] name = "tempfile" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if 1.0.0", "fastrand", - "redox_syscall", + "redox_syscall 0.3.5", "rustix", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -4230,14 +4257,13 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.26.0" +version = "1.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" dependencies = [ "autocfg", "bytes", "libc", - "memchr", "mio", "num_cpus", "pin-project-lite", @@ -4301,9 +4327,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.19.7" +version = "0.19.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc18466501acd8ac6a3f615dd29a3438f8ca6bb3b19537138b3106e575621274" +checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" dependencies = [ "indexmap", "serde", @@ -4379,6 +4405,21 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +[[package]] +name = "trybuild" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501dbdbb99861e4ab6b60eb6a7493956a9defb644fd034bc4a5ef27c693c8a3a" +dependencies = [ + "basic-toml", + "glob", + "once_cell", + "serde", + "serde_derive", + "serde_json", + "termcolor", +] + [[package]] name = "typenum" version = "1.16.0" @@ -4931,6 +4972,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.47.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2649ff315bee4c98757f15dac226efe3d81927adbb6e882084bb1ee3e0c330a7" +dependencies = [ + "windows-targets 0.47.0", +] + [[package]] name = "windows-sys" version = "0.33.0" @@ -4950,12 +5000,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.42.2", "windows_aarch64_msvc 0.42.2", "windows_i686_gnu 0.42.2", "windows_i686_msvc 0.42.2", "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm", + "windows_x86_64_gnullvm 0.42.2", "windows_x86_64_msvc 0.42.2", ] @@ -4965,7 +5015,7 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", ] [[package]] @@ -4974,21 +5024,42 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.42.2", "windows_aarch64_msvc 0.42.2", "windows_i686_gnu 0.42.2", "windows_i686_msvc 0.42.2", "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm", + "windows_x86_64_gnullvm 0.42.2", "windows_x86_64_msvc 0.42.2", ] +[[package]] +name = "windows-targets" +version = "0.47.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f8996d3f43b4b2d44327cd71b7b0efd1284ab60e6e9d0e8b630e18555d87d3e" +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", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.47.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831d567d53d4f3cb1db332b68e6e2b6260228eb4d99a777d8b2e8ed794027c90" + [[package]] name = "windows_aarch64_msvc" version = "0.33.0" @@ -5001,6 +5072,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.47.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a42d54a417c60ce4f0e31661eed628f0fa5aca73448c093ec4d45fab4c51cdf" + [[package]] name = "windows_i686_gnu" version = "0.33.0" @@ -5013,6 +5090,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.47.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1925beafdbb22201a53a483db861a5644123157c1c3cee83323a2ed565d71e3" + [[package]] name = "windows_i686_msvc" version = "0.33.0" @@ -5025,6 +5108,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.47.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8ef8f2f1711b223947d9b69b596cf5a4e452c930fb58b6fc3fdae7d0ec6b31" + [[package]] name = "windows_x86_64_gnu" version = "0.33.0" @@ -5037,12 +5126,24 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.47.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acaa0c2cf0d2ef99b61c308a0c3dbae430a51b7345dedec470bd8f53f5a3642" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.47.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a0628f71be1d11e17ca4a0e9e15b3a5180f6fbf1c2d55e3ba3f850378052c1" + [[package]] name = "windows_x86_64_msvc" version = "0.33.0" @@ -5055,11 +5156,17 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.47.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6e62c256dc6d40b8c8707df17df8d774e60e39db723675241e7c15e910bce7" + [[package]] name = "winnow" -version = "0.3.6" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d020b441f92996c80d94ae9166e8501e59c7bb56121189dc9eab3bd8216966" +checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 0af692a1c1..cad08fa89b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,18 +1,19 @@ [workspace] members = [ + "boa_ast", "boa_cli", "boa_engine", - "boa_ast", - "boa_parser", + "boa_examples", "boa_gc", + "boa_icu_provider", "boa_interner", + "boa_macros", + "boa_macros_tests", + "boa_parser", "boa_profiler", "boa_tester", "boa_unicode", "boa_wasm", - "boa_examples", - "boa_macros", - "boa_icu_provider", ] [workspace.package] diff --git a/boa_engine/src/value/conversions.rs b/boa_engine/src/value/conversions/mod.rs similarity index 64% rename from boa_engine/src/value/conversions.rs rename to boa_engine/src/value/conversions/mod.rs index 8a8335cec5..b90c080ef8 100644 --- a/boa_engine/src/value/conversions.rs +++ b/boa_engine/src/value/conversions/mod.rs @@ -1,5 +1,10 @@ +//! Conversions from JavaScript values into Rust values, and the other way around. + use super::{JsBigInt, JsObject, JsString, JsSymbol, JsValue, Profiler}; +mod serde_json; +pub(super) mod try_from_js; + impl From for JsValue where T: Into, @@ -14,6 +19,8 @@ where impl From for JsValue { #[inline] fn from(value: char) -> Self { + let _timer = Profiler::global().start_event("From", "value"); + Self::new(value.to_string()) } } @@ -21,37 +28,35 @@ impl From for JsValue { impl From for JsValue { #[inline] fn from(value: JsSymbol) -> Self { + let _timer = Profiler::global().start_event("From", "value"); + Self::Symbol(value) } } impl From for JsValue { - #[allow(clippy::float_cmp)] #[inline] fn from(value: f32) -> Self { - // if value as i32 as f64 == value { - // Self::Integer(value as i32) - // } else { + let _timer = Profiler::global().start_event("From", "value"); + Self::Rational(value.into()) - // } } } impl From for JsValue { - #[allow(clippy::float_cmp)] #[inline] fn from(value: f64) -> Self { - // if value as i32 as f64 == value { - // Self::Integer(value as i32) - // } else { + let _timer = Profiler::global().start_event("From", "value"); + Self::Rational(value) - // } } } impl From for JsValue { #[inline] fn from(value: u8) -> Self { + let _timer = Profiler::global().start_event("From", "value"); + Self::Integer(value.into()) } } @@ -59,6 +64,8 @@ impl From for JsValue { impl From for JsValue { #[inline] fn from(value: i8) -> Self { + let _timer = Profiler::global().start_event("From", "value"); + Self::Integer(value.into()) } } @@ -66,6 +73,8 @@ impl From for JsValue { impl From for JsValue { #[inline] fn from(value: u16) -> Self { + let _timer = Profiler::global().start_event("From", "value"); + Self::Integer(value.into()) } } @@ -73,6 +82,8 @@ impl From for JsValue { impl From for JsValue { #[inline] fn from(value: i16) -> Self { + let _timer = Profiler::global().start_event("From", "value"); + Self::Integer(value.into()) } } @@ -80,6 +91,8 @@ impl From for JsValue { impl From for JsValue { #[inline] fn from(value: u32) -> Self { + let _timer = Profiler::global().start_event("From", "value"); + i32::try_from(value).map_or_else(|_| Self::Rational(value.into()), Self::Integer) } } @@ -87,6 +100,8 @@ impl From for JsValue { impl From for JsValue { #[inline] fn from(value: i32) -> Self { + let _timer = Profiler::global().start_event("From", "value"); + Self::Integer(value) } } @@ -94,6 +109,8 @@ impl From for JsValue { impl From for JsValue { #[inline] fn from(value: JsBigInt) -> Self { + let _timer = Profiler::global().start_event("From", "value"); + Self::BigInt(value) } } @@ -101,6 +118,8 @@ impl From for JsValue { impl From for JsValue { #[inline] fn from(value: usize) -> Self { + let _timer = Profiler::global().start_event("From", "value"); + i32::try_from(value).map_or(Self::Rational(value as f64), Self::Integer) } } @@ -108,6 +127,8 @@ impl From for JsValue { impl From for JsValue { #[inline] fn from(value: u64) -> Self { + let _timer = Profiler::global().start_event("From", "value"); + i32::try_from(value).map_or(Self::Rational(value as f64), Self::Integer) } } @@ -115,6 +136,8 @@ impl From for JsValue { impl From for JsValue { #[inline] fn from(value: i64) -> Self { + let _timer = Profiler::global().start_event("From", "value"); + i32::try_from(value).map_or(Self::Rational(value as f64), Self::Integer) } } @@ -122,6 +145,8 @@ impl From for JsValue { impl From for JsValue { #[inline] fn from(value: bool) -> Self { + let _timer = Profiler::global().start_event("From", "value"); + Self::Boolean(value) } } @@ -130,6 +155,7 @@ impl From for JsValue { #[inline] fn from(object: JsObject) -> Self { let _timer = Profiler::global().start_event("From", "value"); + Self::Object(object) } } @@ -137,11 +163,18 @@ impl From for JsValue { impl From<()> for JsValue { #[inline] fn from(_: ()) -> Self { + let _timer = Profiler::global().start_event("From<()>", "value"); + Self::null() } } +/// Converts an `Option` into a `JsValue`. +/// +/// It will convert the `None` variant to `JsValue::undefined()`, and the `Some()` variant into a +/// `JsValue` using the `Into` trait. pub(crate) trait IntoOrUndefined { + /// Converts an `Option` into a `JsValue`. fn into_or_undefined(self) -> JsValue; } @@ -149,6 +182,7 @@ impl IntoOrUndefined for Option where T: Into, { + #[inline] fn into_or_undefined(self) -> JsValue { self.map_or_else(JsValue::undefined, Into::into) } diff --git a/boa_engine/src/value/serde_json.rs b/boa_engine/src/value/conversions/serde_json.rs similarity index 100% rename from boa_engine/src/value/serde_json.rs rename to boa_engine/src/value/conversions/serde_json.rs diff --git a/boa_engine/src/value/conversions/try_from_js.rs b/boa_engine/src/value/conversions/try_from_js.rs new file mode 100644 index 0000000000..bfd44936cb --- /dev/null +++ b/boa_engine/src/value/conversions/try_from_js.rs @@ -0,0 +1,237 @@ +//! This module contains the [`TryFromJs`] trait, and conversions to basic Rust types. + +use crate::{Context, JsBigInt, JsNativeError, JsResult, JsValue}; +use num_bigint::BigInt; + +/// This trait adds a fallible and efficient conversions from a [`JsValue`] to Rust types. +pub trait TryFromJs: Sized { + /// This function tries to convert a JavaScript value into `Self`. + fn try_from_js(value: &JsValue, context: &mut Context<'_>) -> JsResult; +} + +impl JsValue { + /// This function is the inverse of [`TryFromJs`]. It tries to convert a [`JsValue`] to a given + /// Rust type. + pub fn try_js_into(&self, context: &mut Context<'_>) -> JsResult + where + T: TryFromJs, + { + T::try_from_js(self, context) + } +} + +impl TryFromJs for bool { + fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult { + match value { + JsValue::Boolean(b) => Ok(*b), + _ => Err(JsNativeError::typ() + .with_message("cannot convert value to a boolean") + .into()), + } + } +} + +impl TryFromJs for String { + fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult { + match value { + JsValue::String(s) => s.to_std_string().map_err(|e| { + JsNativeError::typ() + .with_message(format!("could not convert JsString to Rust string, since it has UTF-16 characters: {e}")) + .into() + }), + _ => Err(JsNativeError::typ() + .with_message("cannot convert value to a String") + .into()), + } + } +} + +impl TryFromJs for Option +where + T: TryFromJs, +{ + fn try_from_js(value: &JsValue, context: &mut Context<'_>) -> JsResult { + match value { + JsValue::Null | JsValue::Undefined => Ok(None), + value => Ok(Some(T::try_from_js(value, context)?)), + } + } +} + +impl TryFromJs for JsBigInt { + fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult { + match value { + JsValue::BigInt(b) => Ok(b.clone()), + _ => Err(JsNativeError::typ() + .with_message("cannot convert value to a BigInt") + .into()), + } + } +} + +impl TryFromJs for BigInt { + fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult { + match value { + JsValue::BigInt(b) => Ok(b.as_inner().clone()), + _ => Err(JsNativeError::typ() + .with_message("cannot convert value to a BigInt") + .into()), + } + } +} + +impl TryFromJs for JsValue { + fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult { + Ok(value.clone()) + } +} + +impl TryFromJs for f64 { + fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult { + match value { + JsValue::Integer(i) => Ok((*i).into()), + JsValue::Rational(r) => Ok(*r), + _ => Err(JsNativeError::typ() + .with_message("cannot convert value to a f64") + .into()), + } + } +} + +impl TryFromJs for i8 { + fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult { + match value { + JsValue::Integer(i) => (*i).try_into().map_err(|e| { + JsNativeError::typ() + .with_message(format!("cannot convert value to a i8: {e}")) + .into() + }), + _ => Err(JsNativeError::typ() + .with_message("cannot convert value to a i8") + .into()), + } + } +} + +impl TryFromJs for u8 { + fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult { + match value { + JsValue::Integer(i) => (*i).try_into().map_err(|e| { + JsNativeError::typ() + .with_message(format!("cannot convert value to a u8: {e}")) + .into() + }), + _ => Err(JsNativeError::typ() + .with_message("cannot convert value to a u8") + .into()), + } + } +} + +impl TryFromJs for i16 { + fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult { + match value { + JsValue::Integer(i) => (*i).try_into().map_err(|e| { + JsNativeError::typ() + .with_message(format!("cannot convert value to a i16: {e}")) + .into() + }), + _ => Err(JsNativeError::typ() + .with_message("cannot convert value to a i16") + .into()), + } + } +} + +impl TryFromJs for u16 { + fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult { + match value { + JsValue::Integer(i) => (*i).try_into().map_err(|e| { + JsNativeError::typ() + .with_message(format!("cannot convert value to a iu16: {e}")) + .into() + }), + _ => Err(JsNativeError::typ() + .with_message("cannot convert value to a u16") + .into()), + } + } +} + +impl TryFromJs for i32 { + fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult { + match value { + JsValue::Integer(i) => Ok(*i), + _ => Err(JsNativeError::typ() + .with_message("cannot convert value to a i32") + .into()), + } + } +} + +impl TryFromJs for u32 { + fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult { + match value { + JsValue::Integer(i) => (*i).try_into().map_err(|e| { + JsNativeError::typ() + .with_message(format!("cannot convert value to a u32: {e}")) + .into() + }), + _ => Err(JsNativeError::typ() + .with_message("cannot convert value to a u32") + .into()), + } + } +} + +impl TryFromJs for i64 { + fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult { + match value { + JsValue::Integer(i) => Ok((*i).into()), + _ => Err(JsNativeError::typ() + .with_message("cannot convert value to a i64") + .into()), + } + } +} + +impl TryFromJs for u64 { + fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult { + match value { + JsValue::Integer(i) => (*i).try_into().map_err(|e| { + JsNativeError::typ() + .with_message(format!("cannot convert value to a u64: {e}")) + .into() + }), + _ => Err(JsNativeError::typ() + .with_message("cannot convert value to a u64") + .into()), + } + } +} + +impl TryFromJs for i128 { + fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult { + match value { + JsValue::Integer(i) => Ok((*i).into()), + _ => Err(JsNativeError::typ() + .with_message("cannot convert value to a i128") + .into()), + } + } +} + +impl TryFromJs for u128 { + fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult { + match value { + JsValue::Integer(i) => (*i).try_into().map_err(|e| { + JsNativeError::typ() + .with_message(format!("cannot convert value to a u128: {e}")) + .into() + }), + _ => Err(JsNativeError::typ() + .with_message("cannot convert value to a u128") + .into()), + } + } +} diff --git a/boa_engine/src/value/mod.rs b/boa_engine/src/value/mod.rs index 2da1cdfee6..c27dedd30f 100644 --- a/boa_engine/src/value/mod.rs +++ b/boa_engine/src/value/mod.rs @@ -3,15 +3,13 @@ //! Javascript values, utility methods and conversion between Javascript values and Rust values. mod conversions; +pub(crate) mod display; mod equality; mod hash; mod integer; mod operations; -mod serde_json; mod r#type; -pub(crate) mod display; - #[cfg(test)] mod tests; @@ -39,14 +37,15 @@ use std::{ ops::Sub, }; -pub(crate) use conversions::*; - -pub use display::ValueDisplay; -pub use integer::IntegerOrInfinity; -pub use operations::*; -pub use r#type::Type; +#[doc(inline)] +pub use self::{ + conversions::try_from_js::TryFromJs, display::ValueDisplay, integer::IntegerOrInfinity, + operations::*, r#type::Type, +}; +#[doc(inline)] +pub use boa_macros::TryFromJs; -pub(crate) use self::integer::IntegerOrNan; +pub(crate) use self::{conversions::IntoOrUndefined, integer::IntegerOrNan}; static TWO_E_64: Lazy = Lazy::new(|| { const TWO_E_64: u128 = 2u128.pow(64); diff --git a/boa_examples/Cargo.toml b/boa_examples/Cargo.toml index d754a321fb..1b98b91630 100644 --- a/boa_examples/Cargo.toml +++ b/boa_examples/Cargo.toml @@ -9,8 +9,6 @@ license.workspace = true repository.workspace = true rust-version.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] boa_engine = { workspace = true, features = ["console"] } boa_ast.workspace = true diff --git a/boa_examples/src/bin/derive.rs b/boa_examples/src/bin/derive.rs new file mode 100644 index 0000000000..298027dad9 --- /dev/null +++ b/boa_examples/src/bin/derive.rs @@ -0,0 +1,48 @@ +use boa_engine::{value::TryFromJs, Context, JsNativeError, JsResult, JsValue, Source}; + +/// You can easily derive `TryFromJs` for structures with base Rust types. +/// +/// By default, the conversion will only work if the type is directly representable by the Rust +/// type. +#[derive(Debug, TryFromJs)] +#[allow(dead_code)] +struct TestStruct { + inner: bool, + hello: String, + // You can override the conversion of an attribute. + #[boa(from_js_with = "lossy_conversion")] + my_float: i16, +} + +fn main() { + let js_str = r#" + let x = { + inner: false, + hello: "World", + my_float: 2.9, + }; + + x; + "#; + let js = Source::from_bytes(js_str); + + let mut context = Context::default(); + let res = context.eval_script(js).unwrap(); + + let str = TestStruct::try_from_js(&res, &mut context) + .map_err(|e| e.to_string()) + .unwrap(); + + println!("{str:?}"); +} + +/// Converts the value lossly +fn lossy_conversion(value: &JsValue, _context: &mut Context) -> JsResult { + match value { + JsValue::Rational(r) => Ok(r.round() as i16), + JsValue::Integer(i) => Ok(*i as i16), + _ => Err(JsNativeError::typ() + .with_message("cannot convert value to an i16") + .into()), + } +} diff --git a/boa_icu_provider/Cargo.toml b/boa_icu_provider/Cargo.toml index 49c670f181..be32cb34c4 100644 --- a/boa_icu_provider/Cargo.toml +++ b/boa_icu_provider/Cargo.toml @@ -17,7 +17,7 @@ icu_provider = { version = "1.1.0", features = ["serde", "sync"] } icu_provider_blob = "1.1.0" icu_provider_adapters = { version = "1.1.0", features = ["serde"] } once_cell = {version = "1.17.1", default-features = false, features = ["critical-section"]} -icu_datagen = { version = "1.1.1", optional = true } +icu_datagen = { version = "1.1.2", optional = true } log = { version = "0.4.17", optional = true } simple_logger = { version = "4.1.0", optional = true } diff --git a/boa_macros/Cargo.toml b/boa_macros/Cargo.toml index cd7e4e0ad1..5a8aea0f92 100644 --- a/boa_macros/Cargo.toml +++ b/boa_macros/Cargo.toml @@ -8,7 +8,6 @@ license.workspace = true repository.workspace = true rust-version.workspace = true - [lib] proc-macro = true diff --git a/boa_macros/src/lib.rs b/boa_macros/src/lib.rs index 090a98d47b..80c2985e05 100644 --- a/boa_macros/src/lib.rs +++ b/boa_macros/src/lib.rs @@ -58,13 +58,12 @@ )] use proc_macro::TokenStream; -use proc_macro2::Ident; use quote::{quote, ToTokens}; use syn::{ parse::{Parse, ParseStream}, parse_macro_input, punctuated::Punctuated, - Expr, ExprLit, Lit, LitStr, Token, + Data, DeriveInput, Expr, ExprLit, Fields, FieldsNamed, Ident, Lit, LitStr, Token, }; use synstructure::{decl_derive, AddBounds, Structure}; @@ -182,10 +181,14 @@ pub fn static_syms(input: TokenStream) -> TokenStream { /// /// `COMMON_STRINGS_UTF8`, `COMMON_STRINGS_UTF16` and the constants /// defined in [`Sym`] must always be in sync. - // FIXME: use phf when const expressions are allowed. https://github.com/rust-phf/rust-phf/issues/188 + // FIXME: use phf when const expressions are allowed. + // pub(super) static COMMON_STRINGS_UTF16: ::once_cell::sync::Lazy> = ::once_cell::sync::Lazy::new(|| { - let mut set = Set::with_capacity_and_hasher(COMMON_STRINGS_UTF8.len(), ::core::hash::BuildHasherDefault::default()); + let mut set = Set::with_capacity_and_hasher( + COMMON_STRINGS_UTF8.len(), + ::core::hash::BuildHasherDefault::default() + ); #( set.insert(::boa_macros::utf16!(#literals)); )* @@ -216,10 +219,11 @@ pub fn utf16(input: TokenStream) -> TokenStream { decl_derive! { [Trace, attributes(unsafe_ignore_trace)] => - /// Derive the Trace trait. + /// Derive the `Trace` trait. derive_trace } +/// Derives the `Trace` trait. fn derive_trace(mut s: Structure<'_>) -> proc_macro2::TokenStream { s.filter(|bi| { !bi.ast() @@ -299,11 +303,142 @@ fn derive_trace(mut s: Structure<'_>) -> proc_macro2::TokenStream { decl_derive! { [Finalize] => - /// Derive the Finalize trait. + /// Derive the `Finalize` trait. derive_finalize } +/// Derives the `Finalize` trait. #[allow(clippy::needless_pass_by_value)] fn derive_finalize(s: Structure<'_>) -> proc_macro2::TokenStream { s.unbound_impl(quote!(::boa_gc::Finalize), quote!()) } + +/// Derives the `TryFromJs` trait, with the `#[boa()]` attribute. +/// +/// # Panics +/// +/// It will panic if the user tries to derive the `TryFromJs` trait in an `enum` or a tuple struct. +#[proc_macro_derive(TryFromJs, attributes(boa))] +pub fn derive_try_from_js(input: TokenStream) -> TokenStream { + // Parse the input tokens into a syntax tree + let input = parse_macro_input!(input as DeriveInput); + + let Data::Struct(data) = input.data else { + panic!("you can only derive TryFromJs for structs"); + }; + + let Fields::Named(fields) = data.fields else { + panic!("you can only derive TryFromJs for named-field structs") + }; + + let conv = generate_conversion(fields).unwrap_or_else(to_compile_errors); + + let type_name = input.ident; + + // Build the output, possibly using quasi-quotation + let expanded = quote! { + impl boa_engine::value::TryFromJs for #type_name { + fn try_from_js(value: &boa_engine::JsValue, context: &mut boa_engine::Context) + -> boa_engine::JsResult { + match value { + boa_engine::JsValue::Object(o) => {#conv}, + _ => Err(boa_engine::JsError::from( + boa_engine::JsNativeError::typ() + .with_message("cannot convert value to a #type_name") + )), + } + } + } + }; + + // Hand the output tokens back to the compiler + expanded.into() +} + +/// Generates the conversion field by field. +fn generate_conversion(fields: FieldsNamed) -> Result> { + use syn::spanned::Spanned; + + let mut field_list = Vec::with_capacity(fields.named.len()); + let mut final_fields = Vec::with_capacity(fields.named.len()); + + for field in fields.named { + let span = field.span(); + let name = field.ident.ok_or_else(|| { + vec![syn::Error::new( + span, + "you can only derive `TryFromJs` for named-field structs", + )] + })?; + + let name_str = format!("{name}"); + field_list.push(name.clone()); + + let error_str = format!("cannot get property {name_str} of value"); + + let mut from_js_with = None; + if let Some(attr) = field + .attrs + .into_iter() + .find(|attr| attr.path().is_ident("boa")) + { + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("from_js_with") { + let value = meta.value()?; + from_js_with = Some(value.parse::()?); + Ok(()) + } else { + Err(meta.error( + "invalid syntax in the `#[boa()]` attribute. \ + Note that this attribute only accepts the following syntax: \ + `#[boa(from_js_with = \"fully::qualified::path\")]`", + )) + } + }) + .map_err(|err| vec![err])?; + } + + if let Some(method) = from_js_with { + let ident = Ident::new(&method.value(), method.span()); + final_fields.push(quote! { + let #name = #ident(props.get(&#name_str.into()).ok_or_else(|| { + boa_engine::JsError::from( + boa_engine::JsNativeError::typ().with_message(#error_str) + ) + })?.value().ok_or_else(|| { + boa_engine::JsError::from( + boa_engine::JsNativeError::typ().with_message(#error_str) + ) + })?, context)?; + }); + } else { + final_fields.push(quote! { + let #name = props.get(&#name_str.into()).ok_or_else(|| { + boa_engine::JsError::from( + boa_engine::JsNativeError::typ().with_message(#error_str) + ) + })?.value().ok_or_else(|| { + boa_engine::JsError::from( + boa_engine::JsNativeError::typ().with_message(#error_str) + ) + })?.clone().try_js_into(context)?; + }); + } + } + + Ok(quote! { + let o = o.borrow(); + let props = o.properties(); + #(#final_fields)* + Ok(Self { + #(#field_list),* + }) + }) +} + +/// Generates a list of compile errors. +#[allow(clippy::needless_pass_by_value)] +fn to_compile_errors(errors: Vec) -> proc_macro2::TokenStream { + let compile_errors = errors.iter().map(syn::Error::to_compile_error); + quote!(#(#compile_errors)*) +} diff --git a/boa_macros/tests/utf16.rs b/boa_macros/tests/tests.rs similarity index 100% rename from boa_macros/tests/utf16.rs rename to boa_macros/tests/tests.rs diff --git a/boa_macros_tests/Cargo.toml b/boa_macros_tests/Cargo.toml new file mode 100644 index 0000000000..faf7099793 --- /dev/null +++ b/boa_macros_tests/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "boa_macros_tests" +description = "Testing crate for boa_macros" +keywords = ["javascript", "ECMASCript", "compiler", "tester"] +publish = false +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dev-dependencies] +trybuild = "1.0.80" +boa_macros.workspace = true +boa_engine.workspace = true diff --git a/boa_macros_tests/tests/derive/from_js_with.rs b/boa_macros_tests/tests/derive/from_js_with.rs new file mode 100644 index 0000000000..a40bba62a6 --- /dev/null +++ b/boa_macros_tests/tests/derive/from_js_with.rs @@ -0,0 +1,20 @@ +use boa_engine::{value::TryFromJs, Context, JsNativeError, JsResult, JsValue}; + +#[derive(TryFromJs)] +struct TestStruct { + inner: bool, + #[boa(from_js_with = "lossy_float")] + my_int: i16, +} + +fn main() {} + +fn lossy_float(value: &JsValue, _context: &mut Context) -> JsResult { + match value { + JsValue::Rational(r) => Ok(r.round() as i16), + JsValue::Integer(i) => Ok(*i as i16), + _ => Err(JsNativeError::typ() + .with_message("cannot convert value to an i16") + .into()), + } +} diff --git a/boa_macros_tests/tests/derive/simple_struct.rs b/boa_macros_tests/tests/derive/simple_struct.rs new file mode 100644 index 0000000000..f9ea0c97b2 --- /dev/null +++ b/boa_macros_tests/tests/derive/simple_struct.rs @@ -0,0 +1,8 @@ +use boa_engine::value::TryFromJs; + +#[derive(TryFromJs)] +struct TestStruct { + inner: bool, +} + +fn main() {} diff --git a/boa_macros_tests/tests/tests.rs b/boa_macros_tests/tests/tests.rs new file mode 100644 index 0000000000..16de9065e0 --- /dev/null +++ b/boa_macros_tests/tests/tests.rs @@ -0,0 +1,6 @@ +#[test] +fn try_from_js() { + let t = trybuild::TestCases::new(); + t.pass("tests/derive/simple_struct.rs"); + t.pass("tests/derive/from_js_with.rs"); +}