diff --git a/Cargo.lock b/Cargo.lock index d4114eff1f..5ec9c48524 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -397,6 +397,7 @@ dependencies = [ "boa_macros", "boa_parser", "boa_profiler", + "boa_types", "bytemuck", "cfg-if", "chrono", @@ -585,6 +586,21 @@ dependencies = [ "toml 0.8.6", ] +[[package]] +name = "boa_types" +version = "0.17.0" +dependencies = [ + "boa_gc", + "boa_macros", + "fast-float", + "num_enum", + "paste", + "phf", + "portable-atomic", + "sptr", + "static_assertions", +] + [[package]] name = "boa_wasm" version = "0.17.0" diff --git a/Cargo.toml b/Cargo.toml index 9028f2dc9b..421e494c24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "boa_examples", "boa_gc", "boa_icu_provider", + "boa_types", "boa_interner", "boa_macros", "boa_macros_tests", @@ -33,6 +34,7 @@ boa_ast = { version = "~0.17.0", path = "boa_ast" } boa_engine = { version = "~0.17.0", path = "boa_engine" } boa_gc = { version = "~0.17.0", path = "boa_gc" } boa_icu_provider = { version = "~0.17.0", path = "boa_icu_provider" } +boa_types = { version = "~0.17.0", path = "boa_types" } boa_interner = { version = "~0.17.0", path = "boa_interner" } boa_macros = { version = "~0.17.0", path = "boa_macros" } boa_parser = { version = "~0.17.0", path = "boa_parser" } diff --git a/boa_engine/Cargo.toml b/boa_engine/Cargo.toml index f6384439c9..1ab62b733a 100644 --- a/boa_engine/Cargo.toml +++ b/boa_engine/Cargo.toml @@ -62,6 +62,7 @@ boa_profiler.workspace = true boa_macros.workspace = true boa_ast.workspace = true boa_parser.workspace = true +boa_types.workspace = true serde = { workspace = true, features = ["derive", "rc"] } serde_json.workspace = true rand = "0.8.5" diff --git a/boa_engine/src/bigint.rs b/boa_engine/src/bigint.rs index 80b3691ed1..3ee13c7893 100644 --- a/boa_engine/src/bigint.rs +++ b/boa_engine/src/bigint.rs @@ -1,6 +1,6 @@ //! Boa's implementation of ECMAScript's bigint primitive type. -use crate::{builtins::Number, error::JsNativeError, JsResult}; +use crate::{builtins::Number, error::JsNativeError, JsResult, JsString}; use num_integer::Integer; use num_traits::{pow::Pow, FromPrimitive, One, ToPrimitive, Zero}; use std::{ @@ -86,6 +86,22 @@ impl JsBigInt { }) } + /// Abstract operation `StringToBigInt ( str )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-stringtobigint + pub(crate) fn from_js_string(string: &JsString) -> Option { + // 1. Let text be ! StringToCodePoints(str). + // 2. Let literal be ParseText(text, StringIntegerLiteral). + // 3. If literal is a List of errors, return undefined. + // 4. Let mv be the MV of literal. + // 5. Assert: mv is an integer. + // 6. Return ℤ(mv). + JsBigInt::from_string(string.to_std_string().ok().as_ref()?) + } + /// This function takes a string and converts it to `BigInt` type. /// /// More information: diff --git a/boa_engine/src/builtins/function/mod.rs b/boa_engine/src/builtins/function/mod.rs index 55aa645c93..693d9ea2c8 100644 --- a/boa_engine/src/builtins/function/mod.rs +++ b/boa_engine/src/builtins/function/mod.rs @@ -22,7 +22,7 @@ use crate::{ object::{JsFunction, PrivateElement, PrivateName}, property::{Attribute, PropertyDescriptor, PropertyKey}, realm::Realm, - string::{common::StaticJsStrings, utf16}, + string::common::StaticJsStrings, symbol::JsSymbol, value::IntegerOrInfinity, vm::{ActiveRunnable, CodeBlock}, @@ -37,6 +37,7 @@ use boa_ast::{ }; use boa_gc::{self, custom_trace, Finalize, Gc, Trace}; use boa_interner::Sym; +use boa_macros::utf16; use boa_parser::{Parser, Source}; use boa_profiler::Profiler; use std::io::Read; diff --git a/boa_engine/src/builtins/json/mod.rs b/boa_engine/src/builtins/json/mod.rs index 524b4e1ba7..ebbaffa3b0 100644 --- a/boa_engine/src/builtins/json/mod.rs +++ b/boa_engine/src/builtins/json/mod.rs @@ -15,6 +15,7 @@ use std::{borrow::Cow, iter::once}; +use boa_macros::utf16; use itertools::Itertools; use crate::{ @@ -26,7 +27,7 @@ use crate::{ object::JsObject, property::{Attribute, PropertyNameKind}, realm::Realm, - string::{common::StaticJsStrings, utf16, CodePoint}, + string::{common::StaticJsStrings, CodePoint}, symbol::JsSymbol, value::IntegerOrInfinity, vm::{CallFrame, CallFrameFlags}, diff --git a/boa_engine/src/builtins/regexp/mod.rs b/boa_engine/src/builtins/regexp/mod.rs index fafeea5f8c..aa0d4a9b2a 100644 --- a/boa_engine/src/builtins/regexp/mod.rs +++ b/boa_engine/src/builtins/regexp/mod.rs @@ -19,11 +19,12 @@ use crate::{ }, property::Attribute, realm::Realm, - string::{common::StaticJsStrings, utf16, CodePoint}, + string::{common::StaticJsStrings, CodePoint}, symbol::JsSymbol, value::JsValue, Context, JsArgs, JsResult, JsString, }; +use boa_macros::utf16; use boa_parser::lexer::regex::RegExpFlags; use boa_profiler::Profiler; use regress::{Flags, Range, Regex}; diff --git a/boa_engine/src/builtins/string/mod.rs b/boa_engine/src/builtins/string/mod.rs index e47bec6c34..833c240969 100644 --- a/boa_engine/src/builtins/string/mod.rs +++ b/boa_engine/src/builtins/string/mod.rs @@ -17,12 +17,13 @@ use crate::{ object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, property::{Attribute, PropertyDescriptor}, realm::Realm, + string::common::StaticJsStrings, string::CodePoint, - string::{common::StaticJsStrings, utf16}, symbol::JsSymbol, value::IntegerOrInfinity, Context, JsArgs, JsResult, JsString, JsValue, }; +use boa_macros::utf16; use boa_profiler::Profiler; use icu_normalizer::{ComposingNormalizer, DecomposingNormalizer}; use std::cmp::{max, min}; diff --git a/boa_engine/src/lib.rs b/boa_engine/src/lib.rs index 0fee87c626..ae33df731d 100644 --- a/boa_engine/src/lib.rs +++ b/boa_engine/src/lib.rs @@ -128,8 +128,6 @@ #[cfg(not(target_has_atomic = "ptr"))] compile_error!("Boa requires a lock free `AtomicUsize` in order to work properly."); -extern crate static_assertions as sa; - pub mod bigint; pub mod builtins; pub mod bytecompiler; @@ -145,15 +143,12 @@ pub mod optimizer; pub mod property; pub mod realm; pub mod script; -pub mod string; -pub mod symbol; pub mod value; pub mod vm; mod host_defined; mod small_map; mod sys; -mod tagged; #[cfg(test)] mod tests; @@ -184,12 +179,16 @@ pub use crate::{ native_function::NativeFunction, object::JsObject, script::Script, - string::JsString, - symbol::JsSymbol, value::JsValue, }; #[doc(inline)] pub use boa_parser::Source; +#[doc(inline)] +pub use boa_types::{ + js_string, + string::{self, JsString}, + symbol::{self, JsSymbol}, +}; /// The result of a Javascript expression is represented like this so it can succeed (`Ok`) or fail (`Err`) pub type JsResult = StdResult; diff --git a/boa_engine/src/value/equality.rs b/boa_engine/src/value/equality.rs index 7e9255c381..faa8d7e284 100644 --- a/boa_engine/src/value/equality.rs +++ b/boa_engine/src/value/equality.rs @@ -65,14 +65,14 @@ impl JsValue { // a. Let n be ! StringToBigInt(y). // b. If n is NaN, return false. // c. Return the result of the comparison x == n. - (Self::BigInt(ref a), Self::String(ref b)) => { - b.to_big_int().as_ref().map_or(false, |b| a == b) - } + (Self::BigInt(ref a), Self::String(ref b)) => JsBigInt::from_js_string(b) + .as_ref() + .map_or(false, |b| a == b), // 7. If Type(x) is String and Type(y) is BigInt, return the result of the comparison y == x. - (Self::String(ref a), Self::BigInt(ref b)) => { - a.to_big_int().as_ref().map_or(false, |a| a == b) - } + (Self::String(ref a), Self::BigInt(ref b)) => JsBigInt::from_js_string(a) + .as_ref() + .map_or(false, |a| a == b), // 8. If Type(x) is Boolean, return the result of the comparison ! ToNumber(x) == y. (Self::Boolean(x), _) => return other.equals(&Self::new(i32::from(*x)), context), diff --git a/boa_engine/src/value/mod.rs b/boa_engine/src/value/mod.rs index 9bb440d89b..9a414ec9d1 100644 --- a/boa_engine/src/value/mod.rs +++ b/boa_engine/src/value/mod.rs @@ -413,7 +413,7 @@ impl JsValue { Self::Undefined => Err(JsNativeError::typ() .with_message("cannot convert undefined to a BigInt") .into()), - Self::String(ref string) => string.to_big_int().map_or_else( + Self::String(ref string) => JsBigInt::from_js_string(string).map_or_else( || { Err(JsNativeError::syntax() .with_message(format!( diff --git a/boa_engine/src/value/operations.rs b/boa_engine/src/value/operations.rs index 44fe42b37a..b837e1870c 100644 --- a/boa_engine/src/value/operations.rs +++ b/boa_engine/src/value/operations.rs @@ -537,11 +537,9 @@ impl JsValue { match (px, py) { (Self::String(ref x), Self::String(ref y)) => (x < y).into(), - (Self::BigInt(ref x), Self::String(ref y)) => y - .to_big_int() + (Self::BigInt(ref x), Self::String(ref y)) => JsBigInt::from_js_string(y) .map_or(AbstractRelation::Undefined, |y| (*x < y).into()), - (Self::String(ref x), Self::BigInt(ref y)) => x - .to_big_int() + (Self::String(ref x), Self::BigInt(ref y)) => JsBigInt::from_js_string(x) .map_or(AbstractRelation::Undefined, |x| (x < *y).into()), (px, py) => match (px.to_numeric(context)?, py.to_numeric(context)?) { (Numeric::Number(x), Numeric::Number(y)) => Number::less_than(x, y), diff --git a/boa_types/ABOUT.md b/boa_types/ABOUT.md new file mode 100644 index 0000000000..0deb7c9a49 --- /dev/null +++ b/boa_types/ABOUT.md @@ -0,0 +1,33 @@ +# About Boa + +Boa is an open-source, experimental ECMAScript Engine written in Rust for +lexing, parsing and executing ECMAScript/JavaScript. Currently, Boa supports some +of the [language][boa-conformance]. More information can be viewed at [Boa's +website][boa-web]. + +Try out the most recent release with Boa's live demo +[playground][boa-playground]. + +# Boa Crates + +- [**`boa_ast`**][ast] - Boa's ECMAScript Abstract Syntax Tree. +- [**`boa_engine`**][engine] - Boa's implementation of ECMAScript builtin objects and + execution. +- [**`boa_gc`**][gc] - Boa's garbage collector. +- [**`boa_interner`**][interner] - Boa's string interner. +- [**`boa_parser`**][parser] - Boa's lexer and parser. +- [**`boa_profiler`**][profiler] - Boa's code profiler. +- [**`boa_icu_provider`**][icu] - Boa's ICU4X data provider. +- [**`boa_runtime`**][runtime] - Boa's WebAPI features. + +[boa-conformance]: https://boajs.dev/boa/test262/ +[boa-web]: https://boajs.dev/ +[boa-playground]: https://boajs.dev/boa/playground/ +[ast]: https://boajs.dev/boa/doc/boa_ast/index.html +[engine]: https://boajs.dev/boa/doc/boa_engine/index.html +[gc]: https://boajs.dev/boa/doc/boa_gc/index.html +[interner]: https://boajs.dev/boa/doc/boa_interner/index.html +[parser]: https://boajs.dev/boa/doc/boa_parser/index.html +[profiler]: https://boajs.dev/boa/doc/boa_profiler/index.html +[icu]: https://boajs.dev/boa/doc/boa_icu_provider/index.html +[runtime]: https://boajs.dev/boa/doc/boa_runtime/index.html diff --git a/boa_types/Cargo.toml b/boa_types/Cargo.toml new file mode 100644 index 0000000000..9604c38a2f --- /dev/null +++ b/boa_types/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "boa_types" +description = "String for the Boa JavaScript engine." +keywords = ["javascript", "js", "string"] +categories = ["data-structures", "no-std"] +version.workspace = true +edition.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +boa_gc.workspace = true +fast-float.workspace = true +sptr = "0.3.2" +static_assertions.workspace = true +num_enum = "0.7.0" +paste = "1.0" +portable-atomic = "1.5.0" +phf.workspace = true + +[dev-dependencies] +boa_macros.workspace = true diff --git a/boa_types/src/lib.rs b/boa_types/src/lib.rs new file mode 100644 index 0000000000..5fa18bd524 --- /dev/null +++ b/boa_types/src/lib.rs @@ -0,0 +1,100 @@ +//! Boa's **`boa_interner`** is a string interner for compiler performance. +//! +//! # Crate Overview +//! +//! The idea behind using a string interner is that in most of the code, strings such as +//! identifiers and literals are often repeated. This causes extra burden when comparing them and +//! storing them. A string interner stores a unique `usize` symbol for each string, making sure +//! that there are no duplicates. This makes it much easier to compare, since it's just comparing +//! to `usize`, and also it's easier to store, since instead of a heap-allocated string, you only +//! need to store a `usize`. This reduces memory consumption and improves performance in the +//! compiler. +#![doc = include_str!("../ABOUT.md")] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg" +)] +#![cfg_attr(not(test), forbid(clippy::unwrap_used))] +#![warn( + // rustc lint groups https://doc.rust-lang.org/rustc/lints/groups.html + warnings, + future_incompatible, + let_underscore, + nonstandard_style, + rust_2018_compatibility, + rust_2018_idioms, + rust_2021_compatibility, + unused, + + // rustc allowed-by-default lints https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html + missing_docs, + macro_use_extern_crate, + meta_variable_misuse, + missing_abi, + missing_copy_implementations, + missing_debug_implementations, + non_ascii_idents, + noop_method_call, + single_use_lifetimes, + trivial_casts, + trivial_numeric_casts, + unreachable_pub, + unsafe_op_in_unsafe_fn, + unused_crate_dependencies, + unused_import_braces, + unused_lifetimes, + unused_qualifications, + unused_tuple_struct_fields, + variant_size_differences, + + // rustdoc lints https://doc.rust-lang.org/rustdoc/lints.html + rustdoc::broken_intra_doc_links, + rustdoc::private_intra_doc_links, + rustdoc::missing_crate_level_docs, + rustdoc::private_doc_tests, + rustdoc::invalid_codeblock_attributes, + rustdoc::invalid_rust_codeblocks, + rustdoc::bare_urls, + + // clippy allowed by default + clippy::dbg_macro, + + // clippy categories https://doc.rust-lang.org/clippy/ + clippy::all, + clippy::correctness, + clippy::suspicious, + clippy::style, + clippy::complexity, + clippy::perf, + clippy::pedantic, +)] +#![allow( + clippy::redundant_pub_crate, + // TODO deny once false positive is fixed (https://github.com/rust-lang/rust-clippy/issues/9626). + clippy::trait_duplication_in_bounds +)] + +/// Helper function to check if a `char` is trimmable. +#[must_use] +pub const fn is_trimmable_whitespace(c: char) -> bool { + // The rust implementation of `trim` does not regard the same characters whitespace as ecma standard does + // + // Rust uses \p{White_Space} by default, which also includes: + // `\u{0085}' (next line) + // And does not include: + // '\u{FEFF}' (zero width non-breaking space) + // Explicit whitespace: https://tc39.es/ecma262/#sec-white-space + matches!( + c, + '\u{0009}' | '\u{000B}' | '\u{000C}' | '\u{0020}' | '\u{00A0}' | '\u{FEFF}' | + // Unicode Space_Separator category + '\u{1680}' | '\u{2000}' + ..='\u{200A}' | '\u{202F}' | '\u{205F}' | '\u{3000}' | + // Line terminators: https://tc39.es/ecma262/#sec-line-terminators + '\u{000A}' | '\u{000D}' | '\u{2028}' | '\u{2029}' + ) +} + +pub mod string; +pub mod symbol; +pub(crate) mod tagged; diff --git a/boa_engine/src/string/common.rs b/boa_types/src/string/common.rs similarity index 99% rename from boa_engine/src/string/common.rs rename to boa_types/src/string/common.rs index b98481b70a..5f8e0c884a 100644 --- a/boa_engine/src/string/common.rs +++ b/boa_types/src/string/common.rs @@ -1,3 +1,5 @@ +//! List of commonly used strings in Javascript code. + use crate::tagged::Tagged; use super::JsString; @@ -8,8 +10,7 @@ macro_rules! well_known_statics { $( paste!{ #[doc = "Gets the static `JsString` for `\"" $string "\"`."] - #[allow(unused)] - pub(crate) const $name: JsString = JsString { + pub const $name: JsString = JsString { ptr: Tagged::from_tag( Self::find_index($string), ), @@ -22,8 +23,8 @@ macro_rules! well_known_statics { /// List of commonly used strings in Javascript code. /// /// Any strings defined here are used as a static [`JsString`] instead of allocating on the heap. -#[derive(Debug)] -pub(crate) struct StaticJsStrings; +#[derive(Debug, Clone, Copy)] +pub struct StaticJsStrings; impl StaticJsStrings { // useful to search at compile time a certain string in the array diff --git a/boa_engine/src/string/mod.rs b/boa_types/src/string/mod.rs similarity index 96% rename from boa_engine/src/string/mod.rs rename to boa_types/src/string/mod.rs index 0362478606..582ecc2e5e 100644 --- a/boa_engine/src/string/mod.rs +++ b/boa_types/src/string/mod.rs @@ -21,17 +21,12 @@ // the same names from the unstable functions of the `std::ptr` module. #![allow(unstable_name_collisions)] -pub(crate) mod common; +pub mod common; mod slice; mod str; -use crate::{ - builtins::string::is_trimmable_whitespace, - tagged::{Tagged, UnwrappedTagged}, - JsBigInt, -}; +use crate::tagged::{Tagged, UnwrappedTagged}; use boa_gc::{empty_trace, Finalize, Trace}; -pub use boa_macros::utf16; #[doc(inline)] pub use crate::string::{ @@ -64,7 +59,6 @@ fn alloc_overflow() -> ! { /// /// ``` /// use boa_engine::js_string; -/// use boa_engine::string::utf16; /// /// let empty_str = js_string!(); /// assert!(empty_str.is_empty()); @@ -76,7 +70,6 @@ fn alloc_overflow() -> ! { /// /// ``` /// # use boa_engine::js_string; -/// # use boa_engine::string::utf16; /// let hw = js_string!("Hello, world!"); /// assert_eq!(&hw, "Hello, world!"); /// ``` @@ -93,7 +86,6 @@ fn alloc_overflow() -> ! { /// /// ``` /// # use boa_engine::js_string; -/// # use boa_engine::string::utf16; /// const NAME: &[u16] = "human! "; /// let greeting = js_string!("Hello, "); /// let msg = js_string!(&greeting, &NAME, "Nice to meet you!"); @@ -101,21 +93,22 @@ fn alloc_overflow() -> ! { /// assert_eq!(&msg, "Hello, human! Nice to meet you!"); /// ``` #[macro_export] +#[allow(clippy::module_name_repetitions)] macro_rules! js_string { () => { - $crate::JsString::default() + $crate::string::JsString::default() }; ($s:literal) => { - $crate::JsString::from($s) + $crate::string::JsString::from($s) }; ($s:expr) => { - $crate::JsString::from($s) + $crate::string::JsString::from($s) }; ( $x:expr, $y:expr ) => { - $crate::JsString::concat($crate::string::JsStringSlice::from($x), $crate::string::JsStringSlice::from($y)) + $crate::string::JsString::concat($crate::string::JsStringSlice::from($x), $crate::string::JsStringSlice::from($y)) }; ( $( $s:expr ),+ ) => { - $crate::JsString::concat_array(&[ $( $crate::string::JsStringSlice::from($s) ),+ ]) + $crate::string::JsString::concat_array(&[ $( $crate::string::JsStringSlice::from($s) ),+ ]) }; } @@ -212,12 +205,13 @@ const DATA_OFFSET: usize = std::mem::size_of::(); /// [`JsString`] implements [Deref], inheriting all of /// \[u16\]'s methods. #[derive(Finalize)] +#[allow(clippy::module_name_repetitions)] pub struct JsString { ptr: Tagged, } // JsString should always be pointer sized. -sa::assert_eq_size!(JsString, *const ()); +static_assertions::assert_eq_size!(JsString, *const ()); // Safety: `JsString` does not contain any objects which needs to be traced, so this is safe. unsafe impl Trace for JsString { @@ -435,6 +429,9 @@ impl JsString { } /// Decodes a [`JsString`] into a [`String`], returning + /// + /// # Errors + /// /// [`FromUtf16Error`][std::string::FromUtf16Error] if it contains any invalid data. pub fn to_std_string(&self) -> Result { match self.as_str().variant() { @@ -530,7 +527,7 @@ impl JsString { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-stringindexof - pub(crate) fn index_of(&self, search_value: &JsStr<'_>, from_index: usize) -> Option { + pub fn index_of(&self, search_value: &JsStr<'_>, from_index: usize) -> Option { let this = self.iter().collect::>(); let search_value = search_value.iter().collect::>(); @@ -573,7 +570,12 @@ impl JsString { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-codepointat - pub(crate) fn code_point_at(&self, position: usize) -> CodePoint { + /// + /// # Panics + /// + /// If `position` is smaller than size of string. + #[must_use] + pub fn code_point_at(&self, position: usize) -> CodePoint { // 1. Let size be the length of string. let size = self.len(); @@ -613,7 +615,7 @@ impl JsString { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-stringtonumber - pub(crate) fn to_number(&self) -> f64 { + pub fn to_number(&self) -> f64 { // 1. Let text be ! StringToCodePoints(str). // 2. Let literal be ParseText(text, StringNumericLiteral). let Ok(string) = self.to_std_string() else { @@ -621,7 +623,7 @@ impl JsString { return f64::NAN; }; // 4. Return StringNumericValue of literal. - let string = string.trim_matches(is_trimmable_whitespace); + let string = string.trim_matches(crate::is_trimmable_whitespace); match string { "" => return 0.0, "-Infinity" => return f64::NEG_INFINITY, @@ -668,22 +670,6 @@ impl JsString { fast_float::parse(string).unwrap_or(f64::NAN) } - /// Abstract operation `StringToBigInt ( str )` - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - /// [spec]: https://tc39.es/ecma262/#sec-stringtobigint - pub(crate) fn to_big_int(&self) -> Option { - // 1. Let text be ! StringToCodePoints(str). - // 2. Let literal be ParseText(text, StringIntegerLiteral). - // 3. If literal is a List of errors, return undefined. - // 4. Let mv be the MV of literal. - // 5. Assert: mv is an integer. - // 6. Return ℤ(mv). - JsBigInt::from_string(self.to_std_string().ok().as_ref()?) - } - /// Allocates a new [`RawJsString`] with an internal capacity of `str_len` chars. /// /// # Panics @@ -866,15 +852,15 @@ impl JsString { } } - pub(crate) fn trim(&self) -> JsStringSlice<'_> { + pub fn trim(&self) -> JsStringSlice<'_> { self.as_str().trim() } - pub(crate) fn trim_start(&self) -> JsStringSlice<'_> { + pub fn trim_start(&self) -> JsStringSlice<'_> { self.as_str().trim_start() } - pub(crate) fn trim_end(&self) -> JsStringSlice<'_> { + pub fn trim_end(&self) -> JsStringSlice<'_> { self.as_str().trim_end() } @@ -1172,8 +1158,8 @@ impl ToStringEscaped for [u16] { mod tests { use crate::tagged::UnwrappedTagged; - use super::utf16; use super::JsString; + use boa_macros::utf16; impl JsString { /// Gets the number of `JsString`s which point to this allocation. diff --git a/boa_engine/src/string/slice.rs b/boa_types/src/string/slice.rs similarity index 89% rename from boa_engine/src/string/slice.rs rename to boa_types/src/string/slice.rs index e386be9ff3..ac7d88f190 100644 --- a/boa_engine/src/string/slice.rs +++ b/boa_types/src/string/slice.rs @@ -1,4 +1,24 @@ -use crate::{builtins::string::is_trimmable_whitespace, JsString}; +use crate::string::JsString; + +/// Helper function to check if a `char` is trimmable. +pub(crate) const fn is_trimmable_whitespace(c: char) -> bool { + // The rust implementation of `trim` does not regard the same characters whitespace as ecma standard does + // + // Rust uses \p{White_Space} by default, which also includes: + // `\u{0085}' (next line) + // And does not include: + // '\u{FEFF}' (zero width non-breaking space) + // Explicit whitespace: https://tc39.es/ecma262/#sec-white-space + matches!( + c, + '\u{0009}' | '\u{000B}' | '\u{000C}' | '\u{0020}' | '\u{00A0}' | '\u{FEFF}' | + // Unicode Space_Separator category + '\u{1680}' | '\u{2000}' + ..='\u{200A}' | '\u{202F}' | '\u{205F}' | '\u{3000}' | + // Line terminators: https://tc39.es/ecma262/#sec-line-terminators + '\u{000A}' | '\u{000D}' | '\u{2028}' | '\u{2029}' + ) +} use super::{is_ascii, JsStr, JsStrVariant}; diff --git a/boa_engine/src/string/str.rs b/boa_types/src/string/str.rs similarity index 94% rename from boa_engine/src/string/str.rs rename to boa_types/src/string/str.rs index 6413c243e2..e62a58470e 100644 --- a/boa_engine/src/string/str.rs +++ b/boa_types/src/string/str.rs @@ -1,7 +1,6 @@ use std::slice::SliceIndex; use crate::string::Iter; -use boa_interner::JStrRef; use super::JsStringSlice; @@ -75,13 +74,6 @@ impl<'a> JsStr<'a> { Iter::new(self.into()) } - pub(crate) fn as_str_ref(&self) -> JStrRef<'_> { - match self.inner { - JsStrVariant::Ascii(s) => JStrRef::Utf8(s), - JsStrVariant::U16(s) => JStrRef::Utf16(s), - } - } - #[inline] #[must_use] pub fn is_empty(&self) -> bool { diff --git a/boa_engine/src/symbol.rs b/boa_types/src/symbol.rs similarity index 98% rename from boa_engine/src/symbol.rs rename to boa_types/src/symbol.rs index ce625158c8..afd496e5cd 100644 --- a/boa_engine/src/symbol.rs +++ b/boa_types/src/symbol.rs @@ -23,9 +23,8 @@ use crate::{ js_string, - string::common::StaticJsStrings, + string::{common::StaticJsStrings, JsString}, tagged::{Tagged, UnwrappedTagged}, - JsString, }; use boa_gc::{empty_trace, Finalize, Trace}; @@ -131,6 +130,7 @@ struct Inner { } /// This represents a JavaScript symbol primitive. +#[allow(clippy::module_name_repetitions)] pub struct JsSymbol { repr: Tagged, } @@ -151,7 +151,10 @@ unsafe impl Trace for JsSymbol { macro_rules! well_known_symbols { ( $( $(#[$attr:meta])* ($name:ident, $variant:path) ),+$(,)? ) => { $( - $(#[$attr])* pub(crate) const fn $name() -> JsSymbol { + $(#[$attr])* + #[must_use] + #[inline] + pub const fn $name() -> JsSymbol { JsSymbol { // the cast shouldn't matter since we only have 127 const symbols repr: Tagged::from_tag($variant.hash() as usize), diff --git a/boa_engine/src/tagged.rs b/boa_types/src/tagged.rs similarity index 100% rename from boa_engine/src/tagged.rs rename to boa_types/src/tagged.rs