Browse Source

Move JsString and Symbol to boa_types crate

refactor/interner
Haled Odat 1 year ago
parent
commit
943834c543
  1. 16
      Cargo.lock
  2. 2
      Cargo.toml
  3. 1
      boa_engine/Cargo.toml
  4. 18
      boa_engine/src/bigint.rs
  5. 3
      boa_engine/src/builtins/function/mod.rs
  6. 3
      boa_engine/src/builtins/json/mod.rs
  7. 3
      boa_engine/src/builtins/regexp/mod.rs
  8. 3
      boa_engine/src/builtins/string/mod.rs
  9. 13
      boa_engine/src/lib.rs
  10. 12
      boa_engine/src/value/equality.rs
  11. 2
      boa_engine/src/value/mod.rs
  12. 6
      boa_engine/src/value/operations.rs
  13. 33
      boa_types/ABOUT.md
  14. 24
      boa_types/Cargo.toml
  15. 100
      boa_types/src/lib.rs
  16. 9
      boa_types/src/string/common.rs
  17. 66
      boa_types/src/string/mod.rs
  18. 22
      boa_types/src/string/slice.rs
  19. 8
      boa_types/src/string/str.rs
  20. 9
      boa_types/src/symbol.rs
  21. 0
      boa_types/src/tagged.rs

16
Cargo.lock generated

