From 9ffdfd3dd5f0c7d1d5db9b83b668416c05a75bad Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Thu, 28 Apr 2022 19:24:14 -0500 Subject: [PATCH] Implement API for ICU4X data injection --- .github/workflows/bors.yml | 2 +- .github/workflows/master.yml | 2 +- .github/workflows/rust.yml | 2 +- Cargo.lock | 251 +++++++++++++------------- boa_engine/Cargo.toml | 17 +- boa_engine/src/builtins/intl/mod.rs | 209 ++++++++++++--------- boa_engine/src/builtins/intl/tests.rs | 75 ++++++-- boa_engine/src/builtins/mod.rs | 14 +- boa_engine/src/context/icu.rs | 79 ++++++++ boa_engine/src/context/mod.rs | 136 +++++++++++--- boa_engine/src/object/jsproxy.rs | 1 + boa_engine/src/object/mod.rs | 7 +- boa_engine/src/syntax/parser/tests.rs | 5 +- 13 files changed, 529 insertions(+), 271 deletions(-) create mode 100644 boa_engine/src/context/icu.rs diff --git a/.github/workflows/bors.yml b/.github/workflows/bors.yml index 392ac1f7e08..af3785231ad 100644 --- a/.github/workflows/bors.yml +++ b/.github/workflows/bors.yml @@ -159,4 +159,4 @@ jobs: uses: actions-rs/cargo@v1 with: command: doc - args: -v --document-private-items + args: -v --document-private-items --all-features diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 414e939813f..45172e33786 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -29,7 +29,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: doc - args: -v --document-private-items + args: -v --document-private-items --all-features - run: echo "" > target/doc/index.html - run: | if [ -d target/doc_upload ]; then rm -rf target/doc_upload; fi diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index c8b6efbb9fe..fe54e3deb94 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -163,4 +163,4 @@ jobs: uses: actions-rs/cargo@v1 with: command: doc - args: -v --document-private-items + args: -v --document-private-items --all-features diff --git a/Cargo.lock b/Cargo.lock index d13d3f94e44..db5046fbc27 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -100,7 +100,12 @@ dependencies = [ "fast-float", "float-cmp", "gc", - "icu", + "icu_datetime", + "icu_locale_canonicalizer", + "icu_locid", + "icu_plurals", + "icu_provider", + "icu_testdata", "indexmap", "jemallocator", "num-bigint", @@ -113,6 +118,7 @@ dependencies = [ "ryu-js", "serde", "serde_json", + "sys-locale", "tap", "unicode-normalization", ] @@ -196,7 +202,7 @@ checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" dependencies = [ "lazy_static", "memchr", - "regex-automata 0.1.10", + "regex-automata", "serde", ] @@ -403,6 +409,16 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "cstr_core" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644828c273c063ab0d39486ba42a5d1f3a499d35529c759e763a9c6cb8a0fb08" +dependencies = [ + "cty", + "memchr", +] + [[package]] name = "csv" version = "1.1.6" @@ -425,6 +441,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" + [[package]] name = "dirs-next" version = "2.0.0" @@ -632,24 +654,6 @@ dependencies = [ "libc", ] -[[package]] -name = "icu" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15a4e90a2faa6719f4b3b1dac871d1f2794474d453b8e6ca1264062c4bf2c9da" -dependencies = [ - "fixed_decimal", - "icu_calendar", - "icu_datetime", - "icu_decimal", - "icu_list", - "icu_locale_canonicalizer", - "icu_locid", - "icu_plurals", - "icu_properties", - "writeable", -] - [[package]] name = "icu_calendar" version = "0.6.0" @@ -659,23 +663,11 @@ dependencies = [ "displaydoc", "icu_locid", "icu_provider", + "serde", "tinystr", "zerovec", ] -[[package]] -name = "icu_codepointtrie" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cdb6b96093158ec0031f9831283085cf897cf4bffc9a1a35a8360a777141058" -dependencies = [ - "displaydoc", - "icu_uniset", - "yoke", - "zerofrom", - "zerovec", -] - [[package]] name = "icu_datetime" version = "0.6.0" @@ -689,39 +681,13 @@ dependencies = [ "icu_plurals", "icu_provider", "litemap", + "serde", "smallvec", "tinystr", "writeable", "zerovec", ] -[[package]] -name = "icu_decimal" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614ff51266354e8c8d75bfc65f806fbc0d0177da079c2482402cfc20b72de47d" -dependencies = [ - "displaydoc", - "fixed_decimal", - "icu_locid", - "icu_provider", - "writeable", -] - -[[package]] -name = "icu_list" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c144d074b8de0f6adcb6941ac4544abf83483fa5681154dcc361253c3a122c5c" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider", - "regex-automata 0.2.0", - "writeable", - "zerovec", -] - [[package]] name = "icu_locale_canonicalizer" version = "0.6.0" @@ -731,6 +697,7 @@ dependencies = [ "icu_locid", "icu_provider", "litemap", + "serde", "tinystr", "zerovec", ] @@ -759,35 +726,39 @@ dependencies = [ "fixed_decimal", "icu_locid", "icu_provider", + "serde", "zerovec", ] [[package]] -name = "icu_properties" +name = "icu_provider" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ca8f26685a463ff47dc0d9f7b270e8d955d3c2dd9f748292af14940b659671" +checksum = "c7fbd7ffd479fdbbc366334a82821dc50d9f80b758389393374e9b36ff159f1a" dependencies = [ "displaydoc", - "icu_codepointtrie", - "icu_provider", - "icu_uniset", + "icu_locid", + "icu_provider_macros", + "litemap", + "postcard", + "serde", + "writeable", + "yoke", + "zerofrom", "zerovec", ] [[package]] -name = "icu_provider" +name = "icu_provider_blob" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fbd7ffd479fdbbc366334a82821dc50d9f80b758389393374e9b36ff159f1a" +checksum = "474b884a565f7ec52a26754a8b57646c128195e7af629caa52317ef6674e3e0d" dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", - "litemap", + "icu_provider", + "postcard", + "serde", "writeable", "yoke", - "zerofrom", "zerovec", ] @@ -803,16 +774,13 @@ dependencies = [ ] [[package]] -name = "icu_uniset" -version = "0.5.0" +name = "icu_testdata" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecdc2859b6efd75ae22e6350e62f21c87cfe3cfdc11bef6f9565995e88d14ba9" +checksum = "a5580eeaa6ea70b94f286120ffcfb70f75ac8d759d95ccf6223a3c479ff99285" dependencies = [ - "displaydoc", - "tinystr", - "yoke", - "zerofrom", - "zerovec", + "icu_provider", + "icu_provider_blob", ] [[package]] @@ -857,9 +825,9 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "itoa" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" [[package]] name = "jemalloc-sys" @@ -899,9 +867,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.122" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec647867e2bf0772e28c8bcde4f0d19a9216916e890543b5a03ed8ef27b8f259" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] name = "linked-hash-map" @@ -911,9 +879,9 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" [[package]] name = "linux-raw-sys" -version = "0.0.42" +version = "0.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5284f00d480e1c39af34e72f8ad60b94f47007e3481cd3b731c1d67190ddc7b7" +checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d" [[package]] name = "litemap" @@ -921,6 +889,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78d268a51abaaee3b8686e56396eb725b0da510bddd266a52e784aa1029dae73" dependencies = [ + "serde", "yoke", ] @@ -936,9 +905,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.16" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] @@ -959,9 +928,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memmap2" @@ -1074,9 +1043,9 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "os_str_bytes" -version = "6.0.0" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" +checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435" [[package]] name = "parking_lot" @@ -1184,6 +1153,22 @@ dependencies = [ "plotters-backend", ] +[[package]] +name = "postcard" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a25c0b0ae06fcffe600ad392aabfa535696c8973f2253d9ac83171924c58a858" +dependencies = [ + "postcard-cobs", + "serde", +] + +[[package]] +name = "postcard-cobs" +version = "0.1.5-pre" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c68cb38ed13fd7bc9dd5db8f165b7c8d9c1a315104083a2b10f11354c2af97f" + [[package]] name = "ppv-lite86" version = "0.2.16" @@ -1222,18 +1207,18 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro2" -version = "1.0.37" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" +checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" dependencies = [ "proc-macro2", ] @@ -1292,9 +1277,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f51245e1e62e1f1629cbfec37b5793bbabcaeb90f30e94d2ba03564687353e4" +checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -1339,15 +1324,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -[[package]] -name = "regex-automata" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9368763f5a9b804326f3af749e16f9abf378d227bcdee7634b13d8f17793782" -dependencies = [ - "memchr", -] - [[package]] name = "regex-syntax" version = "0.6.26" @@ -1380,9 +1356,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.34.2" +version = "0.34.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96619609a54d638872db136f56941d34e2a00bb0acf3fa783a90d6b96a093ba2" +checksum = "f117495127afb702af6706f879fb2b5c008c38ccf3656afc514e26f35bdb8180" dependencies = [ "bitflags", "errno", @@ -1428,9 +1404,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" [[package]] name = "ryu-js" @@ -1455,9 +1431,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "semver" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4" +checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd" [[package]] name = "serde" @@ -1495,7 +1471,7 @@ version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" dependencies = [ - "itoa 1.0.1", + "itoa 1.0.2", "ryu", "serde", ] @@ -1523,6 +1499,9 @@ name = "smallvec" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" +dependencies = [ + "serde", +] [[package]] name = "stable_deref_trait" @@ -1591,13 +1570,13 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.91" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d" +checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] @@ -1612,6 +1591,19 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "sys-locale" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3913c5a3d30054d7f77cf07cdd800c8103ace15c6e44437c5db66a43dd3a92cf" +dependencies = [ + "cc", + "cstr_core", + "libc", + "web-sys", + "winapi", +] + [[package]] name = "tap" version = "1.0.1" @@ -1644,18 +1636,18 @@ checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" dependencies = [ "proc-macro2", "quote", @@ -1696,9 +1688,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] @@ -1715,6 +1707,12 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1218098468b8085b19a2824104c70d976491d247ce194bbd9dc77181150cdfd6" +[[package]] +name = "unicode-ident" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" + [[package]] name = "unicode-normalization" version = "0.1.19" @@ -1738,9 +1736,9 @@ checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" [[package]] name = "unicode-xid" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" [[package]] name = "utf8parse" @@ -1981,6 +1979,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c1b475ff48237bf7281cfa1721a52f0ad7f95ede1a46385e555870a354afc45" dependencies = [ + "serde", "yoke", "zerofrom", "zerovec-derive", diff --git a/boa_engine/Cargo.toml b/boa_engine/Cargo.toml index 65c0cd40875..53f4024bd63 100644 --- a/boa_engine/Cargo.toml +++ b/boa_engine/Cargo.toml @@ -14,6 +14,15 @@ readme = "../README.md" [features] profiler = ["boa_profiler/profiler"] deser = ["boa_interner/serde"] +intl = [ + "dep:icu_locale_canonicalizer", + "dep:icu_locid", + "dep:icu_datetime", + "dep:icu_plurals", + "dep:icu_provider", + "dep:icu_testdata", + "dep:sys-locale" +] # Enable Boa's WHATWG console object implementation. console = [] @@ -41,7 +50,13 @@ unicode-normalization = "0.1.19" dyn-clone = "1.0.5" once_cell = "1.12.0" tap = "1.0.1" -icu = "0.6.0" +icu_locale_canonicalizer = { version = "0.6.0", features = ["serde"], optional = true } +icu_locid = { version = "0.6.0", features = ["serde"], optional = true } +icu_datetime = { version = "0.6.0", features = ["serde"], optional = true } +icu_plurals = { version = "0.6.0", features = ["serde"], optional = true } +icu_provider = { version = "0.6.0", optional = true } +icu_testdata = {version = "0.6.0", optional = true} +sys-locale = { version = "0.2.0", optional = true } [dev-dependencies] criterion = "0.3.5" diff --git a/boa_engine/src/builtins/intl/mod.rs b/boa_engine/src/builtins/intl/mod.rs index 08c7264edcd..bd5ba0c8ac6 100644 --- a/boa_engine/src/builtins/intl/mod.rs +++ b/boa_engine/src/builtins/intl/mod.rs @@ -21,11 +21,11 @@ pub mod date_time_format; mod tests; use boa_profiler::Profiler; +use icu_locale_canonicalizer::LocaleCanonicalizer; +use icu_locid::{locale, Locale}; use indexmap::IndexSet; use rustc_hash::FxHashMap; -use tap::{Conv, Pipe}; - -use icu::locid::Locale; +use tap::{Conv, Pipe, TapOptional}; /// JavaScript `Intl` object. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -58,75 +58,6 @@ impl BuiltIn for Intl { } impl Intl { - fn canonicalize_locale(locale: &str) -> JsString { - JsString::new(locale) - } - - fn canonicalize_locale_list( - args: &[JsValue], - context: &mut Context, - ) -> JsResult> { - // https://tc39.es/ecma402/#sec-canonicalizelocalelist - // 1. If locales is undefined, then - let locales = args.get_or_undefined(0); - if locales.is_undefined() { - // a. Return a new empty List. - return Ok(Vec::new()); - } - - let locales = &args[0]; - - // 2. Let seen be a new empty List. - let mut seen = IndexSet::new(); - - // 3. If Type(locales) is String or Type(locales) is Object and locales has an [[InitializedLocale]] internal slot, then - // TODO: check if Type(locales) is object and handle the internal slots - let o = if locales.is_string() { - // a. Let O be CreateArrayFromList(« locales »). - Array::create_array_from_list([locales.clone()], context) - } else { - // 4. Else, - // a. Let O be ? ToObject(locales). - locales.to_object(context)? - }; - - // 5. Let len be ? ToLength(? Get(O, "length")). - let len = o.length_of_array_like(context)?; - - // 6 Let k be 0. - // 7. Repeat, while k < len, - for k in 0..len { - // a. Let Pk be ToString(k). - // b. Let kPresent be ? HasProperty(O, Pk). - let k_present = o.has_property(k, context)?; - // c. If kPresent is true, then - if k_present { - // i. Let kValue be ? Get(O, Pk). - let k_value = o.get(k, context)?; - // ii. If Type(kValue) is not String or Object, throw a TypeError exception. - if !(k_value.is_object() || k_value.is_string()) { - return context.throw_type_error("locale should be a String or Object"); - } - // iii. If Type(kValue) is Object and kValue has an [[InitializedLocale]] internal slot, then - // TODO: handle checks for InitializedLocale internal slot (there should be an if statement here) - // 1. Let tag be kValue.[[Locale]]. - // iv. Else, - // 1. Let tag be ? ToString(kValue). - let tag = k_value.to_string(context)?; - // v. If IsStructurallyValidLanguageTag(tag) is false, throw a RangeError exception. - // TODO: implement `IsStructurallyValidLanguageTag` - - // vi. Let canonicalizedTag be CanonicalizeUnicodeLocaleId(tag). - seen.insert(Self::canonicalize_locale(&tag)); - // vii. If canonicalizedTag is not an element of seen, append canonicalizedTag as the last element of seen. - } - // d. Increase k by 1. - } - - // 8. Return seen. - Ok(seen.into_iter().collect::>()) - } - /// Returns an array containing the canonical locale names. /// /// More information: @@ -141,10 +72,11 @@ impl Intl { context: &mut Context, ) -> JsResult { // 1. Let ll be ? CanonicalizeLocaleList(locales). - let ll = Self::canonicalize_locale_list(args, context)?; + let ll = canonicalize_locale_list(args, context)?; + // 2. Return CreateArrayFromList(ll). Ok(JsValue::Object(Array::create_array_from_list( - ll.into_iter().map(Into::into), + ll.into_iter().map(|loc| loc.to_string().into()), context, ))) } @@ -167,9 +99,11 @@ struct MatcherRecord { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma402/#sec-defaultlocale -fn default_locale() -> JsString { - // FIXME get locale from environment - JsString::new("en-US") +fn default_locale(canonicalizer: &LocaleCanonicalizer) -> Locale { + sys_locale::get_locale() + .and_then(|loc| loc.parse::().ok()) + .tap_some_mut(|loc| canonicalize_unicode_locale_id(loc, canonicalizer)) + .unwrap_or(locale!("en-US")) } /// The `BestAvailableLocale` abstract operation compares the provided argument `locale`, @@ -220,7 +154,11 @@ fn best_available_locale(available_locales: &[JsString], locale: &JsString) -> O /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma402/#sec-lookupmatcher -fn lookup_matcher(available_locales: &[JsString], requested_locales: &[JsString]) -> MatcherRecord { +fn lookup_matcher( + available_locales: &[JsString], + requested_locales: &[JsString], + canonicalizer: &LocaleCanonicalizer, +) -> MatcherRecord { // 1. Let result be a new Record. // 2. For each element locale of requestedLocales, do for locale_str in requested_locales { @@ -259,7 +197,7 @@ fn lookup_matcher(available_locales: &[JsString], requested_locales: &[JsString] // 4. Set result.[[locale]] to defLocale. // 5. Return result. MatcherRecord { - locale: default_locale(), + locale: default_locale(canonicalizer).to_string().into(), extension: JsString::empty(), } } @@ -277,8 +215,9 @@ fn lookup_matcher(available_locales: &[JsString], requested_locales: &[JsString] fn best_fit_matcher( available_locales: &[JsString], requested_locales: &[JsString], + canonicalizer: &LocaleCanonicalizer, ) -> MatcherRecord { - lookup_matcher(available_locales, requested_locales) + lookup_matcher(available_locales, requested_locales, canonicalizer) } /// `Keyword` structure is a pair of keyword key and keyword value. @@ -414,7 +353,11 @@ fn unicode_extension_components(extension: &JsString) -> UniExtRecord { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma402/#sec-insert-unicode-extension-and-canonicalize -fn insert_unicode_extension_and_canonicalize(locale: &str, extension: &str) -> JsString { +fn insert_unicode_extension_and_canonicalize( + locale: &str, + extension: &str, + canonicalizer: &LocaleCanonicalizer, +) -> JsString { // TODO 1. Assert: locale does not contain a substring that is a Unicode locale extension sequence. // TODO 2. Assert: extension is a Unicode locale extension sequence. // TODO 3. Assert: tag matches the unicode_locale_id production. @@ -442,9 +385,77 @@ fn insert_unicode_extension_and_canonicalize(locale: &str, extension: &str) -> J } }; - // TODO 7. Assert: ! IsStructurallyValidLanguageTag(locale) is true. + // 7. Assert: ! IsStructurallyValidLanguageTag(locale) is true. + let mut new_locale = new_locale + .parse() + .expect("Assert: ! IsStructurallyValidLanguageTag(locale) is true."); + // 8. Return ! CanonicalizeUnicodeLocaleId(locale). - Intl::canonicalize_locale(&new_locale) + canonicalize_unicode_locale_id(&mut new_locale, canonicalizer); + new_locale.to_string().into() +} + +fn canonicalize_locale_list(args: &[JsValue], context: &mut Context) -> JsResult> { + // https://tc39.es/ecma402/#sec-canonicalizelocalelist + // 1. If locales is undefined, then + let locales = args.get_or_undefined(0); + if locales.is_undefined() { + // a. Return a new empty List. + return Ok(Vec::new()); + } + + // 2. Let seen be a new empty List. + let mut seen = IndexSet::new(); + + // 3. If Type(locales) is String or Type(locales) is Object and locales has an [[InitializedLocale]] internal slot, then + // TODO: check if Type(locales) is object and handle the internal slots + let o = if locales.is_string() { + // a. Let O be CreateArrayFromList(« locales »). + Array::create_array_from_list([locales.clone()], context) + } else { + // 4. Else, + // a. Let O be ? ToObject(locales). + locales.to_object(context)? + }; + + // 5. Let len be ? ToLength(? Get(O, "length")). + let len = o.length_of_array_like(context)?; + + // 6 Let k be 0. + // 7. Repeat, while k < len, + for k in 0..len { + // a. Let Pk be ToString(k). + // b. Let kPresent be ? HasProperty(O, Pk). + let k_present = o.has_property(k, context)?; + // c. If kPresent is true, then + if k_present { + // i. Let kValue be ? Get(O, Pk). + let k_value = o.get(k, context)?; + // ii. If Type(kValue) is not String or Object, throw a TypeError exception. + if !(k_value.is_object() || k_value.is_string()) { + return context.throw_type_error("locale should be a String or Object"); + } + // iii. If Type(kValue) is Object and kValue has an [[InitializedLocale]] internal slot, then + // TODO: handle checks for InitializedLocale internal slot (there should be an if statement here) + // 1. Let tag be kValue.[[Locale]]. + // iv. Else, + // 1. Let tag be ? ToString(kValue). + let tag = k_value.to_string(context)?; + // v. If IsStructurallyValidLanguageTag(tag) is false, throw a RangeError exception. + let mut tag = tag.parse().map_err(|_| { + context.construct_range_error("locale is not a structurally valid language tag") + })?; + + // vi. Let canonicalizedTag be CanonicalizeUnicodeLocaleId(tag). + canonicalize_unicode_locale_id(&mut tag, &*context.icu().locale_canonicalizer()); + seen.insert(tag); + // vii. If canonicalizedTag is not an element of seen, append canonicalizedTag as the last element of seen. + } + // d. Increase k by 1. + } + + // 8. Return seen. + Ok(seen.into_iter().collect()) } /// `LocaleDataRecord` is the type of `locale_data` argument in `resolve_locale` subroutine. @@ -498,9 +509,17 @@ fn resolve_locale( // 3. Else, // a. Let r be ! BestFitMatcher(availableLocales, requestedLocales). let r = if matcher.eq(&JsString::new("lookup")) { - lookup_matcher(available_locales, requested_locales) + lookup_matcher( + available_locales, + requested_locales, + context.icu().locale_canonicalizer(), + ) } else { - best_fit_matcher(available_locales, requested_locales) + best_fit_matcher( + available_locales, + requested_locales, + context.icu().locale_canonicalizer(), + ) }; // 4. Let foundLocale be r.[[locale]]. @@ -643,8 +662,11 @@ fn resolve_locale( // 10. If the number of elements in supportedExtension is greater than 2, then if supported_extension.len() > 2 { // a. Let foundLocale be InsertUnicodeExtensionAndCanonicalize(foundLocale, supportedExtension). - found_locale = - insert_unicode_extension_and_canonicalize(&found_locale, &supported_extension); + found_locale = insert_unicode_extension_and_canonicalize( + &found_locale, + &supported_extension, + context.icu().locale_canonicalizer(), + ); } // 11. Set result.[[locale]] to foundLocale. @@ -765,3 +787,16 @@ pub(crate) fn default_number_option( // 4. Return floor(value). Ok(Some(value.floor())) } + +/// Abstract operation `CanonicalizeUnicodeLocaleId ( locale )`. +/// +/// This function differs sligthly from the specification by modifying in-place +/// the provided [`Locale`] instead of creating a new canonicalized copy. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://402.ecma-international.org/8.0/#sec-canonicalizeunicodelocaleid +fn canonicalize_unicode_locale_id(locale: &mut Locale, canonicalizer: &LocaleCanonicalizer) { + canonicalizer.canonicalize(locale); +} diff --git a/boa_engine/src/builtins/intl/tests.rs b/boa_engine/src/builtins/intl/tests.rs index 8cf08b43d33..6abfb0c6002 100644 --- a/boa_engine/src/builtins/intl/tests.rs +++ b/boa_engine/src/builtins/intl/tests.rs @@ -9,6 +9,7 @@ use crate::{ Context, JsString, JsValue, }; +use icu_locale_canonicalizer::LocaleCanonicalizer; use rustc_hash::FxHashMap; #[test] @@ -47,27 +48,36 @@ fn best_avail_loc() { #[test] fn lookup_match() { + let provider = icu_testdata::get_provider(); + let canonicalizer = + LocaleCanonicalizer::new(&provider).expect("Could not create canonicalizer"); // available: [], requested: [] let available_locales = Vec::::new(); let requested_locales = Vec::::new(); - let matcher = lookup_matcher(&available_locales, &requested_locales); - assert_eq!(matcher.locale, default_locale()); + let matcher = lookup_matcher(&available_locales, &requested_locales, &canonicalizer); + assert_eq!( + matcher.locale, + default_locale(&canonicalizer).to_string().as_str() + ); assert_eq!(matcher.extension, ""); // available: [de-DE], requested: [] let available_locales = vec![JsString::new("de-DE")]; let requested_locales = Vec::::new(); - let matcher = lookup_matcher(&available_locales, &requested_locales); - assert_eq!(matcher.locale, default_locale()); + let matcher = lookup_matcher(&available_locales, &requested_locales, &canonicalizer); + assert_eq!( + matcher.locale, + default_locale(&canonicalizer).to_string().as_str() + ); assert_eq!(matcher.extension, ""); // available: [fr-FR], requested: [fr-FR-u-hc-h12] let available_locales = vec![JsString::new("fr-FR")]; let requested_locales = vec![JsString::new("fr-FR-u-hc-h12")]; - let matcher = lookup_matcher(&available_locales, &requested_locales); + let matcher = lookup_matcher(&available_locales, &requested_locales, &canonicalizer); assert_eq!(matcher.locale, "fr-FR"); assert_eq!(matcher.extension, "u-hc-h12"); @@ -75,32 +85,35 @@ fn lookup_match() { let available_locales = vec![JsString::new("es-ES")]; let requested_locales = vec![JsString::new("es-ES")]; - let matcher = best_fit_matcher(&available_locales, &requested_locales); + let matcher = best_fit_matcher(&available_locales, &requested_locales, &canonicalizer); assert_eq!(matcher.locale, "es-ES"); assert_eq!(matcher.extension, ""); } #[test] fn insert_unicode_ext() { + let provider = icu_testdata::get_provider(); + let canonicalizer = + LocaleCanonicalizer::new(&provider).expect("Could not create canonicalizer"); let locale = JsString::new("hu-HU"); let ext = JsString::empty(); assert_eq!( - insert_unicode_extension_and_canonicalize(&locale, &ext), + insert_unicode_extension_and_canonicalize(&locale, &ext, &canonicalizer), locale ); let locale = JsString::new("hu-HU"); let ext = JsString::new("-u-hc-h12"); assert_eq!( - insert_unicode_extension_and_canonicalize(&locale, &ext), + insert_unicode_extension_and_canonicalize(&locale, &ext, &canonicalizer), JsString::new("hu-HU-u-hc-h12") ); let locale = JsString::new("hu-HU-x-PRIVATE"); let ext = JsString::new("-u-hc-h12"); assert_eq!( - insert_unicode_extension_and_canonicalize(&locale, &ext), - JsString::new("hu-HU-u-hc-h12-x-PRIVATE") + insert_unicode_extension_and_canonicalize(&locale, &ext, &canonicalizer), + JsString::new("hu-HU-u-hc-h12-x-private") ); } @@ -165,8 +178,18 @@ fn locale_resolution() { &locale_data, &mut context, ); - assert_eq!(locale_record.locale, default_locale()); - assert_eq!(locale_record.data_locale, default_locale()); + assert_eq!( + locale_record.locale, + default_locale(context.icu().locale_canonicalizer()) + .to_string() + .as_str() + ); + assert_eq!( + locale_record.data_locale, + default_locale(context.icu().locale_canonicalizer()) + .to_string() + .as_str() + ); assert!(locale_record.properties.is_empty()); // test best fit @@ -187,8 +210,18 @@ fn locale_resolution() { &locale_data, &mut context, ); - assert_eq!(locale_record.locale, default_locale()); - assert_eq!(locale_record.data_locale, default_locale()); + assert_eq!( + locale_record.locale, + default_locale(context.icu().locale_canonicalizer()) + .to_string() + .as_str() + ); + assert_eq!( + locale_record.data_locale, + default_locale(context.icu().locale_canonicalizer()) + .to_string() + .as_str() + ); assert!(locale_record.properties.is_empty()); // available: [es-ES], requested: [es-ES] @@ -231,8 +264,18 @@ fn locale_resolution() { &locale_data, &mut context, ); - assert_eq!(locale_record.locale, default_locale()); - assert_eq!(locale_record.data_locale, default_locale()); + assert_eq!( + locale_record.locale, + default_locale(context.icu().locale_canonicalizer()) + .to_string() + .as_str() + ); + assert_eq!( + locale_record.data_locale, + default_locale(context.icu().locale_canonicalizer()) + .to_string() + .as_str() + ); assert!(locale_record.properties.is_empty()); } diff --git a/boa_engine/src/builtins/mod.rs b/boa_engine/src/builtins/mod.rs index 7da47d784f8..938c4b7c3b0 100644 --- a/boa_engine/src/builtins/mod.rs +++ b/boa_engine/src/builtins/mod.rs @@ -4,8 +4,6 @@ pub mod array; pub mod array_buffer; pub mod bigint; pub mod boolean; -#[cfg(feature = "console")] -pub mod console; pub mod dataview; pub mod date; pub mod error; @@ -15,7 +13,6 @@ pub mod generator; pub mod generator_function; pub mod global_this; pub mod infinity; -pub mod intl; pub mod iterable; pub mod json; pub mod map; @@ -32,6 +29,12 @@ pub mod symbol; pub mod typed_array; pub mod undefined; +#[cfg(feature = "console")] +pub mod console; + +#[cfg(feature = "intl")] +pub mod intl; + pub(crate) use self::{ array::{array_iterator::ArrayIterator, Array}, bigint::BigInt, @@ -46,7 +49,6 @@ pub(crate) use self::{ function::BuiltInFunctionObject, global_this::GlobalThis, infinity::Infinity, - intl::Intl, json::Json, map::map_iterator::MapIterator, map::Map, @@ -143,7 +145,6 @@ pub fn init(context: &mut Context) { BuiltInFunctionObject, BuiltInObjectObject, Math, - Intl, Json, Array, Proxy, @@ -184,6 +185,9 @@ pub fn init(context: &mut Context) { GeneratorFunction }; + #[cfg(feature = "intl")] + init_builtin::(context); + #[cfg(feature = "console")] init_builtin::(context); } diff --git a/boa_engine/src/context/icu.rs b/boa_engine/src/context/icu.rs new file mode 100644 index 00000000000..53dbd26dc1b --- /dev/null +++ b/boa_engine/src/context/icu.rs @@ -0,0 +1,79 @@ +use icu_datetime::provider::{ + calendar::{DatePatternsV1Marker, DateSkeletonPatternsV1Marker, DateSymbolsV1Marker}, + week_data::WeekDataV1Marker, +}; +use icu_locale_canonicalizer::{ + provider::{AliasesV1Marker, LikelySubtagsV1Marker}, + LocaleCanonicalizer, +}; +use icu_plurals::provider::OrdinalV1Marker; +use icu_provider::prelude::*; + +/// Trait encompassing all the required implementations that define +/// a valid icu data provider. +pub trait BoaProvider: + ResourceProvider + + ResourceProvider + + ResourceProvider + + ResourceProvider + + ResourceProvider + + ResourceProvider + + ResourceProvider +{ +} + +impl BoaProvider for T where + T: ResourceProvider + + ResourceProvider + + ResourceProvider + + ResourceProvider + + ResourceProvider + + ResourceProvider + + ResourceProvider + + ?Sized +{ +} + +/// Collection of tools initialized from a [`BoaProvider`] that are used +/// for the functionality of `Intl`. +#[allow(unused)] +pub(crate) struct Icu { + provider: Box, + locale_canonicalizer: LocaleCanonicalizer, +} + +impl std::fmt::Debug for Icu { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + #[derive(Debug)] + struct Canonicalizer; + f.debug_struct("Icu") + .field("locale_canonicalizer", &Canonicalizer) + .finish() + } +} + +impl Icu { + /// Create a new [`Icu`] from a valid [`BoaProvider`] + /// + /// # Errors + /// + /// This method will return an error if any of the tools + /// required cannot be constructed. + pub(crate) fn new(provider: P) -> Result { + Ok(Self { + locale_canonicalizer: LocaleCanonicalizer::new(&provider)?, + provider: Box::new(provider), + }) + } + + /// Get the [`LocaleCanonicalizer`] tool. + pub(crate) fn locale_canonicalizer(&self) -> &LocaleCanonicalizer { + &self.locale_canonicalizer + } + + /// Get the inner icu data provider + #[allow(unused)] + pub(crate) fn provider(&self) -> &dyn BoaProvider { + self.provider.as_ref() + } +} diff --git a/boa_engine/src/context/mod.rs b/boa_engine/src/context/mod.rs index 080a3224a09..a7f4b2bcdae 100644 --- a/boa_engine/src/context/mod.rs +++ b/boa_engine/src/context/mod.rs @@ -2,8 +2,13 @@ pub mod intrinsics; +#[cfg(feature = "intl")] +mod icu; + use intrinsics::{IntrinsicObjects, Intrinsics}; +#[cfg(feature = "console")] +use crate::builtins::console::Console; use crate::{ builtins::{self, function::NativeFunctionSignature}, bytecompiler::ByteCompiler, @@ -15,12 +20,17 @@ use crate::{ vm::{CallFrame, CodeBlock, FinallyReturn, GeneratorResumeKind, Vm}, JsResult, JsValue, }; + use boa_gc::Gc; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; -#[cfg(feature = "console")] -use crate::builtins::console::Console; +#[cfg(feature = "intl")] +use icu_provider::DataError; + +#[doc(inline)] +#[cfg(all(feature = "intl", doc))] +pub use icu::BoaProvider; /// Javascript context. It is the primary way to interact with the runtime. /// @@ -82,44 +92,25 @@ pub struct Context { /// Intrinsic objects intrinsics: Intrinsics, + /// ICU related utilities + #[cfg(feature = "intl")] + icu: icu::Icu, + pub(crate) vm: Vm, } impl Default for Context { fn default() -> Self { - let mut context = Self { - realm: Realm::create(), - interner: Interner::default(), - #[cfg(feature = "console")] - console: Console::default(), - intrinsics: Intrinsics::default(), - vm: Vm { - frame: None, - stack: Vec::with_capacity(1024), - trace: false, - stack_size_limit: 1024, - }, - }; - - // Add new builtIns to Context Realm - // At a later date this can be removed from here and called explicitly, - // but for now we almost always want these default builtins - context.intrinsics.objects = IntrinsicObjects::init(&mut context); - context.create_intrinsics(); - context + ContextBuilder::default().build() } } impl Context { - /// Create a new `Context`. - #[inline] - pub fn new(interner: Interner) -> Self { - Self { - interner, - ..Self::default() - } + /// Create a new [`ContextBuilder`] to specify the [`Interner`] and/or + /// the icu data provider. + pub fn builder() -> ContextBuilder { + ContextBuilder::default() } - /// Gets the string interner. #[inline] pub fn interner(&self) -> &Interner { @@ -730,4 +721,89 @@ impl Context { pub fn set_trace(&mut self, trace: bool) { self.vm.trace = trace; } + + #[cfg(feature = "intl")] + #[inline] + /// Get the ICU related utilities + pub(crate) fn icu(&self) -> &icu::Icu { + &self.icu + } +} +/// Builder for the [`Context`] type. +/// +/// This builder allows custom initialization of the [`Interner`] within +/// the context. +/// Additionally, if the `intl` feature is enabled, [`ContextBuilder`] becomes +/// the only way to create a new [`Context`], since now it requires a +/// valid data provider for the `Intl` functionality. +#[cfg_attr( + feature = "intl", + doc = "The required data for a valid data provider is specified in [`BoaProvider`]" +)] +#[derive(Debug, Default)] +pub struct ContextBuilder { + interner: Option, + #[cfg(feature = "intl")] + icu: Option, +} + +impl ContextBuilder { + /// Initializes the context [`Interner`] to the provided interner. + /// + /// This is useful when you want to initialize an [`Interner`] with + /// a collection of words before parsing. + #[must_use] + pub fn interner(mut self, interner: Interner) -> Self { + self.interner = Some(interner); + self + } + + /// Provides an icu data provider to the [`Context`]. + /// + /// This function is only available if the `intl` feature is enabled. + #[cfg(any(feature = "intl", docs))] + pub fn icu_provider( + mut self, + provider: P, + ) -> Result { + self.icu = Some(icu::Icu::new(provider)?); + Ok(self) + } + + /// Creates a new [`ContextBuilder`] with a default empty [`Interner`] + /// and a default [`BoaProvider`] if the `intl` feature is enabled. + pub fn new() -> Self { + Self::default() + } + + /// Builds a new [`Context`] with the provided parameters, and defaults + /// all missing parameters to their default values. + pub fn build(self) -> Context { + let mut context = Context { + realm: Realm::create(), + interner: self.interner.unwrap_or_default(), + #[cfg(feature = "console")] + console: Console::default(), + intrinsics: Intrinsics::default(), + vm: Vm { + frame: None, + stack: Vec::with_capacity(1024), + trace: false, + stack_size_limit: 1024, + }, + #[cfg(feature = "intl")] + icu: self.icu.unwrap_or_else(|| { + // TODO: Replace with a more fitting default + let provider = icu_testdata::get_provider(); + icu::Icu::new(provider).expect("Failed to initialize default icu data.") + }), + }; + + // Add new builtIns to Context Realm + // At a later date this can be removed from here and called explicitly, + // but for now we almost always want these default builtins + context.intrinsics.objects = IntrinsicObjects::init(&mut context); + context.create_intrinsics(); + context + } } diff --git a/boa_engine/src/object/jsproxy.rs b/boa_engine/src/object/jsproxy.rs index 5987da80ebc..ad9a65867d6 100644 --- a/boa_engine/src/object/jsproxy.rs +++ b/boa_engine/src/object/jsproxy.rs @@ -65,6 +65,7 @@ impl JsObjectType for JsProxy {} /// accessible from [`JsProxy::builder`]; with the [`JsProxyBuilder::build_revocable`] /// method. /// +/// [proxy]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy /// [revocable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/revocable #[derive(Debug, Trace, Finalize)] pub struct JsRevocableProxy { diff --git a/boa_engine/src/object/mod.rs b/boa_engine/src/object/mod.rs index efae88a17b5..5eb4d6e9722 100644 --- a/boa_engine/src/object/mod.rs +++ b/boa_engine/src/object/mod.rs @@ -20,6 +20,8 @@ use self::internal_methods::{ string::STRING_EXOTIC_INTERNAL_METHODS, InternalObjectMethods, ORDINARY_INTERNAL_METHODS, }; +#[cfg(feature = "intl")] +use crate::builtins::intl::date_time_format::DateTimeFormat; use crate::{ builtins::{ array::array_iterator::ArrayIterator, @@ -29,7 +31,6 @@ use crate::{ arguments::ParameterMap, BoundFunction, Captures, Function, NativeFunctionSignature, }, generator::Generator, - intl::date_time_format::DateTimeFormat, map::map_iterator::MapIterator, map::ordered_map::OrderedMap, object::for_in_iterator::ForInIterator, @@ -45,6 +46,7 @@ use crate::{ property::{Attribute, PropertyDescriptor, PropertyKey}, Context, JsBigInt, JsResult, JsString, JsSymbol, JsValue, }; + use boa_gc::{Finalize, Trace}; use boa_interner::Sym; use rustc_hash::FxHashMap; @@ -168,6 +170,7 @@ pub enum ObjectKind { Arguments(Arguments), NativeObject(Box), IntegerIndexed(IntegerIndexed), + #[cfg(feature = "intl")] DateTimeFormat(Box), } @@ -427,6 +430,7 @@ impl ObjectData { } /// Create the `DateTimeFormat` object data + #[cfg(feature = "intl")] pub fn date_time_format(date_time_fmt: Box) -> Self { Self { kind: ObjectKind::DateTimeFormat(date_time_fmt), @@ -467,6 +471,7 @@ impl Display for ObjectKind { Self::NativeObject(_) => "NativeObject", Self::IntegerIndexed(_) => "TypedArray", Self::DataView(_) => "DataView", + #[cfg(feature = "intl")] Self::DateTimeFormat(_) => "DateTimeFormat", }) } diff --git a/boa_engine/src/syntax/parser/tests.rs b/boa_engine/src/syntax/parser/tests.rs index 76bcdec1cfe..5ccc45a0cf5 100644 --- a/boa_engine/src/syntax/parser/tests.rs +++ b/boa_engine/src/syntax/parser/tests.rs @@ -2,6 +2,7 @@ use super::Parser; use crate::{ + context::ContextBuilder, syntax::ast::{ node::{ field::GetConstField, object::PropertyDefinition, ArrowFunctionDecl, Assign, BinOp, @@ -23,7 +24,7 @@ pub(super) fn check_parser(js: &str, expr: L, interner: Interner) where L: Into>, { - let mut context = Context::new(interner); + let mut context = ContextBuilder::default().interner(interner).build(); assert_eq!( Parser::new(js.as_bytes()) .parse_all(&mut context) @@ -469,7 +470,7 @@ fn hashbang_use_strict_no_with() { fn hashbang_use_strict_with_with_statement() { check_parser( r#"#!\"use strict" - + with({}) {} "#, vec![],