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..d0cff263474
--- /dev/null
+++ b/boa_engine/src/context/icu.rs
@@ -0,0 +1,81 @@
+use std::rc::Rc;
+
+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: Rc,
+ 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: Rc) -> Result {
+ Ok(Self {
+ locale_canonicalizer: LocaleCanonicalizer::new(&*provider)?,
+ 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..73ac3ee826b 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,90 @@ 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 in a valid 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: std::rc::Rc,
+ ) -> 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
+ icu::Icu::new(std::rc::Rc::new(icu_testdata::get_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/lib.rs b/boa_engine/src/lib.rs
index 65d128ec149..86831460edb 100644
--- a/boa_engine/src/lib.rs
+++ b/boa_engine/src/lib.rs
@@ -3,8 +3,12 @@
//!
//! # Crate Features
//! - **serde** - Enables serialization and deserialization of the AST (Abstract Syntax Tree).
-//! - **console** - Enables `boa`s WHATWG `console` object implementation.
+//! - **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)
+//!
+//! [whatwg]: https://console.spec.whatwg.org
+//! [ecma-402]: https://402.ecma-international.org
#![doc(
html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg",
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![],