@ -397,6 +397,7 @@ dependencies = [
"boa_macros", "boa_macros",
"boa_parser", "boa_parser",
"boa_profiler", "boa_profiler",
"boa_types",
"bytemuck", "bytemuck",
"cfg-if", "cfg-if",
"chrono", "chrono",
@ -585,6 +586,21 @@ dependencies = [
"toml 0.8.6", "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]] [[package]]
name = "boa_wasm" name = "boa_wasm"
version = "0.17.0" version = "0.17.0"

2
Cargo.toml

@ -7,6 +7,7 @@ members = [
"boa_examples", "boa_examples",
"boa_gc", "boa_gc",
"boa_icu_provider", "boa_icu_provider",
"boa_types",
"boa_interner", "boa_interner",
"boa_macros", "boa_macros",
"boa_macros_tests", "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_engine = { version = "~0.17.0", path = "boa_engine" }
boa_gc = { version = "~0.17.0", path = "boa_gc" } boa_gc = { version = "~0.17.0", path = "boa_gc" }
boa_icu_provider = { version = "~0.17.0", path = "boa_icu_provider" } 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_interner = { version = "~0.17.0", path = "boa_interner" }
boa_macros = { version = "~0.17.0", path = "boa_macros" } boa_macros = { version = "~0.17.0", path = "boa_macros" }
boa_parser = { version = "~0.17.0", path = "boa_parser" } boa_parser = { version = "~0.17.0", path = "boa_parser" }

1
boa_engine/Cargo.toml

@ -62,6 +62,7 @@ boa_profiler.workspace = true
boa_macros.workspace = true boa_macros.workspace = true
boa_ast.workspace = true boa_ast.workspace = true
boa_parser.workspace = true boa_parser.workspace = true
boa_types.workspace = true
serde = { workspace = true, features = ["derive", "rc"] } serde = { workspace = true, features = ["derive", "rc"] }
serde_json.workspace = true serde_json.workspace = true
rand = "0.8.5" rand = "0.8.5"

18
boa_engine/src/bigint.rs

@ -1,6 +1,6 @@
//! Boa's implementation of ECMAScript's bigint primitive type. //! 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_integer::Integer;
use num_traits::{pow::Pow, FromPrimitive, One, ToPrimitive, Zero}; use num_traits::{pow::Pow, FromPrimitive, One, ToPrimitive, Zero};
use std::{ 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<JsBigInt> {
// 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. /// This function takes a string and converts it to `BigInt` type.
/// ///
/// More information: /// More information:

3
boa_engine/src/builtins/function/mod.rs

@ -22,7 +22,7 @@ use crate::{
object::{JsFunction, PrivateElement, PrivateName}, object::{JsFunction, PrivateElement, PrivateName},
property::{Attribute, PropertyDescriptor, PropertyKey}, property::{Attribute, PropertyDescriptor, PropertyKey},
realm::Realm, realm::Realm,
string::{common::StaticJsStrings, utf16}, string::common::StaticJsStrings,
symbol::JsSymbol, symbol::JsSymbol,
value::IntegerOrInfinity, value::IntegerOrInfinity,
vm::{ActiveRunnable, CodeBlock}, vm::{ActiveRunnable, CodeBlock},
@ -37,6 +37,7 @@ use boa_ast::{
}; };
use boa_gc::{self, custom_trace, Finalize, Gc, Trace}; use boa_gc::{self, custom_trace, Finalize, Gc, Trace};
use boa_interner::Sym; use boa_interner::Sym;
use boa_macros::utf16;
use boa_parser::{Parser, Source}; use boa_parser::{Parser, Source};
use boa_profiler::Profiler; use boa_profiler::Profiler;
use std::io::Read; use std::io::Read;

3
boa_engine/src/builtins/json/mod.rs

@ -15,6 +15,7 @@
use std::{borrow::Cow, iter::once}; use std::{borrow::Cow, iter::once};
use boa_macros::utf16;
use itertools::Itertools; use itertools::Itertools;
use crate::{ use crate::{
@ -26,7 +27,7 @@ use crate::{
object::JsObject, object::JsObject,
property::{Attribute, PropertyNameKind}, property::{Attribute, PropertyNameKind},
realm::Realm, realm::Realm,
string::{common::StaticJsStrings, utf16, CodePoint}, string::{common::StaticJsStrings, CodePoint},
symbol::JsSymbol, symbol::JsSymbol,
value::IntegerOrInfinity, value::IntegerOrInfinity,
vm::{CallFrame, CallFrameFlags}, vm::{CallFrame, CallFrameFlags},

3
boa_engine/src/builtins/regexp/mod.rs

@ -19,11 +19,12 @@ use crate::{
}, },
property::Attribute, property::Attribute,
realm::Realm, realm::Realm,
string::{common::StaticJsStrings, utf16, CodePoint}, string::{common::StaticJsStrings, CodePoint},
symbol::JsSymbol, symbol::JsSymbol,
value::JsValue, value::JsValue,
Context, JsArgs, JsResult, JsString, Context, JsArgs, JsResult, JsString,
}; };
use boa_macros::utf16;
use boa_parser::lexer::regex::RegExpFlags; use boa_parser::lexer::regex::RegExpFlags;
use boa_profiler::Profiler; use boa_profiler::Profiler;
use regress::{Flags, Range, Regex}; use regress::{Flags, Range, Regex};

3
boa_engine/src/builtins/string/mod.rs

@ -17,12 +17,13 @@ use crate::{
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData}, object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::{Attribute, PropertyDescriptor}, property::{Attribute, PropertyDescriptor},
realm::Realm, realm::Realm,
string::common::StaticJsStrings,
string::CodePoint, string::CodePoint,
string::{common::StaticJsStrings, utf16},
symbol::JsSymbol, symbol::JsSymbol,
value::IntegerOrInfinity, value::IntegerOrInfinity,
Context, JsArgs, JsResult, JsString, JsValue, Context, JsArgs, JsResult, JsString, JsValue,
}; };
use boa_macros::utf16;
use boa_profiler::Profiler; use boa_profiler::Profiler;
use icu_normalizer::{ComposingNormalizer, DecomposingNormalizer}; use icu_normalizer::{ComposingNormalizer, DecomposingNormalizer};
use std::cmp::{max, min}; use std::cmp::{max, min};

13
boa_engine/src/lib.rs

@ -128,8 +128,6 @@
#[cfg(not(target_has_atomic = "ptr"))] #[cfg(not(target_has_atomic = "ptr"))]
compile_error!("Boa requires a lock free `AtomicUsize` in order to work properly."); compile_error!("Boa requires a lock free `AtomicUsize` in order to work properly.");
extern crate static_assertions as sa;
pub mod bigint; pub mod bigint;
pub mod builtins; pub mod builtins;
pub mod bytecompiler; pub mod bytecompiler;
@ -145,15 +143,12 @@ pub mod optimizer;
pub mod property; pub mod property;
pub mod realm; pub mod realm;
pub mod script; pub mod script;
pub mod string;
pub mod symbol;
pub mod value; pub mod value;
pub mod vm; pub mod vm;
mod host_defined; mod host_defined;
mod small_map; mod small_map;
mod sys; mod sys;
mod tagged;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -184,12 +179,16 @@ pub use crate::{
native_function::NativeFunction, native_function::NativeFunction,
object::JsObject, object::JsObject,
script::Script, script::Script,
string::JsString,
symbol::JsSymbol,
value::JsValue, value::JsValue,
}; };
#[doc(inline)] #[doc(inline)]
pub use boa_parser::Source; 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`) /// The result of a Javascript expression is represented like this so it can succeed (`Ok`) or fail (`Err`)
pub type JsResult<T> = StdResult<T, JsError>; pub type JsResult<T> = StdResult<T, JsError>;

12
boa_engine/src/value/equality.rs

@ -65,14 +65,14 @@ impl JsValue {
// a. Let n be ! StringToBigInt(y). // a. Let n be ! StringToBigInt(y).
// b. If n is NaN, return false. // b. If n is NaN, return false.
// c. Return the result of the comparison x == n. // c. Return the result of the comparison x == n.
(Self::BigInt(ref a), Self::String(ref b)) => { (Self::BigInt(ref a), Self::String(ref b)) => JsBigInt::from_js_string(b)
b.to_big_int().as_ref().map_or(false, |b| a == 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. // 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)) => { (Self::String(ref a), Self::BigInt(ref b)) => JsBigInt::from_js_string(a)
a.to_big_int().as_ref().map_or(false, |a| a == b) .as_ref()
} .map_or(false, |a| a == b),
// 8. If Type(x) is Boolean, return the result of the comparison ! ToNumber(x) == y. // 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), (Self::Boolean(x), _) => return other.equals(&Self::new(i32::from(*x)), context),

2
boa_engine/src/value/mod.rs

@ -413,7 +413,7 @@ impl JsValue {
Self::Undefined => Err(JsNativeError::typ() Self::Undefined => Err(JsNativeError::typ()
.with_message("cannot convert undefined to a BigInt") .with_message("cannot convert undefined to a BigInt")
.into()), .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() Err(JsNativeError::syntax()
.with_message(format!( .with_message(format!(

6
boa_engine/src/value/operations.rs

@ -537,11 +537,9 @@ impl JsValue {
match (px, py) { match (px, py) {
(Self::String(ref x), Self::String(ref y)) => (x < y).into(), (Self::String(ref x), Self::String(ref y)) => (x < y).into(),
(Self::BigInt(ref x), Self::String(ref y)) => y (Self::BigInt(ref x), Self::String(ref y)) => JsBigInt::from_js_string(y)
.to_big_int()
.map_or(AbstractRelation::Undefined, |y| (*x < y).into()), .map_or(AbstractRelation::Undefined, |y| (*x < y).into()),
(Self::String(ref x), Self::BigInt(ref y)) => x (Self::String(ref x), Self::BigInt(ref y)) => JsBigInt::from_js_string(x)
.to_big_int()
.map_or(AbstractRelation::Undefined, |x| (x < *y).into()), .map_or(AbstractRelation::Undefined, |x| (x < *y).into()),
(px, py) => match (px.to_numeric(context)?, py.to_numeric(context)?) { (px, py) => match (px.to_numeric(context)?, py.to_numeric(context)?) {
(Numeric::Number(x), Numeric::Number(y)) => Number::less_than(x, y), (Numeric::Number(x), Numeric::Number(y)) => Number::less_than(x, y),

33
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

24
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

100
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;

9
boa_engine/src/string/common.rs → boa_types/src/string/common.rs

@ -1,3 +1,5 @@
//! List of commonly used strings in Javascript code.
use crate::tagged::Tagged; use crate::tagged::Tagged;
use super::JsString; use super::JsString;
@ -8,8 +10,7 @@ macro_rules! well_known_statics {
$( $(
paste!{ paste!{
#[doc = "Gets the static `JsString` for `\"" $string "\"`."] #[doc = "Gets the static `JsString` for `\"" $string "\"`."]
#[allow(unused)] pub const $name: JsString = JsString {
pub(crate) const $name: JsString = JsString {
ptr: Tagged::from_tag( ptr: Tagged::from_tag(
Self::find_index($string), Self::find_index($string),
), ),
@ -22,8 +23,8 @@ macro_rules! well_known_statics {
/// List of commonly used strings in Javascript code. /// List of commonly used strings in Javascript code.
/// ///
/// Any strings defined here are used as a static [`JsString`] instead of allocating on the heap. /// Any strings defined here are used as a static [`JsString`] instead of allocating on the heap.
#[derive(Debug)] #[derive(Debug, Clone, Copy)]
pub(crate) struct StaticJsStrings; pub struct StaticJsStrings;
impl StaticJsStrings { impl StaticJsStrings {
// useful to search at compile time a certain string in the array // useful to search at compile time a certain string in the array

66
boa_engine/src/string/mod.rs → boa_types/src/string/mod.rs

@ -21,17 +21,12 @@
// the same names from the unstable functions of the `std::ptr` module. // the same names from the unstable functions of the `std::ptr` module.
#![allow(unstable_name_collisions)] #![allow(unstable_name_collisions)]
pub(crate) mod common; pub mod common;
mod slice; mod slice;
mod str; mod str;
use crate::{ use crate::tagged::{Tagged, UnwrappedTagged};
builtins::string::is_trimmable_whitespace,
tagged::{Tagged, UnwrappedTagged},
JsBigInt,
};
use boa_gc::{empty_trace, Finalize, Trace}; use boa_gc::{empty_trace, Finalize, Trace};
pub use boa_macros::utf16;
#[doc(inline)] #[doc(inline)]
pub use crate::string::{ pub use crate::string::{
@ -64,7 +59,6 @@ fn alloc_overflow() -> ! {
/// ///
/// ``` /// ```
/// use boa_engine::js_string; /// use boa_engine::js_string;
/// use boa_engine::string::utf16;
/// ///
/// let empty_str = js_string!(); /// let empty_str = js_string!();
/// assert!(empty_str.is_empty()); /// assert!(empty_str.is_empty());
@ -76,7 +70,6 @@ fn alloc_overflow() -> ! {
/// ///
/// ``` /// ```
/// # use boa_engine::js_string; /// # use boa_engine::js_string;
/// # use boa_engine::string::utf16;
/// let hw = js_string!("Hello, world!"); /// let hw = js_string!("Hello, world!");
/// assert_eq!(&hw, "Hello, world!"); /// assert_eq!(&hw, "Hello, world!");
/// ``` /// ```
@ -93,7 +86,6 @@ fn alloc_overflow() -> ! {
/// ///
/// ``` /// ```
/// # use boa_engine::js_string; /// # use boa_engine::js_string;
/// # use boa_engine::string::utf16;
/// const NAME: &[u16] = "human! "; /// const NAME: &[u16] = "human! ";
/// let greeting = js_string!("Hello, "); /// let greeting = js_string!("Hello, ");
/// let msg = js_string!(&greeting, &NAME, "Nice to meet you!"); /// 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!"); /// assert_eq!(&msg, "Hello, human! Nice to meet you!");
/// ``` /// ```
#[macro_export] #[macro_export]
#[allow(clippy::module_name_repetitions)]
macro_rules! js_string { macro_rules! js_string {
() => { () => {
$crate::JsString::default() $crate::string::JsString::default()
}; };
($s:literal) => { ($s:literal) => {
$crate::JsString::from($s) $crate::string::JsString::from($s)
}; };
($s:expr) => { ($s:expr) => {
$crate::JsString::from($s) $crate::string::JsString::from($s)
}; };
( $x:expr, $y:expr ) => { ( $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 ),+ ) => { ( $( $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::<RawJsString>();
/// [`JsString`] implements <code>[Deref]<Target = \[u16\]></code>, inheriting all of /// [`JsString`] implements <code>[Deref]<Target = \[u16\]></code>, inheriting all of
/// <code>\[u16\]</code>'s methods. /// <code>\[u16\]</code>'s methods.
#[derive(Finalize)] #[derive(Finalize)]
#[allow(clippy::module_name_repetitions)]
pub struct JsString { pub struct JsString {
ptr: Tagged<RawJsString>, ptr: Tagged<RawJsString>,
} }
// JsString should always be pointer sized. // 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. // Safety: `JsString` does not contain any objects which needs to be traced, so this is safe.
unsafe impl Trace for JsString { unsafe impl Trace for JsString {
@ -435,6 +429,9 @@ impl JsString {
} }
/// Decodes a [`JsString`] into a [`String`], returning /// Decodes a [`JsString`] into a [`String`], returning
///
/// # Errors
///
/// [`FromUtf16Error`][std::string::FromUtf16Error] if it contains any invalid data. /// [`FromUtf16Error`][std::string::FromUtf16Error] if it contains any invalid data.
pub fn to_std_string(&self) -> Result<String, std::string::FromUtf16Error> { pub fn to_std_string(&self) -> Result<String, std::string::FromUtf16Error> {
match self.as_str().variant() { match self.as_str().variant() {
@ -530,7 +527,7 @@ impl JsString {
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-stringindexof /// [spec]: https://tc39.es/ecma262/#sec-stringindexof
pub(crate) fn index_of(&self, search_value: &JsStr<'_>, from_index: usize) -> Option<usize> { pub fn index_of(&self, search_value: &JsStr<'_>, from_index: usize) -> Option<usize> {
let this = self.iter().collect::<Vec<_>>(); let this = self.iter().collect::<Vec<_>>();
let search_value = search_value.iter().collect::<Vec<_>>(); let search_value = search_value.iter().collect::<Vec<_>>();
@ -573,7 +570,12 @@ impl JsString {
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-codepointat /// [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. // 1. Let size be the length of string.
let size = self.len(); let size = self.len();
@ -613,7 +615,7 @@ impl JsString {
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-stringtonumber /// [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). // 1. Let text be ! StringToCodePoints(str).
// 2. Let literal be ParseText(text, StringNumericLiteral). // 2. Let literal be ParseText(text, StringNumericLiteral).
let Ok(string) = self.to_std_string() else { let Ok(string) = self.to_std_string() else {
@ -621,7 +623,7 @@ impl JsString {
return f64::NAN; return f64::NAN;
}; };
// 4. Return StringNumericValue of literal. // 4. Return StringNumericValue of literal.
let string = string.trim_matches(is_trimmable_whitespace); let string = string.trim_matches(crate::is_trimmable_whitespace);
match string { match string {
"" => return 0.0, "" => return 0.0,
"-Infinity" => return f64::NEG_INFINITY, "-Infinity" => return f64::NEG_INFINITY,
@ -668,22 +670,6 @@ impl JsString {
fast_float::parse(string).unwrap_or(f64::NAN) 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<JsBigInt> {
// 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. /// Allocates a new [`RawJsString`] with an internal capacity of `str_len` chars.
/// ///
/// # Panics /// # Panics
@ -866,15 +852,15 @@ impl JsString {
} }
} }
pub(crate) fn trim(&self) -> JsStringSlice<'_> { pub fn trim(&self) -> JsStringSlice<'_> {
self.as_str().trim() self.as_str().trim()
} }
pub(crate) fn trim_start(&self) -> JsStringSlice<'_> { pub fn trim_start(&self) -> JsStringSlice<'_> {
self.as_str().trim_start() self.as_str().trim_start()
} }
pub(crate) fn trim_end(&self) -> JsStringSlice<'_> { pub fn trim_end(&self) -> JsStringSlice<'_> {
self.as_str().trim_end() self.as_str().trim_end()
} }
@ -1172,8 +1158,8 @@ impl ToStringEscaped for [u16] {
mod tests { mod tests {
use crate::tagged::UnwrappedTagged; use crate::tagged::UnwrappedTagged;
use super::utf16;
use super::JsString; use super::JsString;
use boa_macros::utf16;
impl JsString { impl JsString {
/// Gets the number of `JsString`s which point to this allocation. /// Gets the number of `JsString`s which point to this allocation.

22
boa_engine/src/string/slice.rs → 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}; use super::{is_ascii, JsStr, JsStrVariant};

8
boa_engine/src/string/str.rs → boa_types/src/string/str.rs

@ -1,7 +1,6 @@
use std::slice::SliceIndex; use std::slice::SliceIndex;
use crate::string::Iter; use crate::string::Iter;
use boa_interner::JStrRef;
use super::JsStringSlice; use super::JsStringSlice;
@ -75,13 +74,6 @@ impl<'a> JsStr<'a> {
Iter::new(self.into()) 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] #[inline]
#[must_use] #[must_use]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {

9
boa_engine/src/symbol.rs → boa_types/src/symbol.rs

@ -23,9 +23,8 @@
use crate::{ use crate::{
js_string, js_string,
string::common::StaticJsStrings, string::{common::StaticJsStrings, JsString},
tagged::{Tagged, UnwrappedTagged}, tagged::{Tagged, UnwrappedTagged},
JsString,
}; };
use boa_gc::{empty_trace, Finalize, Trace}; use boa_gc::{empty_trace, Finalize, Trace};
@ -131,6 +130,7 @@ struct Inner {
} }
/// This represents a JavaScript symbol primitive. /// This represents a JavaScript symbol primitive.
#[allow(clippy::module_name_repetitions)]
pub struct JsSymbol { pub struct JsSymbol {
repr: Tagged<Inner>, repr: Tagged<Inner>,
} }
@ -151,7 +151,10 @@ unsafe impl Trace for JsSymbol {
macro_rules! well_known_symbols { macro_rules! well_known_symbols {
( $( $(#[$attr:meta])* ($name:ident, $variant:path) ),+$(,)? ) => { ( $( $(#[$attr:meta])* ($name:ident, $variant:path) ),+$(,)? ) => {
$( $(
$(#[$attr])* pub(crate) const fn $name() -> JsSymbol { $(#[$attr])*
#[must_use]
#[inline]
pub const fn $name() -> JsSymbol {
JsSymbol { JsSymbol {
// the cast shouldn't matter since we only have 127 const symbols // the cast shouldn't matter since we only have 127 const symbols
repr: Tagged::from_tag($variant.hash() as usize), repr: Tagged::from_tag($variant.hash() as usize),

0
boa_engine/src/tagged.rs → boa_types/src/tagged.rs

Loading…
Cancel
Save