diff --git a/Cargo.lock b/Cargo.lock index 599fe559eb..e8326e8c2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -129,7 +129,6 @@ dependencies = [ "boa_macros", "boa_parser", "boa_profiler", - "boa_unicode", "chrono", "criterion", "dyn-clone", diff --git a/boa_engine/Cargo.toml b/boa_engine/Cargo.toml index 4230391da5..fe26eccaaa 100644 --- a/boa_engine/Cargo.toml +++ b/boa_engine/Cargo.toml @@ -33,7 +33,6 @@ flowgraph = [] console = [] [dependencies] -boa_unicode.workspace = true boa_interner.workspace = true boa_gc.workspace = true boa_profiler.workspace = true diff --git a/boa_engine/src/bigint.rs b/boa_engine/src/bigint.rs index 1035801282..029350cb9f 100644 --- a/boa_engine/src/bigint.rs +++ b/boa_engine/src/bigint.rs @@ -25,12 +25,14 @@ pub struct JsBigInt { impl JsBigInt { /// Create a new [`JsBigInt`]. #[inline] + #[must_use] pub fn new>(value: T) -> Self { value.into() } /// Create a [`JsBigInt`] with value `0`. #[inline] + #[must_use] pub fn zero() -> Self { Self { inner: Rc::new(RawBigInt::zero()), @@ -39,12 +41,14 @@ impl JsBigInt { /// Check if is zero. #[inline] + #[must_use] pub fn is_zero(&self) -> bool { self.inner.is_zero() } /// Create a [`JsBigInt`] with value `1`. #[inline] + #[must_use] pub fn one() -> Self { Self { inner: Rc::new(RawBigInt::one()), @@ -53,12 +57,14 @@ impl JsBigInt { /// Check if is one. #[inline] + #[must_use] pub fn is_one(&self) -> bool { self.inner.is_one() } /// Convert bigint to string with radix. #[inline] + #[must_use] pub fn to_string_radix(&self, radix: u32) -> String { self.inner.to_str_radix(radix) } @@ -67,12 +73,14 @@ impl JsBigInt { /// /// Returns `f64::INFINITY` if the `BigInt` is too big. #[inline] + #[must_use] pub fn to_f64(&self) -> f64 { self.inner.to_f64().unwrap_or(f64::INFINITY) } /// Converts a string to a `BigInt` with the specified radix. #[inline] + #[must_use] pub fn from_string_radix(buf: &str, radix: u32) -> Option { Some(Self { inner: Rc::new(RawBigInt::parse_bytes(buf.as_bytes(), radix)?), @@ -86,6 +94,7 @@ impl JsBigInt { /// /// [spec]: https://tc39.es/ecma262/#sec-stringtobigint #[inline] + #[must_use] pub fn from_string(mut string: &str) -> Option { string = string.trim(); @@ -115,6 +124,7 @@ impl JsBigInt { /// /// [spec]: https://tc39.es/ecma262/#sec-numeric-types-bigint-equal #[inline] + #[must_use] pub fn same_value_zero(x: &Self, y: &Self) -> bool { // Return BigInt::equal(x, y) Self::equal(x, y) @@ -128,6 +138,7 @@ impl JsBigInt { /// /// [spec]: https://tc39.es/ecma262/#sec-numeric-types-bigint-sameValue #[inline] + #[must_use] pub fn same_value(x: &Self, y: &Self) -> bool { // Return BigInt::equal(x, y) Self::equal(x, y) @@ -143,10 +154,12 @@ impl JsBigInt { /// /// [spec]: https://tc39.es/ecma262/#sec-numeric-types-bigint-sameValueZero #[inline] + #[must_use] pub fn equal(x: &Self, y: &Self) -> bool { x == y } + /// Returns `x` to the power `y`. #[inline] pub fn pow(x: &Self, y: &Self) -> JsResult { let y = y @@ -168,37 +181,27 @@ impl JsBigInt { Ok(Self::new(x.inner.as_ref().clone().pow(y))) } + /// Performs the `>>` operation. #[inline] pub fn shift_right(x: &Self, y: &Self) -> JsResult { - if let Some(n) = y.inner.to_i32() { - let inner = if n > 0 { - x.inner.as_ref().clone().shr(n as usize) - } else { - x.inner.as_ref().clone().shl(n.unsigned_abs()) - }; - - Ok(Self::new(inner)) - } else { - Err(JsNativeError::range() + match y.inner.to_i32() { + Some(n) if n > 0 => Ok(Self::new(x.inner.as_ref().clone().shr(n as usize))), + Some(n) => Ok(Self::new(x.inner.as_ref().clone().shl(n.unsigned_abs()))), + None => Err(JsNativeError::range() .with_message("Maximum BigInt size exceeded") - .into()) + .into()), } } + /// Performs the `<<` operation. #[inline] pub fn shift_left(x: &Self, y: &Self) -> JsResult { - if let Some(n) = y.inner.to_i32() { - let inner = if n > 0 { - x.inner.as_ref().clone().shl(n as usize) - } else { - x.inner.as_ref().clone().shr(n.unsigned_abs()) - }; - - Ok(Self::new(inner)) - } else { - Err(JsNativeError::range() + match y.inner.to_i32() { + Some(n) if n > 0 => Ok(Self::new(x.inner.as_ref().clone().shl(n as usize))), + Some(n) => Ok(Self::new(x.inner.as_ref().clone().shr(n.unsigned_abs()))), + None => Err(JsNativeError::range() .with_message("Maximum BigInt size exceeded") - .into()) + .into()), } } @@ -211,56 +214,77 @@ impl JsBigInt { /// assert_eq!((8).mod_floor(&-3), -1); /// ``` #[inline] + #[must_use] pub fn mod_floor(x: &Self, y: &Self) -> Self { Self::new(x.inner.mod_floor(&y.inner)) } + /// Performs the `+` operation. #[inline] + #[must_use] pub fn add(x: &Self, y: &Self) -> Self { Self::new(x.inner.as_ref().clone().add(y.inner.as_ref())) } + /// Performs the `-` operation. #[inline] + #[must_use] pub fn sub(x: &Self, y: &Self) -> Self { Self::new(x.inner.as_ref().clone().sub(y.inner.as_ref())) } + /// Performs the `*` operation. #[inline] + #[must_use] pub fn mul(x: &Self, y: &Self) -> Self { Self::new(x.inner.as_ref().clone().mul(y.inner.as_ref())) } + /// Performs the `/` operation. #[inline] + #[must_use] pub fn div(x: &Self, y: &Self) -> Self { Self::new(x.inner.as_ref().clone().div(y.inner.as_ref())) } + /// Performs the `%` operation. #[inline] + #[must_use] pub fn rem(x: &Self, y: &Self) -> Self { Self::new(x.inner.as_ref().clone().rem(y.inner.as_ref())) } + /// Performs the `&` operation. #[inline] + #[must_use] pub fn bitand(x: &Self, y: &Self) -> Self { Self::new(x.inner.as_ref().clone().bitand(y.inner.as_ref())) } + /// Performs the `|` operation. #[inline] + #[must_use] pub fn bitor(x: &Self, y: &Self) -> Self { Self::new(x.inner.as_ref().clone().bitor(y.inner.as_ref())) } + /// Performs the `^` operation. #[inline] + #[must_use] pub fn bitxor(x: &Self, y: &Self) -> Self { Self::new(x.inner.as_ref().clone().bitxor(y.inner.as_ref())) } + /// Performs the unary `-` operation. #[inline] + #[must_use] pub fn neg(x: &Self) -> Self { Self::new(x.as_inner().neg()) } + /// Performs the unary `!` operation. #[inline] + #[must_use] pub fn not(x: &Self) -> Self { Self::new(!x.as_inner()) } @@ -386,6 +410,7 @@ impl From for JsBigInt { } } +/// The error indicates that the conversion from [`f64`] to [`JsBigInt`] failed. #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct TryFromF64Error; @@ -407,10 +432,7 @@ impl TryFrom for JsBigInt { if !Number::equal(n.trunc(), n) { return Err(TryFromF64Error); } - match RawBigInt::from_f64(n) { - Some(bigint) => Ok(Self::new(bigint)), - None => Err(TryFromF64Error), - } + RawBigInt::from_f64(n).map_or(Err(TryFromF64Error), |bigint| Ok(Self::new(bigint))) } } diff --git a/boa_engine/src/builtins/array/array_iterator.rs b/boa_engine/src/builtins/array/array_iterator.rs index 67e985b94e..0f13afc267 100644 --- a/boa_engine/src/builtins/array/array_iterator.rs +++ b/boa_engine/src/builtins/array/array_iterator.rs @@ -1,3 +1,10 @@ +//! This module implements the `ArrayIterator` object. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma262/#sec-array-iterator-objects + use crate::{ builtins::{function::make_builtin_fn, iterable::create_iter_result_object, Array, JsValue}, error::JsNativeError, diff --git a/boa_engine/src/builtins/array/mod.rs b/boa_engine/src/builtins/array/mod.rs index 2e4245a8ea..5a859587d2 100644 --- a/boa_engine/src/builtins/array/mod.rs +++ b/boa_engine/src/builtins/array/mod.rs @@ -236,10 +236,8 @@ impl Array { // 3. Let A be ! MakeBasicObject(« [[Prototype]], [[Extensible]] »). // 4. Set A.[[Prototype]] to proto. // 5. Set A.[[DefineOwnProperty]] as specified in 10.4.2.1. - let prototype = match prototype { - Some(prototype) => prototype, - None => context.intrinsics().constructors().array().prototype(), - }; + let prototype = + prototype.unwrap_or_else(|| context.intrinsics().constructors().array().prototype()); let array = JsObject::from_proto_and_data(prototype, ObjectData::array()); // 6. Perform ! OrdinaryDefineOwnProperty(A, "length", PropertyDescriptor { [[Value]]: 𝔽(length), [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }). @@ -380,15 +378,15 @@ impl Array { return Self::array_create(length, None, context); } - // 7. If IsConstructor(C) is false, throw a TypeError exception. if let Some(c) = c.as_constructor() { // 8. Return ? Construct(C, « 𝔽(length) »). - c.construct(&[JsValue::new(length)], Some(c), context) - } else { - Err(JsNativeError::typ() - .with_message("Symbol.species must be a constructor") - .into()) + return c.construct(&[JsValue::new(length)], Some(c), context); } + + // 7. If IsConstructor(C) is false, throw a TypeError exception. + Err(JsNativeError::typ() + .with_message("Symbol.species must be a constructor") + .into()) } /// `Array.from(arrayLike)` @@ -670,7 +668,7 @@ impl Array { let mut n = 0; // 4. Prepend O to items. // 5. For each element E of items, do - for item in [JsValue::new(obj)].iter().chain(args.iter()) { + for item in std::iter::once(&JsValue::new(obj)).chain(args.iter()) { // a. Let spreadable be ? IsConcatSpreadable(E). let spreadable = Self::is_concat_spreadable(item, context)?; // b. If spreadable is true, then @@ -1776,13 +1774,13 @@ impl Array { } // iii. Let shouldFlatten be false - let mut should_flatten = false; - // iv. If depth > 0, then - if depth > 0 { + let should_flatten = if depth > 0 { // 1. Set shouldFlatten to ? IsArray(element). - should_flatten = element.is_array()?; - } + element.is_array()? + } else { + false + }; // v. If shouldFlatten is true if should_flatten { diff --git a/boa_engine/src/builtins/array_buffer/mod.rs b/boa_engine/src/builtins/array_buffer/mod.rs index 561fa324b8..6a694e57a2 100644 --- a/boa_engine/src/builtins/array_buffer/mod.rs +++ b/boa_engine/src/builtins/array_buffer/mod.rs @@ -1,3 +1,12 @@ +//! This module implements the global `ArrayBuffer` object. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! - [MDN documentation][mdn] +//! +//! [spec]: https://tc39.es/ecma262/#sec-arraybuffer-objects +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer + #[cfg(test)] mod tests; @@ -19,15 +28,21 @@ use boa_profiler::Profiler; use num_traits::{Signed, ToPrimitive}; use tap::{Conv, Pipe}; +/// The internal representation of an `ArrayBuffer` object. #[derive(Debug, Clone, Trace, Finalize)] pub struct ArrayBuffer { + /// The `[[ArrayBufferData]]` internal slot. pub array_buffer_data: Option>, + + /// The `[[ArrayBufferByteLength]]` internal slot. pub array_buffer_byte_length: u64, + + /// The `[[ArrayBufferDetachKey]]` internal slot. pub array_buffer_detach_key: JsValue, } impl ArrayBuffer { - pub(crate) fn array_buffer_byte_length(&self) -> u64 { + pub(crate) const fn array_buffer_byte_length(&self) -> u64 { self.array_buffer_byte_length } } @@ -349,7 +364,7 @@ impl ArrayBuffer { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-isdetachedbuffer - pub(crate) fn is_detached_buffer(&self) -> bool { + pub(crate) const fn is_detached_buffer(&self) -> bool { // 1. If arrayBuffer.[[ArrayBufferData]] is null, return true. // 2. Return false. self.array_buffer_data.is_none() @@ -407,7 +422,7 @@ impl ArrayBuffer { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-isunclampedintegerelementtype - fn is_unclamped_integer_element_type(t: TypedArrayKind) -> bool { + const fn is_unclamped_integer_element_type(t: TypedArrayKind) -> bool { // 1. If type is Int8, Uint8, Int16, Uint16, Int32, or Uint32, return true. // 2. Return false. matches!( @@ -427,7 +442,7 @@ impl ArrayBuffer { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-isbigintelementtype - fn is_big_int_element_type(t: TypedArrayKind) -> bool { + const fn is_big_int_element_type(t: TypedArrayKind) -> bool { // 1. If type is BigUint64 or BigInt64, return true. // 2. Return false. matches!(t, TypedArrayKind::BigUint64 | TypedArrayKind::BigInt64) @@ -441,7 +456,7 @@ impl ArrayBuffer { /// [spec]: https://tc39.es/ecma262/#sec-isnotearconfiguration // TODO: Allow unused function until shared array buffers are implemented. #[allow(dead_code)] - fn is_no_tear_configuration(t: TypedArrayKind, order: SharedMemoryOrder) -> bool { + const fn is_no_tear_configuration(t: TypedArrayKind, order: SharedMemoryOrder) -> bool { // 1. If ! IsUnclampedIntegerElementType(type) is true, return true. if Self::is_unclamped_integer_element_type(t) { return true; diff --git a/boa_engine/src/builtins/async_function/mod.rs b/boa_engine/src/builtins/async_function/mod.rs index 12ba9e4b7a..bf508b104d 100644 --- a/boa_engine/src/builtins/async_function/mod.rs +++ b/boa_engine/src/builtins/async_function/mod.rs @@ -19,6 +19,7 @@ use crate::{ }; use boa_profiler::Profiler; +/// The internal representation of an `AsyncFunction` object. #[derive(Debug, Clone, Copy)] pub struct AsyncFunction; diff --git a/boa_engine/src/builtins/async_generator/mod.rs b/boa_engine/src/builtins/async_generator/mod.rs index 768dfd48a6..e377f18cd1 100644 --- a/boa_engine/src/builtins/async_generator/mod.rs +++ b/boa_engine/src/builtins/async_generator/mod.rs @@ -48,7 +48,7 @@ pub(crate) struct AsyncGeneratorRequest { capability: PromiseCapability, } -/// The internal representation on an `AsyncGenerator` object. +/// The internal representation of an `AsyncGenerator` object. #[derive(Debug, Clone, Finalize, Trace)] pub struct AsyncGenerator { /// The `[[AsyncGeneratorState]]` internal slot. diff --git a/boa_engine/src/builtins/async_generator_function/mod.rs b/boa_engine/src/builtins/async_generator_function/mod.rs index 4bd3a186f3..59a94b35a2 100644 --- a/boa_engine/src/builtins/async_generator_function/mod.rs +++ b/boa_engine/src/builtins/async_generator_function/mod.rs @@ -18,7 +18,7 @@ use crate::{ }; use boa_profiler::Profiler; -/// The internal representation on a `AsyncGeneratorFunction` object. +/// The internal representation of an `AsyncGeneratorFunction` object. #[derive(Debug, Clone, Copy)] pub struct AsyncGeneratorFunction; diff --git a/boa_engine/src/builtins/console/mod.rs b/boa_engine/src/builtins/console/mod.rs index 997219a92c..4e1a1fdc1b 100644 --- a/boa_engine/src/builtins/console/mod.rs +++ b/boa_engine/src/builtins/console/mod.rs @@ -29,7 +29,7 @@ use tap::{Conv, Pipe}; /// This represents the different types of log messages. #[derive(Debug)] -pub enum LogMessage { +enum LogMessage { Log(String), Info(String), Warn(String), @@ -37,7 +37,7 @@ pub enum LogMessage { } /// Helper function for logging messages. -pub(crate) fn logger(msg: LogMessage, console_state: &Console) { +fn logger(msg: LogMessage, console_state: &Console) { let indent = 2 * console_state.groups.len(); match msg { diff --git a/boa_engine/src/builtins/dataview/mod.rs b/boa_engine/src/builtins/dataview/mod.rs index be04663e83..76348367d0 100644 --- a/boa_engine/src/builtins/dataview/mod.rs +++ b/boa_engine/src/builtins/dataview/mod.rs @@ -1,3 +1,12 @@ +//! This module implements the global `DataView` object. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! - [MDN documentation][mdn] +//! +//! [spec]: https://tc39.es/ecma262/#sec-dataview-objects +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView + use crate::{ builtins::{array_buffer::SharedMemoryOrder, typed_array::TypedArrayKind, BuiltIn, JsArgs}, context::intrinsics::StandardConstructors, @@ -14,6 +23,7 @@ use crate::{ use boa_gc::{Finalize, Trace}; use tap::{Conv, Pipe}; +/// The internal representation of a `DataView` object. #[derive(Debug, Clone, Trace, Finalize)] pub struct DataView { pub(crate) viewed_array_buffer: JsObject, diff --git a/boa_engine/src/builtins/date/mod.rs b/boa_engine/src/builtins/date/mod.rs index 2e0776ae19..06ffdedf43 100644 --- a/boa_engine/src/builtins/date/mod.rs +++ b/boa_engine/src/builtins/date/mod.rs @@ -1,5 +1,15 @@ +//! This module implements the global `Date` object. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! - [MDN documentation][mdn] +//! +//! [spec]: https://tc39.es/ecma262/#sec-date-objects +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date + mod utils; use utils::{make_date, make_day, make_time, replace_params, time_clip, DateParameters}; + #[cfg(test)] mod tests; @@ -58,13 +68,14 @@ pub(super) fn this_time_value(value: &JsValue) -> JsResult .0) } +/// The internal representation of a `Date` object. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Date(Option); impl Date { /// Creates a new `Date`. #[inline] - pub(crate) fn new(dt: Option) -> Self { + pub(crate) const fn new(dt: Option) -> Self { Self(dt) } diff --git a/boa_engine/src/builtins/date/tests.rs b/boa_engine/src/builtins/date/tests.rs index b0050c7d69..54191df42e 100644 --- a/boa_engine/src/builtins/date/tests.rs +++ b/boa_engine/src/builtins/date/tests.rs @@ -194,7 +194,7 @@ fn date_ctor_parse_call() { let date_time = forward_val(&mut context, "Date.parse('2020-06-08T09:16:15.779-07:30')"); - assert_eq!(JsValue::new(1591634775779i64), date_time.unwrap()); + assert_eq!(JsValue::new(1_591_634_775_779_i64), date_time.unwrap()); } #[test] @@ -203,7 +203,7 @@ fn date_ctor_utc_call() { let date_time = forward_val(&mut context, "Date.UTC(2020, 6, 8, 9, 16, 15, 779)"); - assert_eq!(JsValue::new(1594199775779i64), date_time.unwrap()); + assert_eq!(JsValue::new(1_594_199_775_779_i64), date_time.unwrap()); } #[test] @@ -1379,7 +1379,7 @@ fn date_proto_value_of() { "new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779)).valueOf()", ) .unwrap(); - assert_eq!(JsValue::new(1594199775779i64), actual); + assert_eq!(JsValue::new(1_594_199_775_779_i64), actual); } #[test] @@ -1391,7 +1391,7 @@ fn date_neg() { "-new Date(Date.UTC(2020, 6, 8, 9, 16, 15, 779))", ) .unwrap(); - assert_eq!(JsValue::new(-1594199775779i64), actual); + assert_eq!(JsValue::new(-1_594_199_775_779_i64), actual); } #[test] diff --git a/boa_engine/src/builtins/error/mod.rs b/boa_engine/src/builtins/error/mod.rs index 3bfca5bdaf..3c7e3d52e0 100644 --- a/boa_engine/src/builtins/error/mod.rs +++ b/boa_engine/src/builtins/error/mod.rs @@ -60,13 +60,68 @@ use super::JsArgs; /// [spec]: https://tc39.es/ecma262/#sec-error-objects #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum ErrorKind { + /// The `AggregateError` object type. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-aggregate-error-objects Aggregate, + + /// The `Error` object type. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-error-objects Error, + + /// The `EvalError` type. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-evalerror Eval, + + /// The `TypeError` type. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-typeerror Type, + + /// The `RangeError` type. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-rangeerror Range, + + /// The `ReferenceError` type. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-referenceerror Reference, + + /// The `SyntaxError` type. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-syntaxerror Syntax, + + /// The `URIError` type. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-urierror Uri, } diff --git a/boa_engine/src/builtins/function/mod.rs b/boa_engine/src/builtins/function/mod.rs index ebd927fd79..5d7cc9bfb4 100644 --- a/boa_engine/src/builtins/function/mod.rs +++ b/boa_engine/src/builtins/function/mod.rs @@ -73,7 +73,10 @@ mod sealed { pub trait Sealed {} impl Sealed for T {} } + +/// This trait is implemented by any type that implements [`core::marker::Copy`]. pub trait DynCopy: sealed::Sealed {} + impl DynCopy for T {} /// Trait representing a native built-in closure. @@ -94,26 +97,41 @@ impl ClosureFunctionSignature for T where // Allows cloning Box dyn_clone::clone_trait_object!(ClosureFunctionSignature); +/// Represents the `[[ThisMode]]` internal slot of function objects. +/// +/// More information: +/// - [ECMAScript specification][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-ecmascript-function-objects #[derive(Debug, Trace, Finalize, PartialEq, Eq, Clone)] pub enum ThisMode { + /// The `this` value refers to the `this` value of a lexically enclosing function. Lexical, + + /// The `this` value is used exactly as provided by an invocation of the function. Strict, + + /// The `this` value of `undefined` or `null` is interpreted as a reference to the global object, + /// and any other `this` value is first passed to `ToObject`. Global, } impl ThisMode { /// Returns `true` if the this mode is `Lexical`. - pub fn is_lexical(&self) -> bool { + #[must_use] + pub const fn is_lexical(&self) -> bool { matches!(self, Self::Lexical) } /// Returns `true` if the this mode is `Strict`. - pub fn is_strict(&self) -> bool { + #[must_use] + pub const fn is_strict(&self) -> bool { matches!(self, Self::Strict) } /// Returns `true` if the this mode is `Global`. - pub fn is_global(&self) -> bool { + #[must_use] + pub const fn is_global(&self) -> bool { matches!(self, Self::Global) } } @@ -126,18 +144,23 @@ impl ThisMode { /// [spec]: https://tc39.es/ecma262/#sec-ecmascript-function-objects #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ConstructorKind { + /// The class constructor is not derived. Base, + + /// The class constructor is a derived class constructor. Derived, } impl ConstructorKind { /// Returns `true` if the constructor kind is `Base`. - pub fn is_base(&self) -> bool { + #[must_use] + pub const fn is_base(&self) -> bool { matches!(self, Self::Base) } /// Returns `true` if the constructor kind is `Derived`. - pub fn is_derived(&self) -> bool { + #[must_use] + pub const fn is_derived(&self) -> bool { matches!(self, Self::Derived) } } @@ -150,7 +173,10 @@ impl ConstructorKind { /// [spec]: https://tc39.es/ecma262/#sec-classfielddefinition-record-specification-type #[derive(Clone, Debug, Finalize)] pub enum ClassFieldDefinition { + /// A class field definition with a `string` or `symbol` as a name. Public(PropertyKey, JsFunction), + + /// A class field definition with a private name. Private(Sym, JsFunction), } @@ -216,17 +242,33 @@ impl Captures { /// #[derive(Finalize)] pub enum Function { + /// A rust function. Native { + /// The rust function. function: NativeFunctionSignature, + + /// The kind of the function constructor if it is a constructor. constructor: Option, }, + + /// A rust function that may contain captured values. Closure { + /// The rust function. function: Box, + + /// The kind of the function constructor if it is a constructor. constructor: Option, + + /// The captured values. captures: Captures, }, + + /// A bytecode function. Ordinary { + /// The code block containing the compiled function. code: Gc, + + /// The `[[Environment]]` internal slot. environments: DeclarativeEnvironmentStack, /// The `[[ConstructorKind]]` internal slot. @@ -241,22 +283,42 @@ pub enum Function { /// The `[[PrivateMethods]]` internal slot. private_methods: Vec<(Sym, PrivateElement)>, }, + + /// A bytecode async function. Async { + /// The code block containing the compiled function. code: Gc, + + /// The `[[Environment]]` internal slot. environments: DeclarativeEnvironmentStack, + /// The `[[HomeObject]]` internal slot. home_object: Option, + + /// The promise capability record of the async function. promise_capability: PromiseCapability, }, + + /// A bytecode generator function. Generator { + /// The code block containing the compiled function. code: Gc, + + /// The `[[Environment]]` internal slot. environments: DeclarativeEnvironmentStack, + /// The `[[HomeObject]]` internal slot. home_object: Option, }, + + /// A bytecode async generator function. AsyncGenerator { + /// The code block containing the compiled function. code: Gc, + + /// The `[[Environment]]` internal slot. environments: DeclarativeEnvironmentStack, + /// The `[[HomeObject]]` internal slot. home_object: Option, }, @@ -311,7 +373,7 @@ impl Function { } /// Returns true if the function object is a derived constructor. - pub(crate) fn is_derived_constructor(&self) -> bool { + pub(crate) const fn is_derived_constructor(&self) -> bool { if let Self::Ordinary { constructor_kind, .. } = self @@ -323,7 +385,7 @@ impl Function { } /// Returns a reference to the function `[[HomeObject]]` slot if present. - pub(crate) fn get_home_object(&self) -> Option<&JsObject> { + pub(crate) const fn get_home_object(&self) -> Option<&JsObject> { match self { Self::Ordinary { home_object, .. } | Self::Async { home_object, .. } @@ -390,7 +452,7 @@ impl Function { } /// Returns the promise capability if the function is an async function. - pub(crate) fn get_promise_capability(&self) -> Option<&PromiseCapability> { + pub(crate) const fn get_promise_capability(&self) -> Option<&PromiseCapability> { if let Self::Async { promise_capability, .. } = self @@ -461,11 +523,12 @@ pub(crate) fn make_builtin_fn( ); } +/// The internal representation of a `Function` object. #[derive(Debug, Clone, Copy)] pub struct BuiltInFunctionObject; impl BuiltInFunctionObject { - pub const LENGTH: usize = 1; + const LENGTH: usize = 1; /// `Function ( p1, p2, … , pn, body )` /// @@ -946,13 +1009,12 @@ fn set_function_name( let mut name = match name { PropertyKey::Symbol(sym) => { // a. Let description be name's [[Description]] value. - if let Some(desc) = sym.description() { - // c. Else, set name to the string-concatenation of "[", description, and "]". - js_string!(utf16!("["), &desc, utf16!("]")) - } else { - // b. If description is undefined, set name to the empty String. - js_string!() - } + // b. If description is undefined, set name to the empty String. + // c. Else, set name to the string-concatenation of "[", description, and "]". + sym.description().map_or_else( + || js_string!(), + |desc| js_string!(utf16!("["), &desc, utf16!("]")), + ) } PropertyKey::String(string) => string.clone(), PropertyKey::Index(index) => js_string!(format!("{}", index)), @@ -1038,12 +1100,12 @@ impl BoundFunction { } /// Get a reference to the bound function's this. - pub fn this(&self) -> &JsValue { + pub const fn this(&self) -> &JsValue { &self.this } /// Get a reference to the bound function's target function. - pub fn target_function(&self) -> &JsObject { + pub const fn target_function(&self) -> &JsObject { &self.target_function } diff --git a/boa_engine/src/builtins/generator/mod.rs b/boa_engine/src/builtins/generator/mod.rs index 7599dd9074..317781e5a5 100644 --- a/boa_engine/src/builtins/generator/mod.rs +++ b/boa_engine/src/builtins/generator/mod.rs @@ -44,7 +44,7 @@ pub(crate) struct GeneratorContext { pub(crate) stack: Vec, } -/// The internal representation on a `Generator` object. +/// The internal representation of a `Generator` object. #[derive(Debug, Clone, Finalize, Trace)] pub struct Generator { /// The `[[GeneratorState]]` internal slot. @@ -148,14 +148,14 @@ impl Generator { context: &mut Context, ) -> JsResult { // 1. Return ? GeneratorResume(this value, value, empty). - match this.as_object() { - Some(obj) if obj.is_generator() => { - Self::generator_resume(obj, args.get_or_undefined(0), context) - } - _ => Err(JsNativeError::typ() - .with_message("Generator.prototype.next called on non generator") - .into()), - } + this.as_object().map_or_else( + || { + Err(JsNativeError::typ() + .with_message("Generator.prototype.next called on non generator") + .into()) + }, + |obj| Self::generator_resume(obj, args.get_or_undefined(0), context), + ) } /// `Generator.prototype.return ( value )` diff --git a/boa_engine/src/builtins/generator_function/mod.rs b/boa_engine/src/builtins/generator_function/mod.rs index 637b81ef44..49845956d2 100644 --- a/boa_engine/src/builtins/generator_function/mod.rs +++ b/boa_engine/src/builtins/generator_function/mod.rs @@ -23,7 +23,7 @@ use crate::{ }; use boa_profiler::Profiler; -/// The internal representation on a `Generator` object. +/// The internal representation of a `Generator` object. #[derive(Debug, Clone, Copy)] pub struct GeneratorFunction; diff --git a/boa_engine/src/builtins/intl/mod.rs b/boa_engine/src/builtins/intl/mod.rs index 4f6198cf97..c0290f7a3b 100644 --- a/boa_engine/src/builtins/intl/mod.rs +++ b/boa_engine/src/builtins/intl/mod.rs @@ -104,6 +104,7 @@ struct MatcherRecord { /// /// [spec]: https://tc39.es/ecma402/#sec-defaultlocale fn default_locale(canonicalizer: &LocaleCanonicalizer) -> Locale { + #[allow(clippy::string_lit_as_bytes)] sys_locale::get_locale() .and_then(|loc| loc.parse::().ok()) .tap_some_mut(|loc| canonicalize_unicode_locale_id(loc, canonicalizer)) @@ -281,10 +282,7 @@ fn unicode_extension_components(extension: &str) -> UniExtRecord { let e = extension[k..].find('-'); // b. If e = -1, let len be size - k; else let len be e - k. - let len = match e { - Some(pos) => pos, - None => size - k, - }; + let len = e.unwrap_or(size - k); // c. Let subtag be the String value equal to the substring of extension consisting of the // code units at indices k (inclusive) through k + len (exclusive). @@ -587,24 +585,21 @@ fn resolve_locale( for &key in relevant_extension_keys { // a. Let foundLocaleData be localeData.[[]]. // TODO b. Assert: Type(foundLocaleData) is Record. - let found_locale_data = match locale_data.get(&found_locale) { - Some(locale_value) => locale_value.clone(), - None => FxHashMap::default(), - }; + let found_locale_data = locale_data + .get(&found_locale) + .map_or_else(FxHashMap::default, Clone::clone); // c. Let keyLocaleData be foundLocaleData.[[]]. // TODO d. Assert: Type(keyLocaleData) is List. - let key_locale_data = match found_locale_data.get(key) { - Some(locale_vec) => locale_vec.clone(), - None => Vec::new(), - }; + let key_locale_data = found_locale_data + .get(key) + .map_or_else(Vec::new, Clone::clone); // e. Let value be keyLocaleData[0]. // TODO f. Assert: Type(value) is either String or Null. - let mut value = match key_locale_data.get(0) { - Some(first_elt) => first_elt.clone().into(), - None => JsValue::null(), - }; + let mut value = key_locale_data + .get(0) + .map_or_else(JsValue::null, |first_elt| first_elt.clone().into()); // g. Let supportedExtensionAddition be "". let mut supported_extension_addition = String::new(); diff --git a/boa_engine/src/builtins/iterable/mod.rs b/boa_engine/src/builtins/iterable/mod.rs index 789abd4405..df48e8209d 100644 --- a/boa_engine/src/builtins/iterable/mod.rs +++ b/boa_engine/src/builtins/iterable/mod.rs @@ -1,3 +1,5 @@ +//! This module implements the global iterator prototype objects. + mod async_from_sync_iterator; use crate::{ @@ -17,25 +19,34 @@ use boa_profiler::Profiler; pub(crate) use async_from_sync_iterator::AsyncFromSyncIterator; +/// The built-in iterator prototypes. #[derive(Debug, Default)] pub struct IteratorPrototypes { - /// %IteratorPrototype% + /// The `IteratorPrototype` object. iterator_prototype: JsObject, - /// %AsyncIteratorPrototype% + + /// The `AsyncIteratorPrototype` object. async_iterator_prototype: JsObject, - /// %AsyncFromSyncIteratorPrototype% + + /// The `AsyncFromSyncIteratorPrototype` prototype object. async_from_sync_iterator_prototype: JsObject, - /// %MapIteratorPrototype% + + /// The `ArrayIteratorPrototype` prototype object. array_iterator: JsObject, - /// %SetIteratorPrototype% + + /// The `SetIteratorPrototype` prototype object. set_iterator: JsObject, - /// %StringIteratorPrototype% + + /// The `StringIteratorPrototype` prototype object. string_iterator: JsObject, - /// %RegExpStringIteratorPrototype% + + /// The `RegExpStringIteratorPrototype` prototype object. regexp_string_iterator: JsObject, - /// %MapIteratorPrototype% + + /// The `MapIteratorPrototype` prototype object. map_iterator: JsObject, - /// %ForInIteratorPrototype% + + /// The `ForInIteratorPrototype` prototype object. for_in_iterator: JsObject, } @@ -62,46 +73,55 @@ impl IteratorPrototypes { } } + /// Returns the `ArrayIteratorPrototype` object. #[inline] pub fn array_iterator(&self) -> JsObject { self.array_iterator.clone() } + /// Returns the `IteratorPrototype` object. #[inline] pub fn iterator_prototype(&self) -> JsObject { self.iterator_prototype.clone() } + /// Returns the `AsyncIteratorPrototype` object. #[inline] pub fn async_iterator_prototype(&self) -> JsObject { self.async_iterator_prototype.clone() } + /// Returns the `AsyncFromSyncIteratorPrototype` object. #[inline] pub fn async_from_sync_iterator_prototype(&self) -> JsObject { self.async_from_sync_iterator_prototype.clone() } + /// Returns the `SetIteratorPrototype` object. #[inline] pub fn set_iterator(&self) -> JsObject { self.set_iterator.clone() } + /// Returns the `StringIteratorPrototype` object. #[inline] pub fn string_iterator(&self) -> JsObject { self.string_iterator.clone() } + /// Returns the `RegExpStringIteratorPrototype` object. #[inline] pub fn regexp_string_iterator(&self) -> JsObject { self.regexp_string_iterator.clone() } + /// Returns the `MapIteratorPrototype` object. #[inline] pub fn map_iterator(&self) -> JsObject { self.map_iterator.clone() } + /// Returns the `ForInIteratorPrototype` object. #[inline] pub fn for_in_iterator(&self) -> JsObject { self.for_in_iterator.clone() @@ -132,7 +152,10 @@ pub fn create_iter_result_object(value: JsValue, done: bool, context: &mut Conte /// Iterator hint for `GetIterator`. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum IteratorHint { + /// Hints that the iterator should be sync. Sync, + + /// Hints that the iterator should be async. Async, } @@ -311,19 +334,19 @@ impl IteratorRecord { /// Get the `[[Iterator]]` field of the `IteratorRecord`. #[inline] - pub(crate) fn iterator(&self) -> &JsObject { + pub(crate) const fn iterator(&self) -> &JsObject { &self.iterator } /// Get the `[[NextMethod]]` field of the `IteratorRecord`. #[inline] - pub(crate) fn next_method(&self) -> &JsValue { + pub(crate) const fn next_method(&self) -> &JsValue { &self.next_method } /// Get the `[[Done]]` field of the `IteratorRecord`. #[inline] - pub(crate) fn done(&self) -> bool { + pub(crate) const fn done(&self) -> bool { self.done } diff --git a/boa_engine/src/builtins/json/mod.rs b/boa_engine/src/builtins/json/mod.rs index f43a0d6b00..3d0725f01f 100644 --- a/boa_engine/src/builtins/json/mod.rs +++ b/boa_engine/src/builtins/json/mod.rs @@ -83,7 +83,7 @@ where fn size_hint(&self) -> (usize, Option) { type SizeHint = (usize, Option); - fn add(a: SizeHint, b: SizeHint) -> SizeHint { + const fn add(a: SizeHint, b: SizeHint) -> SizeHint { let min = a.0.saturating_add(b.0); let max = match (a.1, b.1) { (Some(x), Some(y)) => x.checked_add(y), diff --git a/boa_engine/src/builtins/map/map_iterator.rs b/boa_engine/src/builtins/map/map_iterator.rs index 12d48fdc1f..ffb4edaac0 100644 --- a/boa_engine/src/builtins/map/map_iterator.rs +++ b/boa_engine/src/builtins/map/map_iterator.rs @@ -1,3 +1,10 @@ +//! This module implements the `MapIterator` object. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma262/#sec-map-iterator-objects + use super::ordered_map::MapLock; use crate::{ builtins::{function::make_builtin_fn, iterable::create_iter_result_object, Array, JsValue}, @@ -15,7 +22,7 @@ use boa_profiler::Profiler; /// More information: /// - [ECMAScript reference][spec] /// -/// [spec]: https://tc39.es/ecma262/#sec-array-iterator-objects +/// [spec]: https://tc39.es/ecma262/#sec-map-iterator-objects #[derive(Debug, Clone, Finalize, Trace)] pub struct MapIterator { iterated_map: Option, @@ -86,7 +93,7 @@ impl MapIterator { if let Some(obj) = map_iterator.iterated_map.take() { let e = { let map = obj.borrow(); - let entries = map.as_map_ref().expect("iterator should only iterate maps"); + let entries = map.as_map().expect("iterator should only iterate maps"); let len = entries.full_len(); loop { let element = entries diff --git a/boa_engine/src/builtins/map/mod.rs b/boa_engine/src/builtins/map/mod.rs index 0713e75a2b..928c8ca918 100644 --- a/boa_engine/src/builtins/map/mod.rs +++ b/boa_engine/src/builtins/map/mod.rs @@ -34,7 +34,7 @@ pub mod ordered_map; mod tests; #[derive(Debug, Clone)] -pub(crate) struct Map(OrderedMap); +pub(crate) struct Map; impl BuiltIn for Map { const NAME: &'static str = "Map"; @@ -334,7 +334,7 @@ impl Map { if let JsValue::Object(ref object) = this { // 2. Perform ? RequireInternalSlot(M, [[MapData]]). // 3. Let entries be the List that is M.[[MapData]]. - if let Some(map) = object.borrow().as_map_ref() { + if let Some(map) = object.borrow().as_map() { // 4. For each Record { [[Key]], [[Value]] } p of entries, do // a. If p.[[Key]] is not empty and SameValueZero(p.[[Key]], key) is true, return p.[[Value]]. // 5. Return undefined. @@ -406,7 +406,7 @@ impl Map { if let JsValue::Object(ref object) = this { // 2. Perform ? RequireInternalSlot(M, [[MapData]]). // 3. Let entries be the List that is M.[[MapData]]. - if let Some(map) = object.borrow().as_map_ref() { + if let Some(map) = object.borrow().as_map() { // 4. For each Record { [[Key]], [[Value]] } p of entries, do // a. If p.[[Key]] is not empty and SameValueZero(p.[[Key]], key) is true, return true. // 5. Return false. @@ -470,7 +470,7 @@ impl Map { loop { let arguments = { let map = map.borrow(); - let map = map.as_map_ref().expect("checked that `this` was a map"); + let map = map.as_map().expect("checked that `this` was a map"); if index < map.full_len() { map.get_index(index) .map(|(k, v)| [v.clone(), k.clone(), this.clone()]) diff --git a/boa_engine/src/builtins/map/ordered_map.rs b/boa_engine/src/builtins/map/ordered_map.rs index 7b5e60d6d9..0d79ceaf2e 100644 --- a/boa_engine/src/builtins/map/ordered_map.rs +++ b/boa_engine/src/builtins/map/ordered_map.rs @@ -1,3 +1,5 @@ +//! Implements a map type that preserves insertion order. + use crate::{object::JsObject, JsValue}; use boa_gc::{custom_trace, Finalize, Trace}; use indexmap::{Equivalent, IndexMap}; @@ -18,8 +20,8 @@ enum MapKey { impl Hash for MapKey { fn hash(&self, state: &mut H) { match self { - MapKey::Key(v) => v.hash(state), - MapKey::Empty(e) => e.hash(state), + Self::Key(v) => v.hash(state), + Self::Empty(e) => e.hash(state), } } } @@ -66,6 +68,8 @@ impl Default for OrderedMap { } impl OrderedMap { + /// Creates a new empty `OrderedMap`. + #[must_use] pub fn new() -> Self { Self { map: IndexMap::new(), @@ -74,6 +78,8 @@ impl OrderedMap { } } + /// Creates a new empty `OrderedMap` with the specified capacity. + #[must_use] pub fn with_capacity(capacity: usize) -> Self { Self { map: IndexMap::with_capacity(capacity), @@ -85,6 +91,7 @@ impl OrderedMap { /// Return the number of key-value pairs in the map, including empty values. /// /// Computes in **O(1)** time. + #[must_use] pub fn full_len(&self) -> usize { self.map.len() } @@ -92,6 +99,7 @@ impl OrderedMap { /// Gets the number of key-value pairs in the map, not including empty values. /// /// Computes in **O(1)** time. + #[must_use] pub fn len(&self) -> usize { self.map.len() - self.empty_count } @@ -99,6 +107,7 @@ impl OrderedMap { /// Returns true if the map contains no elements. /// /// Computes in **O(1)** time. + #[must_use] pub fn is_empty(&self) -> bool { self.len() == 0 } @@ -160,6 +169,7 @@ impl OrderedMap { /// Valid indices are `0 <= index < self.full_len()`. /// /// Computes in O(1) time. + #[must_use] pub fn get_index(&self, index: usize) -> Option<(&JsValue, &V)> { if let (MapKey::Key(key), Some(value)) = self.map.get_index(index)? { Some((key, value)) diff --git a/boa_engine/src/builtins/math/tests.rs b/boa_engine/src/builtins/math/tests.rs index d22e971d0e..c86219ed6f 100644 --- a/boa_engine/src/builtins/math/tests.rs +++ b/boa_engine/src/builtins/math/tests.rs @@ -394,7 +394,7 @@ fn hypot() { assert_eq!(b.to_number(&mut context).unwrap(), 5f64); assert_eq!(c.to_number(&mut context).unwrap(), 13f64); assert_eq!(d.to_number(&mut context).unwrap(), 7.071_067_811_865_475_5); - assert_eq!(e.to_number(&mut context).unwrap(), 8.774964387392123); + assert_eq!(e.to_number(&mut context).unwrap(), 8.774_964_387_392_123); assert!(f.to_number(&mut context).unwrap().is_infinite()); assert_eq!(g.to_number(&mut context).unwrap(), 12f64); } diff --git a/boa_engine/src/builtins/mod.rs b/boa_engine/src/builtins/mod.rs index 3e7aa658d8..e28b8f2d1b 100644 --- a/boa_engine/src/builtins/mod.rs +++ b/boa_engine/src/builtins/mod.rs @@ -205,6 +205,7 @@ pub fn init(context: &mut Context) { init_builtin::(context); } +/// A utility trait to make working with function arguments easier. pub trait JsArgs { /// Utility function to `get` a parameter from a `[JsValue]` or default to `JsValue::Undefined` /// if `get` returns `None`. diff --git a/boa_engine/src/builtins/number/conversions.rs b/boa_engine/src/builtins/number/conversions.rs index e9960cbb95..f4280c03c0 100644 --- a/boa_engine/src/builtins/number/conversions.rs +++ b/boa_engine/src/builtins/number/conversions.rs @@ -4,10 +4,10 @@ #[inline] #[allow(clippy::float_cmp)] pub(crate) fn f64_to_int32(number: f64) -> i32 { - const SIGN_MASK: u64 = 0x8000000000000000; - const EXPONENT_MASK: u64 = 0x7FF0000000000000; - const SIGNIFICAND_MASK: u64 = 0x000FFFFFFFFFFFFF; - const HIDDEN_BIT: u64 = 0x0010000000000000; + const SIGN_MASK: u64 = 0x8000_0000_0000_0000; + const EXPONENT_MASK: u64 = 0x7FF0_0000_0000_0000; + const SIGNIFICAND_MASK: u64 = 0x000F_FFFF_FFFF_FFFF; + const HIDDEN_BIT: u64 = 0x0010_0000_0000_0000; const PHYSICAL_SIGNIFICAND_SIZE: i32 = 52; // Excludes the hidden bit. const SIGNIFICAND_SIZE: i32 = 53; @@ -71,7 +71,7 @@ pub(crate) fn f64_to_int32(number: f64) -> i32 { return 0; } - (significand(number) << exponent) & 0xFFFFFFFF + (significand(number) << exponent) & 0xFFFF_FFFF }; (sign(number) * (bits as i64)) as i32 diff --git a/boa_engine/src/builtins/number/mod.rs b/boa_engine/src/builtins/number/mod.rs index 375a36e3e7..f12f670698 100644 --- a/boa_engine/src/builtins/number/mod.rs +++ b/boa_engine/src/builtins/number/mod.rs @@ -992,15 +992,14 @@ impl Number { args: &[JsValue], _ctx: &mut Context, ) -> JsResult { - Ok(JsValue::new(if let Some(val) = args.get(0) { - match val { - JsValue::Integer(_) => true, - JsValue::Rational(number) => number.is_finite(), - _ => false, - } - } else { - false - })) + // 1. If number is not a Number, return false. + // 2. If number is not finite, return false. + // 3. Otherwise, return true. + Ok(JsValue::new(args.get(0).map_or(false, |val| match val { + JsValue::Integer(_) => true, + JsValue::Rational(number) => number.is_finite(), + _ => false, + }))) } /// `Number.isInteger( number )` diff --git a/boa_engine/src/builtins/object/for_in_iterator.rs b/boa_engine/src/builtins/object/for_in_iterator.rs index 18ce1984e5..be8bcfaf14 100644 --- a/boa_engine/src/builtins/object/for_in_iterator.rs +++ b/boa_engine/src/builtins/object/for_in_iterator.rs @@ -1,3 +1,10 @@ +//! This module implements the `ForInIterator` object. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma262/#sec-for-in-iterator-objects + use crate::{ builtins::{function::make_builtin_fn, iterable::create_iter_result_object}, error::JsNativeError, diff --git a/boa_engine/src/builtins/object/mod.rs b/boa_engine/src/builtins/object/mod.rs index 7f82c980a0..9502d706e2 100644 --- a/boa_engine/src/builtins/object/mod.rs +++ b/boa_engine/src/builtins/object/mod.rs @@ -503,60 +503,61 @@ impl Object { desc: Option, context: &mut Context, ) -> JsValue { - match desc { - // 1. If Desc is undefined, return undefined. - None => JsValue::undefined(), - Some(desc) => { - // 2. Let obj be ! OrdinaryObjectCreate(%Object.prototype%). - // 3. Assert: obj is an extensible ordinary object with no own properties. - let obj = context.construct_object(); - - // 4. If Desc has a [[Value]] field, then - if let Some(value) = desc.value() { - // a. Perform ! CreateDataPropertyOrThrow(obj, "value", Desc.[[Value]]). - obj.create_data_property_or_throw("value", value, context) - .expect("CreateDataPropertyOrThrow cannot fail here"); - } + // 1. If Desc is undefined, return undefined. + let desc = if let Some(desc) = desc { + desc + } else { + return JsValue::undefined(); + }; - // 5. If Desc has a [[Writable]] field, then - if let Some(writable) = desc.writable() { - // a. Perform ! CreateDataPropertyOrThrow(obj, "writable", Desc.[[Writable]]). - obj.create_data_property_or_throw("writable", writable, context) - .expect("CreateDataPropertyOrThrow cannot fail here"); - } + // 2. Let obj be ! OrdinaryObjectCreate(%Object.prototype%). + // 3. Assert: obj is an extensible ordinary object with no own properties. + let obj = context.construct_object(); - // 6. If Desc has a [[Get]] field, then - if let Some(get) = desc.get() { - // a. Perform ! CreateDataPropertyOrThrow(obj, "get", Desc.[[Get]]). - obj.create_data_property_or_throw("get", get, context) - .expect("CreateDataPropertyOrThrow cannot fail here"); - } + // 4. If Desc has a [[Value]] field, then + if let Some(value) = desc.value() { + // a. Perform ! CreateDataPropertyOrThrow(obj, "value", Desc.[[Value]]). + obj.create_data_property_or_throw("value", value, context) + .expect("CreateDataPropertyOrThrow cannot fail here"); + } - // 7. If Desc has a [[Set]] field, then - if let Some(set) = desc.set() { - // a. Perform ! CreateDataPropertyOrThrow(obj, "set", Desc.[[Set]]). - obj.create_data_property_or_throw("set", set, context) - .expect("CreateDataPropertyOrThrow cannot fail here"); - } + // 5. If Desc has a [[Writable]] field, then + if let Some(writable) = desc.writable() { + // a. Perform ! CreateDataPropertyOrThrow(obj, "writable", Desc.[[Writable]]). + obj.create_data_property_or_throw("writable", writable, context) + .expect("CreateDataPropertyOrThrow cannot fail here"); + } - // 8. If Desc has an [[Enumerable]] field, then - if let Some(enumerable) = desc.enumerable() { - // a. Perform ! CreateDataPropertyOrThrow(obj, "enumerable", Desc.[[Enumerable]]). - obj.create_data_property_or_throw("enumerable", enumerable, context) - .expect("CreateDataPropertyOrThrow cannot fail here"); - } + // 6. If Desc has a [[Get]] field, then + if let Some(get) = desc.get() { + // a. Perform ! CreateDataPropertyOrThrow(obj, "get", Desc.[[Get]]). + obj.create_data_property_or_throw("get", get, context) + .expect("CreateDataPropertyOrThrow cannot fail here"); + } - // 9. If Desc has a [[Configurable]] field, then - if let Some(configurable) = desc.configurable() { - // a. Perform ! CreateDataPropertyOrThrow(obj, "configurable", Desc.[[Configurable]]). - obj.create_data_property_or_throw("configurable", configurable, context) - .expect("CreateDataPropertyOrThrow cannot fail here"); - } + // 7. If Desc has a [[Set]] field, then + if let Some(set) = desc.set() { + // a. Perform ! CreateDataPropertyOrThrow(obj, "set", Desc.[[Set]]). + obj.create_data_property_or_throw("set", set, context) + .expect("CreateDataPropertyOrThrow cannot fail here"); + } - // 10. Return obj. - obj.into() - } + // 8. If Desc has an [[Enumerable]] field, then + if let Some(enumerable) = desc.enumerable() { + // a. Perform ! CreateDataPropertyOrThrow(obj, "enumerable", Desc.[[Enumerable]]). + obj.create_data_property_or_throw("enumerable", enumerable, context) + .expect("CreateDataPropertyOrThrow cannot fail here"); + } + + // 9. If Desc has a [[Configurable]] field, then + if let Some(configurable) = desc.configurable() { + // a. Perform ! CreateDataPropertyOrThrow(obj, "configurable", Desc.[[Configurable]]). + obj.create_data_property_or_throw("configurable", configurable, context) + .expect("CreateDataPropertyOrThrow cannot fail here"); } + + // 10. Return obj. + obj.into() } /// Uses the `SameValue` algorithm to check equality of objects diff --git a/boa_engine/src/builtins/promise/mod.rs b/boa_engine/src/builtins/promise/mod.rs index 7bdf64cbc7..dc79743c05 100644 --- a/boa_engine/src/builtins/promise/mod.rs +++ b/boa_engine/src/builtins/promise/mod.rs @@ -65,6 +65,7 @@ pub(crate) enum PromiseState { Rejected(JsValue), } +/// The internal representation of a `Promise` object. #[derive(Debug, Clone, Trace, Finalize)] pub struct Promise { promise_state: PromiseState, @@ -73,24 +74,52 @@ pub struct Promise { promise_is_handled: bool, } +/// The internal `PromiseReaction` data type. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-promisereaction-records #[derive(Debug, Clone, Trace, Finalize)] -pub struct ReactionRecord { +pub(crate) struct ReactionRecord { + /// The `[[Capability]]` field. promise_capability: Option, + + /// The `[[Type]]` field. #[unsafe_ignore_trace] reaction_type: ReactionType, + + /// The `[[Handler]]` field. handler: Option, } +/// The `[[Type]]` field values of a `PromiseReaction` record. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-promisereaction-records #[derive(Debug, Clone, Copy)] enum ReactionType { Fulfill, Reject, } +/// The internal `PromiseCapability` data type. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-promisecapability-records #[derive(Debug, Clone, Trace, Finalize)] pub struct PromiseCapability { + /// The `[[Promise]]` field. promise: JsObject, + + /// The `[[Resolve]]` field. resolve: JsFunction, + + /// The `[[Reject]]` field. reject: JsFunction, } @@ -202,17 +231,17 @@ impl PromiseCapability { } /// Returns the promise object. - pub(crate) fn promise(&self) -> &JsObject { + pub(crate) const fn promise(&self) -> &JsObject { &self.promise } /// Returns the resolve function. - pub(crate) fn resolve(&self) -> &JsFunction { + pub(crate) const fn resolve(&self) -> &JsFunction { &self.resolve } /// Returns the reject function. - pub(crate) fn reject(&self) -> &JsFunction { + pub(crate) const fn reject(&self) -> &JsFunction { &self.reject } } @@ -276,7 +305,7 @@ impl Promise { const LENGTH: usize = 1; /// Gets the current state of the promise. - pub(crate) fn state(&self) -> &PromiseState { + pub(crate) const fn state(&self) -> &PromiseState { &self.promise_state } @@ -1277,7 +1306,7 @@ impl Promise { .borrow_mut() .as_promise_mut() .expect("Expected promise to be a Promise") - .fulfill_promise(resolution, context)?; + .fulfill_promise(resolution, context); // b. Return undefined. return Ok(JsValue::Undefined); @@ -1310,7 +1339,7 @@ impl Promise { .borrow_mut() .as_promise_mut() .expect("Expected promise to be a Promise") - .fulfill_promise(resolution, context)?; + .fulfill_promise(resolution, context); // b. Return undefined. return Ok(JsValue::Undefined); @@ -1406,7 +1435,11 @@ impl Promise { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-fulfillpromise - pub fn fulfill_promise(&mut self, value: &JsValue, context: &mut Context) -> JsResult<()> { + /// + /// # Panics + /// + /// Panics if `Promise` is not pending. + fn fulfill_promise(&mut self, value: &JsValue, context: &mut Context) { // 1. Assert: The value of promise.[[PromiseState]] is pending. assert!( matches!(self.promise_state, PromiseState::Pending), @@ -1431,7 +1464,6 @@ impl Promise { self.promise_state = PromiseState::Fulfilled(value.clone()); // 8. Return unused. - Ok(()) } /// `RejectPromise ( promise, reason )` @@ -1443,6 +1475,10 @@ impl Promise { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-rejectpromise + /// + /// # Panics + /// + /// Panics if `Promise` is not pending. pub fn reject_promise(&mut self, reason: &JsValue, context: &mut Context) { // 1. Assert: The value of promise.[[PromiseState]] is pending. assert!( @@ -1488,7 +1524,7 @@ impl Promise { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-triggerpromisereactions - pub fn trigger_promise_reactions( + fn trigger_promise_reactions( reactions: &[ReactionRecord], argument: &JsValue, context: &mut Context, @@ -1745,7 +1781,7 @@ impl Promise { let c = promise_obj.species_constructor(StandardConstructors::promise, context)?; // 4. Assert: IsConstructor(C) is true. - assert!(c.is_constructor()); + debug_assert!(c.is_constructor()); let on_finally = args.get_or_undefined(0); @@ -2065,13 +2101,13 @@ impl Promise { let promise_resolve = promise_constructor.get("resolve", context)?; // 2. If IsCallable(promiseResolve) is false, throw a TypeError exception. - if let Some(promise_resolve) = promise_resolve.as_callable() { - // 3. Return promiseResolve. - Ok(promise_resolve.clone()) - } else { - Err(JsNativeError::typ() - .with_message("retrieving a non-callable promise resolver") - .into()) - } + promise_resolve.as_callable().map_or_else( + || { + Err(JsNativeError::typ() + .with_message("retrieving a non-callable promise resolver") + .into()) + }, + |promise_resolve| Ok(promise_resolve.clone()), + ) } } diff --git a/boa_engine/src/builtins/regexp/mod.rs b/boa_engine/src/builtins/regexp/mod.rs index f8b3ab6b17..3db60a5f32 100644 --- a/boa_engine/src/builtins/regexp/mod.rs +++ b/boa_engine/src/builtins/regexp/mod.rs @@ -37,7 +37,7 @@ use tap::{Conv, Pipe}; #[cfg(test)] mod tests; -/// The internal representation on a `RegExp` object. +/// The internal representation of a `RegExp` object. #[derive(Debug, Clone)] pub struct RegExp { /// Regex matcher. @@ -615,42 +615,42 @@ impl RegExp { ) -> JsResult { // 1. Let R be the this value. // 2. If Type(R) is not Object, throw a TypeError exception. - if let Some(object) = this.as_object() { - let object = object.borrow(); + let object = if let Some(object) = this.as_object() { + object + } else { + return Err(JsNativeError::typ() + .with_message("RegExp.prototype.source method called on incompatible value") + .into()); + }; - match object.as_regexp() { - // 3. If R does not have an [[OriginalSource]] internal slot, then - None => { - // a. If SameValue(R, %RegExp.prototype%) is true, return "(?:)". - // b. Otherwise, throw a TypeError exception. - if JsValue::same_value( - this, - &JsValue::new(context.intrinsics().constructors().regexp().prototype()), - ) { - Ok(JsValue::new("(?:)")) - } else { - Err(JsNativeError::typ() - .with_message( - "RegExp.prototype.source method called on incompatible value", - ) - .into()) - } - } - // 4. Assert: R has an [[OriginalFlags]] internal slot. - Some(re) => { - // 5. Let src be R.[[OriginalSource]]. - // 6. Let flags be R.[[OriginalFlags]]. - // 7. Return EscapeRegExpPattern(src, flags). - Ok(Self::escape_pattern( - &re.original_source, - &re.original_flags, - )) + let object = object.borrow(); + + match object.as_regexp() { + // 3. If R does not have an [[OriginalSource]] internal slot, then + None => { + // a. If SameValue(R, %RegExp.prototype%) is true, return "(?:)". + // b. Otherwise, throw a TypeError exception. + if JsValue::same_value( + this, + &JsValue::new(context.intrinsics().constructors().regexp().prototype()), + ) { + Ok(JsValue::new("(?:)")) + } else { + Err(JsNativeError::typ() + .with_message("RegExp.prototype.source method called on incompatible value") + .into()) } } - } else { - Err(JsNativeError::typ() - .with_message("RegExp.prototype.source method called on incompatible value") - .into()) + // 4. Assert: R has an [[OriginalFlags]] internal slot. + Some(re) => { + // 5. Let src be R.[[OriginalSource]]. + // 6. Let flags be R.[[OriginalFlags]]. + // 7. Return EscapeRegExpPattern(src, flags). + Ok(Self::escape_pattern( + &re.original_source, + &re.original_flags, + )) + } } } @@ -752,11 +752,8 @@ impl RegExp { let arg_str = args.get_or_undefined(0).to_string(context)?; // 4. Return ? RegExpBuiltinExec(R, S). - if let Some(v) = Self::abstract_builtin_exec(obj, &arg_str, context)? { - Ok(v.into()) - } else { - Ok(JsValue::null()) - } + (Self::abstract_builtin_exec(obj, &arg_str, context)?) + .map_or_else(|| Ok(JsValue::null()), |v| Ok(v.into())) } /// `22.2.5.2.1 RegExpExec ( R, S )` @@ -1010,14 +1007,13 @@ impl RegExp { // a. Let captureI be ith element of r's captures List. let capture = match_value.group(i as usize); - let captured_value = match capture { - // b. If captureI is undefined, let capturedValue be undefined. - None => JsValue::undefined(), - // c. Else if fullUnicode is true, then - // d. Else, - // TODO: Full UTF-16 regex support - Some(range) => js_string!(&lossy_input[range]).into(), - }; + // b. If captureI is undefined, let capturedValue be undefined. + // c. Else if fullUnicode is true, then + // d. Else, + // TODO: Full UTF-16 regex support + let captured_value = capture.map_or_else(JsValue::undefined, |range| { + js_string!(&lossy_input[range]).into() + }); // e. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(i)), capturedValue). a.create_data_property_or_throw(i, captured_value, context) @@ -1060,11 +1056,8 @@ impl RegExp { #[allow(clippy::if_not_else)] if !global { // a. Return ? RegExpExec(rx, S). - if let Some(v) = Self::abstract_exec(rx, arg_str, context)? { - Ok(v.into()) - } else { - Ok(JsValue::null()) - } + (Self::abstract_exec(rx, arg_str, context)?) + .map_or_else(|| Ok(JsValue::null()), |v| Ok(v.into())) // 6. Else, } else { // a. Assert: global is true. @@ -1365,8 +1358,7 @@ impl RegExp { // k. If functionalReplace is true, then // l. Else, - let replacement: JsString; - if functional_replace { + let replacement = if functional_replace { // i. Let replacerArgs be « matched ». let mut replacer_args = vec![JsValue::new(matched)]; @@ -1388,7 +1380,7 @@ impl RegExp { context.call(&replace_value, &JsValue::undefined(), &replacer_args)?; // vi. Let replacement be ? ToString(replValue). - replacement = repl_value.to_string(context)?; + repl_value.to_string(context)? } else { // i. If namedCaptures is not undefined, then if !named_captures.is_undefined() { @@ -1397,7 +1389,7 @@ impl RegExp { } // ii. Let replacement be ? GetSubstitution(matched, S, position, captures, namedCaptures, replaceValue). - replacement = string::get_substitution( + string::get_substitution( &matched, &arg_str, position, @@ -1405,8 +1397,8 @@ impl RegExp { &named_captures, &replace_value.to_string(context)?, context, - )?; - } + )? + }; // m. If position ≥ nextSourcePosition, then if position >= next_source_position { @@ -1481,11 +1473,10 @@ impl RegExp { // 9. If result is null, return -1𝔽. // 10. Return ? Get(result, "index"). - if let Some(result) = result { - result.get("index", context) - } else { - Ok(JsValue::new(-1)) - } + result.map_or_else( + || Ok(JsValue::new(-1)), + |result| result.get("index", context), + ) } /// `RegExp.prototype [ @@split ] ( string, limit )` diff --git a/boa_engine/src/builtins/regexp/regexp_string_iterator.rs b/boa_engine/src/builtins/regexp/regexp_string_iterator.rs index 7c4724b5c3..cf624e8a2e 100644 --- a/boa_engine/src/builtins/regexp/regexp_string_iterator.rs +++ b/boa_engine/src/builtins/regexp/regexp_string_iterator.rs @@ -22,7 +22,12 @@ use boa_gc::{Finalize, Trace}; use boa_profiler::Profiler; use regexp::{advance_string_index, RegExp}; -// TODO: See todos in create_regexp_string_iterator and next. +/// The `RegExp String Iterator` object. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-regexp-string-iterator-objects #[derive(Debug, Clone, Finalize, Trace)] pub struct RegExpStringIterator { matcher: JsObject, @@ -81,6 +86,12 @@ impl RegExpStringIterator { regexp_string_iterator.into() } + /// `%RegExpStringIteratorPrototype%.next ( )` + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-%regexpstringiteratorprototype%.next pub fn next(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { let mut iterator = this.as_object().map(JsObject::borrow_mut); let iterator = iterator diff --git a/boa_engine/src/builtins/set/mod.rs b/boa_engine/src/builtins/set/mod.rs index 997225dedd..124309ed55 100644 --- a/boa_engine/src/builtins/set/mod.rs +++ b/boa_engine/src/builtins/set/mod.rs @@ -33,7 +33,7 @@ pub mod set_iterator; mod tests; #[derive(Debug, Clone)] -pub(crate) struct Set(OrderedSet); +pub(crate) struct Set; impl BuiltIn for Set { const NAME: &'static str = "Set"; @@ -363,7 +363,7 @@ impl Set { let arguments = this .as_object() .and_then(|obj| { - obj.borrow().as_set_ref().map(|set| { + obj.borrow().as_set().map(|set| { set.get_index(index) .map(|value| [value.clone(), value.clone(), this.clone()]) }) @@ -394,11 +394,7 @@ impl Set { let value = args.get_or_undefined(0); this.as_object() - .and_then(|obj| { - obj.borrow() - .as_set_ref() - .map(|set| set.contains(value).into()) - }) + .and_then(|obj| obj.borrow().as_set().map(|set| set.contains(value).into())) .ok_or_else(|| { JsNativeError::typ() .with_message("'this' is not a Set") @@ -448,7 +444,7 @@ impl Set { /// Helper function to get the size of the `Set` object. pub(crate) fn get_size(set: &JsValue) -> JsResult { set.as_object() - .and_then(|obj| obj.borrow().as_set_ref().map(OrderedSet::size)) + .and_then(|obj| obj.borrow().as_set().map(OrderedSet::size)) .ok_or_else(|| { JsNativeError::typ() .with_message("'this' is not a Set") diff --git a/boa_engine/src/builtins/set/ordered_set.rs b/boa_engine/src/builtins/set/ordered_set.rs index 6d8753fbdd..77754fdaf6 100644 --- a/boa_engine/src/builtins/set/ordered_set.rs +++ b/boa_engine/src/builtins/set/ordered_set.rs @@ -1,3 +1,5 @@ +//! Implements a set type that preserves insertion order. + use boa_gc::{custom_trace, Finalize, Trace}; use indexmap::{ set::{IntoIter, Iter}, @@ -43,12 +45,16 @@ impl OrderedSet where V: Hash + Eq, { + /// Creates a new empty `OrderedSet`. + #[must_use] pub fn new() -> Self { Self { inner: IndexSet::new(), } } + /// Creates a new empty `OrderedSet` with the specified capacity. + #[must_use] pub fn with_capacity(capacity: usize) -> Self { Self { inner: IndexSet::with_capacity(capacity), @@ -58,6 +64,7 @@ where /// Return the number of key-value pairs in the map. /// /// Computes in **O(1)** time. + #[must_use] pub fn size(&self) -> usize { self.inner.len() } @@ -65,6 +72,7 @@ where /// Returns true if the map contains no elements. /// /// Computes in **O(1)** time. + #[must_use] pub fn is_empty(&self) -> bool { self.inner.len() == 0 } @@ -108,11 +116,13 @@ where /// Get a key-value pair by index /// Valid indices are 0 <= index < self.len() /// Computes in O(1) time. + #[must_use] pub fn get_index(&self, index: usize) -> Option<&V> { self.inner.get_index(index) } /// Return an iterator over the values of the set, in their order + #[must_use] pub fn iter(&self) -> Iter<'_, V> { self.inner.iter() } diff --git a/boa_engine/src/builtins/set/set_iterator.rs b/boa_engine/src/builtins/set/set_iterator.rs index 653458b81c..265b78fc49 100644 --- a/boa_engine/src/builtins/set/set_iterator.rs +++ b/boa_engine/src/builtins/set/set_iterator.rs @@ -1,3 +1,10 @@ +//! This module implements the `SetIterator` object. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma262/#sec-set-iterator-objects + use crate::{ builtins::{function::make_builtin_fn, iterable::create_iter_result_object, Array, JsValue}, error::JsNativeError, @@ -27,7 +34,7 @@ impl SetIterator { pub(crate) const NAME: &'static str = "SetIterator"; /// Constructs a new `SetIterator`, that will iterate over `set`, starting at index 0 - fn new(set: JsValue, kind: PropertyNameKind) -> Self { + const fn new(set: JsValue, kind: PropertyNameKind) -> Self { Self { iterated_set: set, next_index: 0, @@ -90,7 +97,7 @@ impl SetIterator { let entries = m.as_object().map(JsObject::borrow); let entries = entries .as_ref() - .and_then(|obj| obj.as_set_ref()) + .and_then(|obj| obj.as_set()) .ok_or_else(|| JsNativeError::typ().with_message("'this' is not a Set"))?; let num_entries = entries.size(); diff --git a/boa_engine/src/builtins/string/mod.rs b/boa_engine/src/builtins/string/mod.rs index f9a5816c0d..249c025074 100644 --- a/boa_engine/src/builtins/string/mod.rs +++ b/boa_engine/src/builtins/string/mod.rs @@ -41,7 +41,7 @@ pub(crate) enum Placement { /// Helper function to check if a `char` is trimmable. #[inline] -pub(crate) fn is_trimmable_whitespace(c: char) -> bool { +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: @@ -258,7 +258,7 @@ impl String { } // c. If ℝ(nextCP) < 0 or ℝ(nextCP) > 0x10FFFF, throw a RangeError exception. - if nextcp < 0.0 || nextcp > f64::from(0x10FFFF) { + if nextcp < 0.0 || nextcp > f64::from(0x0010_FFFF) { return Err(JsNativeError::range() .with_message(format!("invalid code point: {nextcp}")) .into()); diff --git a/boa_engine/src/builtins/string/string_iterator.rs b/boa_engine/src/builtins/string/string_iterator.rs index fb3c5493a0..4fa1c6631c 100644 --- a/boa_engine/src/builtins/string/string_iterator.rs +++ b/boa_engine/src/builtins/string/string_iterator.rs @@ -1,3 +1,10 @@ +//! This module implements the `StringIterator` object. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! +//! [spec]: https://tc39.es/ecma262/#sec-string-iterator-objects + use crate::{ builtins::{function::make_builtin_fn, iterable::create_iter_result_object}, error::JsNativeError, @@ -9,6 +16,12 @@ use crate::{ use boa_gc::{Finalize, Trace}; use boa_profiler::Profiler; +/// The `StringIterator` object represents an iteration over a string. It implements the iterator protocol. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-string-iterator-objects #[derive(Debug, Clone, Finalize, Trace)] pub struct StringIterator { string: JsValue, @@ -16,13 +29,7 @@ pub struct StringIterator { } impl StringIterator { - fn new(string: JsValue) -> Self { - Self { - string, - next_index: 0, - } - } - + /// Create a new `StringIterator`. pub fn create_string_iterator(string: JsValue, context: &mut Context) -> JsResult { let string_iterator = JsObject::from_proto_and_data( context @@ -30,11 +37,15 @@ impl StringIterator { .objects() .iterator_prototypes() .string_iterator(), - ObjectData::string_iterator(Self::new(string)), + ObjectData::string_iterator(Self { + string, + next_index: 0, + }), ); Ok(string_iterator.into()) } + /// `StringIterator.prototype.next( )` pub fn next(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { let mut string_iterator = this.as_object().map(JsObject::borrow_mut); let string_iterator = string_iterator diff --git a/boa_engine/src/builtins/symbol/mod.rs b/boa_engine/src/builtins/symbol/mod.rs index 5ed87e055d..899b63b8fc 100644 --- a/boa_engine/src/builtins/symbol/mod.rs +++ b/boa_engine/src/builtins/symbol/mod.rs @@ -70,6 +70,7 @@ impl GlobalSymbolRegistry { } } +/// The internal representation of a `Symbol` object. #[derive(Debug, Clone, Copy)] pub struct Symbol; @@ -253,12 +254,14 @@ impl Symbol { _: &[JsValue], _: &mut Context, ) -> JsResult { - let symbol = Self::this_symbol_value(this)?; - if let Some(ref description) = symbol.description() { - Ok(description.clone().into()) - } else { - Ok(JsValue::undefined()) - } + // 1. Let s be the this value. + // 2. Let sym be ? thisSymbolValue(s). + let sym = Self::this_symbol_value(this)?; + + // 3. Return sym.[[Description]]. + Ok(sym + .description() + .map_or(JsValue::undefined(), JsValue::from)) } /// `Symbol.for( key )` diff --git a/boa_engine/src/builtins/typed_array/integer_indexed_object.rs b/boa_engine/src/builtins/typed_array/integer_indexed_object.rs index 96841c9665..681fd38f10 100644 --- a/boa_engine/src/builtins/typed_array/integer_indexed_object.rs +++ b/boa_engine/src/builtins/typed_array/integer_indexed_object.rs @@ -34,7 +34,7 @@ pub struct IntegerIndexed { } impl IntegerIndexed { - pub(crate) fn new( + pub(crate) const fn new( viewed_array_buffer: Option, typed_array_name: TypedArrayKind, byte_offset: u64, @@ -101,7 +101,7 @@ impl IntegerIndexed { } /// Get the integer indexed object's byte offset. - pub(crate) fn byte_offset(&self) -> u64 { + pub(crate) const fn byte_offset(&self) -> u64 { self.byte_offset } @@ -111,12 +111,12 @@ impl IntegerIndexed { } /// Get the integer indexed object's typed array name. - pub(crate) fn typed_array_name(&self) -> TypedArrayKind { + pub(crate) const fn typed_array_name(&self) -> TypedArrayKind { self.typed_array_name } /// Get a reference to the integer indexed object's viewed array buffer. - pub fn viewed_array_buffer(&self) -> Option<&JsObject> { + pub const fn viewed_array_buffer(&self) -> Option<&JsObject> { self.viewed_array_buffer.as_ref() } @@ -126,7 +126,7 @@ impl IntegerIndexed { } /// Get the integer indexed object's byte length. - pub fn byte_length(&self) -> u64 { + pub const fn byte_length(&self) -> u64 { self.byte_length } @@ -136,7 +136,7 @@ impl IntegerIndexed { } /// Get the integer indexed object's array length. - pub fn array_length(&self) -> u64 { + pub const fn array_length(&self) -> u64 { self.array_length } diff --git a/boa_engine/src/builtins/typed_array/mod.rs b/boa_engine/src/builtins/typed_array/mod.rs index b40bfca0c0..aa1ecee2f1 100644 --- a/boa_engine/src/builtins/typed_array/mod.rs +++ b/boa_engine/src/builtins/typed_array/mod.rs @@ -2757,9 +2757,6 @@ impl TypedArray { { return Ok(Ordering::Greater); } - - // 10. Return +0𝔽. - Ok(Ordering::Equal) } else { let x = x .as_number() @@ -2802,10 +2799,10 @@ impl TypedArray { if x.is_zero() && y.is_zero() && x.is_sign_positive() && y.is_sign_negative() { return Ok(Ordering::Greater); } - - // 10. Return +0𝔽. - Ok(Ordering::Equal) } + + // 10. Return +0𝔽. + Ok(Ordering::Equal) }; // 8. Sort items using an implementation-defined sequence of calls to SortCompare. @@ -3542,7 +3539,7 @@ impl TypedArrayKind { } } - pub(crate) fn is_big_int_element_type(self) -> bool { + pub(crate) const fn is_big_int_element_type(self) -> bool { matches!(self, Self::BigUint64 | Self::BigInt64) } } diff --git a/boa_engine/src/builtins/weak/mod.rs b/boa_engine/src/builtins/weak/mod.rs index 0beedf0384..6600a25952 100644 --- a/boa_engine/src/builtins/weak/mod.rs +++ b/boa_engine/src/builtins/weak/mod.rs @@ -1,3 +1,5 @@ +//! This module implements the global `Weak*` objects. + mod weak_ref; pub(crate) use weak_ref::WeakRef; diff --git a/boa_engine/src/bytecompiler/function.rs b/boa_engine/src/bytecompiler/function.rs index 9f4a385c37..1b248eaee3 100644 --- a/boa_engine/src/bytecompiler/function.rs +++ b/boa_engine/src/bytecompiler/function.rs @@ -26,7 +26,7 @@ pub(crate) struct FunctionCompiler { impl FunctionCompiler { /// Create a new `FunctionCompiler`. #[inline] - pub(crate) fn new() -> Self { + pub(crate) const fn new() -> Self { Self { name: Sym::EMPTY_STRING, generator: false, @@ -52,34 +52,34 @@ impl FunctionCompiler { /// Indicate if the function is an arrow function. #[inline] - pub(crate) fn arrow(mut self, arrow: bool) -> Self { + pub(crate) const fn arrow(mut self, arrow: bool) -> Self { self.arrow = arrow; self } /// Indicate if the function is a generator function. #[inline] - pub(crate) fn generator(mut self, generator: bool) -> Self { + pub(crate) const fn generator(mut self, generator: bool) -> Self { self.generator = generator; self } /// Indicate if the function is an async function. #[inline] - pub(crate) fn r#async(mut self, r#async: bool) -> Self { + pub(crate) const fn r#async(mut self, r#async: bool) -> Self { self.r#async = r#async; self } /// Indicate if the function is in a strict context. #[inline] - pub(crate) fn strict(mut self, strict: bool) -> Self { + pub(crate) const fn strict(mut self, strict: bool) -> Self { self.strict = strict; self } /// Indicate if the function has a binding identifier. #[inline] - pub(crate) fn has_binding_identifier(mut self, has_binding_identifier: bool) -> Self { + pub(crate) const fn has_binding_identifier(mut self, has_binding_identifier: bool) -> Self { self.has_binding_identifier = has_binding_identifier; self } diff --git a/boa_engine/src/bytecompiler/mod.rs b/boa_engine/src/bytecompiler/mod.rs index 8bff029b4e..7166cd0476 100644 --- a/boa_engine/src/bytecompiler/mod.rs +++ b/boa_engine/src/bytecompiler/mod.rs @@ -1,3 +1,5 @@ +//! This module contains the bytecode compiler. + mod function; use crate::{ @@ -57,6 +59,7 @@ enum FunctionKind { /// Describes the complete specification of a function node. #[derive(Debug, Clone, Copy, PartialEq)] +#[allow(single_use_lifetimes)] struct FunctionSpec<'a> { kind: FunctionKind, name: Option, @@ -65,14 +68,14 @@ struct FunctionSpec<'a> { has_binding_identifier: bool, } -impl<'a> FunctionSpec<'a> { +impl FunctionSpec<'_> { #[inline] - fn is_arrow(&self) -> bool { + const fn is_arrow(&self) -> bool { matches!(self.kind, FunctionKind::Arrow | FunctionKind::AsyncArrow) } #[inline] - fn is_async(&self) -> bool { + const fn is_async(&self) -> bool { matches!( self.kind, FunctionKind::Async | FunctionKind::AsyncGenerator | FunctionKind::AsyncArrow @@ -80,7 +83,7 @@ impl<'a> FunctionSpec<'a> { } #[inline] - fn is_generator(&self) -> bool { + const fn is_generator(&self) -> bool { matches!( self.kind, FunctionKind::Generator | FunctionKind::AsyncGenerator @@ -208,7 +211,7 @@ enum Access<'a> { } impl Access<'_> { - fn from_assign_target(target: &AssignTarget) -> Result, &Pattern> { + const fn from_assign_target(target: &AssignTarget) -> Result, &Pattern> { match target { AssignTarget::Identifier(ident) => Ok(Access::Variable { name: *ident }), AssignTarget::Access(access) => Ok(Access::Property { access }), @@ -216,7 +219,7 @@ impl Access<'_> { } } - fn from_expression(expr: &Expression) -> Option> { + const fn from_expression(expr: &Expression) -> Option> { match expr { Expression::Identifier(name) => Some(Access::Variable { name: *name }), Expression::PropertyAccess(access) => Some(Access::Property { access }), @@ -226,6 +229,7 @@ impl Access<'_> { } } +/// The [`ByteCompiler`] is used to compile ECMAScript AST from [`boa_ast`] to bytecode. #[derive(Debug)] pub struct ByteCompiler<'b> { code_block: CodeBlock, @@ -242,6 +246,7 @@ impl<'b> ByteCompiler<'b> { /// Represents a placeholder address that will be patched later. const DUMMY_ADDRESS: u32 = u32::MAX; + /// Creates a new [`ByteCompiler`]. #[inline] pub fn new(name: Sym, strict: bool, json_parse: bool, context: &'b mut Context) -> Self { Self { @@ -865,6 +870,7 @@ impl<'b> ByteCompiler<'b> { Ok(()) } + /// Compile a [`StatementList`]. #[inline] pub fn compile_statement_list( &mut self, @@ -910,6 +916,7 @@ impl<'b> ByteCompiler<'b> { Ok(()) } + /// Compile an [`Expression`]. #[inline] pub fn compile_expr(&mut self, expr: &Expression, use_expr: bool) -> JsResult<()> { match expr { @@ -1747,7 +1754,8 @@ impl<'b> ByteCompiler<'b> { Ok(()) } - pub fn compile_var_decl(&mut self, decl: &VarDeclaration) -> JsResult<()> { + /// Compile a [`VarDeclaration`]. + fn compile_var_decl(&mut self, decl: &VarDeclaration) -> JsResult<()> { for variable in decl.0.as_ref() { match variable.binding() { Binding::Identifier(ident) => { @@ -1773,7 +1781,8 @@ impl<'b> ByteCompiler<'b> { Ok(()) } - pub fn compile_lexical_decl(&mut self, decl: &LexicalDeclaration) -> JsResult<()> { + /// Compile a [`LexicalDeclaration`]. + fn compile_lexical_decl(&mut self, decl: &LexicalDeclaration) -> JsResult<()> { match decl { LexicalDeclaration::Let(decls) => { for variable in decls.as_ref() { @@ -1826,8 +1835,9 @@ impl<'b> ByteCompiler<'b> { Ok(()) } + /// Compile a [`StatementListItem`]. #[inline] - pub fn compile_stmt_list_item( + fn compile_stmt_list_item( &mut self, item: &StatementListItem, use_expr: bool, @@ -1841,6 +1851,7 @@ impl<'b> ByteCompiler<'b> { } } + /// Compile a [`Declaration`]. #[inline] pub fn compile_decl(&mut self, decl: &Declaration) -> JsResult<()> { match decl { @@ -1861,8 +1872,9 @@ impl<'b> ByteCompiler<'b> { } } + /// Compile a [`ForLoop`]. #[inline] - pub fn compile_for_loop( + fn compile_for_loop( &mut self, for_loop: &ForLoop, label: Option, @@ -1921,8 +1933,9 @@ impl<'b> ByteCompiler<'b> { Ok(()) } + /// Compile a [`ForInLoop`]. #[inline] - pub fn compile_for_in_loop( + fn compile_for_in_loop( &mut self, for_in_loop: &ForInLoop, label: Option, @@ -2036,8 +2049,9 @@ impl<'b> ByteCompiler<'b> { Ok(()) } + /// Compile a [`ForOfLoop`]. #[inline] - pub fn compile_for_of_loop( + fn compile_for_of_loop( &mut self, for_of_loop: &ForOfLoop, label: Option, @@ -2160,8 +2174,9 @@ impl<'b> ByteCompiler<'b> { Ok(()) } + /// Compile a [`WhileLoop`]. #[inline] - pub fn compile_while_loop( + fn compile_while_loop( &mut self, while_loop: &WhileLoop, label: Option, @@ -2183,8 +2198,9 @@ impl<'b> ByteCompiler<'b> { Ok(()) } + /// Compile a [`DoWhileLoop`]. #[inline] - pub fn compile_do_while_loop( + fn compile_do_while_loop( &mut self, do_while_loop: &DoWhileLoop, label: Option, @@ -2212,6 +2228,7 @@ impl<'b> ByteCompiler<'b> { Ok(()) } + /// Compile a [`Block`]. #[inline] pub fn compile_block( &mut self, @@ -2242,6 +2259,7 @@ impl<'b> ByteCompiler<'b> { Ok(()) } + /// Compile a [`Statement`]. #[inline] pub fn compile_stmt( &mut self, @@ -2346,11 +2364,9 @@ impl<'b> ByteCompiler<'b> { .filter(|info| info.kind == JumpControlInfoKind::Try) { let start_address = info.start_address; - let in_finally = if let Some(finally_start) = info.finally_start { - next > finally_start.index - } else { - false - }; + let in_finally = info + .finally_start + .map_or(false, |finally_start| next > finally_start.index); let in_catch_no_finally = !info.has_finally && info.in_catch; if in_finally { @@ -2358,11 +2374,10 @@ impl<'b> ByteCompiler<'b> { } if in_finally || in_catch_no_finally { self.emit_opcode(Opcode::CatchEnd2); - self.emit(Opcode::FinallySetJump, &[start_address]); } else { self.emit_opcode(Opcode::TryEnd); - self.emit(Opcode::FinallySetJump, &[start_address]); } + self.emit(Opcode::FinallySetJump, &[start_address]); let label = self.jump(); self.jump_info .last_mut() @@ -2428,11 +2443,9 @@ impl<'b> ByteCompiler<'b> { .last() .filter(|info| info.kind == JumpControlInfoKind::Try) { - let in_finally = if let Some(finally_start) = info.finally_start { - next >= finally_start.index - } else { - false - }; + let in_finally = info + .finally_start + .map_or(false, |finally_start| next >= finally_start.index); let in_catch_no_finally = !info.has_finally && info.in_catch; if in_finally { @@ -2785,7 +2798,10 @@ impl<'b> ByteCompiler<'b> { Ok(()) } + /// Finish compiling code with the [`ByteCompiler`] and return the generated [`CodeBlock`]. #[inline] + #[must_use] + #[allow(clippy::missing_const_for_fn)] pub fn finish(self) -> CodeBlock { self.code_block } diff --git a/boa_engine/src/context/icu.rs b/boa_engine/src/context/icu.rs index 880d9d0191..b89f564990 100644 --- a/boa_engine/src/context/icu.rs +++ b/boa_engine/src/context/icu.rs @@ -67,7 +67,7 @@ impl Icu { } /// Get the [`LocaleCanonicalizer`] tool. - pub(crate) fn locale_canonicalizer(&self) -> &LocaleCanonicalizer { + pub(crate) const fn locale_canonicalizer(&self) -> &LocaleCanonicalizer { &self.locale_canonicalizer } diff --git a/boa_engine/src/context/intrinsics.rs b/boa_engine/src/context/intrinsics.rs index 0da0e55581..772883bd10 100644 --- a/boa_engine/src/context/intrinsics.rs +++ b/boa_engine/src/context/intrinsics.rs @@ -1,3 +1,5 @@ +//! This module implements the data structures that contain intrinsic objects and constructors. + use crate::{ builtins::{ array::Array, error::r#type::create_throw_type_error, iterable::IteratorPrototypes, @@ -7,6 +9,7 @@ use crate::{ Context, }; +/// The intrinsic objects and constructors. #[derive(Debug, Default)] pub struct Intrinsics { /// Cached standard constructors @@ -18,13 +21,13 @@ pub struct Intrinsics { impl Intrinsics { /// Return the cached intrinsic objects. #[inline] - pub fn objects(&self) -> &IntrinsicObjects { + pub const fn objects(&self) -> &IntrinsicObjects { &self.objects } /// Return the cached standard constructors. #[inline] - pub fn constructors(&self) -> &StandardConstructors { + pub const fn constructors(&self) -> &StandardConstructors { &self.constructors } } @@ -195,218 +198,476 @@ impl Default for StandardConstructors { } impl StandardConstructors { + /// Returns the `AsyncGeneratorFunction` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-asyncgeneratorfunction-constructor #[inline] - pub fn async_generator_function(&self) -> &StandardConstructor { + pub const fn async_generator_function(&self) -> &StandardConstructor { &self.async_generator_function } + /// Returns the `AsyncGenerator` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-asyncgenerator-objects #[inline] - pub fn async_generator(&self) -> &StandardConstructor { + pub const fn async_generator(&self) -> &StandardConstructor { &self.async_generator } + /// Returns the `Object` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-object-constructor #[inline] - pub fn object(&self) -> &StandardConstructor { + pub const fn object(&self) -> &StandardConstructor { &self.object } + /// Returns the `Proxy` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-proxy-constructor #[inline] - pub fn proxy(&self) -> &StandardConstructor { + pub const fn proxy(&self) -> &StandardConstructor { &self.proxy } + /// Returns the `Date` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-date-constructor #[inline] - pub fn date(&self) -> &StandardConstructor { + pub const fn date(&self) -> &StandardConstructor { &self.date } + /// Returns the `Function` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-function-constructor #[inline] - pub fn function(&self) -> &StandardConstructor { + pub const fn function(&self) -> &StandardConstructor { &self.function } + /// Returns the `AsyncFunction` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-async-function-constructor #[inline] - pub fn async_function(&self) -> &StandardConstructor { + pub const fn async_function(&self) -> &StandardConstructor { &self.async_function } + /// Returns the `Generator` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-generator-objects #[inline] - pub fn generator(&self) -> &StandardConstructor { + pub const fn generator(&self) -> &StandardConstructor { &self.generator } + /// Returns the `GeneratorFunction` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-generatorfunction-constructor #[inline] - pub fn generator_function(&self) -> &StandardConstructor { + pub const fn generator_function(&self) -> &StandardConstructor { &self.generator_function } + /// Returns the `Array` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-array-constructor #[inline] - pub fn array(&self) -> &StandardConstructor { + pub const fn array(&self) -> &StandardConstructor { &self.array } + /// Returns the `BigInt` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-bigint-constructor #[inline] - pub fn bigint_object(&self) -> &StandardConstructor { + pub const fn bigint_object(&self) -> &StandardConstructor { &self.bigint } + /// Returns the `Number` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-number-constructor #[inline] - pub fn number(&self) -> &StandardConstructor { + pub const fn number(&self) -> &StandardConstructor { &self.number } + /// Returns the `Boolean` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-boolean-constructor #[inline] - pub fn boolean(&self) -> &StandardConstructor { + pub const fn boolean(&self) -> &StandardConstructor { &self.boolean } + /// Returns the `String` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-string-constructor #[inline] - pub fn string(&self) -> &StandardConstructor { + pub const fn string(&self) -> &StandardConstructor { &self.string } + /// Returns the `RegExp` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-regexp-constructor #[inline] - pub fn regexp(&self) -> &StandardConstructor { + pub const fn regexp(&self) -> &StandardConstructor { &self.regexp } + /// Returns the `Symbol` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-symbol-constructor #[inline] - pub fn symbol(&self) -> &StandardConstructor { + pub const fn symbol(&self) -> &StandardConstructor { &self.symbol } + /// Returns the `Error` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-error-constructor #[inline] - pub fn error(&self) -> &StandardConstructor { + pub const fn error(&self) -> &StandardConstructor { &self.error } + /// Returns the `ReferenceError` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-referenceerror #[inline] - pub fn reference_error(&self) -> &StandardConstructor { + pub const fn reference_error(&self) -> &StandardConstructor { &self.reference_error } + /// Returns the `TypeError` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-typeerror #[inline] - pub fn type_error(&self) -> &StandardConstructor { + pub const fn type_error(&self) -> &StandardConstructor { &self.type_error } + /// Returns the `RangeError` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-rangeerror #[inline] - pub fn range_error(&self) -> &StandardConstructor { + pub const fn range_error(&self) -> &StandardConstructor { &self.range_error } + /// Returns the `SyntaxError` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-syntaxerror #[inline] - pub fn syntax_error(&self) -> &StandardConstructor { + pub const fn syntax_error(&self) -> &StandardConstructor { &self.syntax_error } + /// Returns the `EvalError` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-evalerror #[inline] - pub fn eval_error(&self) -> &StandardConstructor { + pub const fn eval_error(&self) -> &StandardConstructor { &self.eval_error } + /// Returns the `URIError` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-native-error-types-used-in-this-standard-urierror #[inline] - pub fn uri_error(&self) -> &StandardConstructor { + pub const fn uri_error(&self) -> &StandardConstructor { &self.uri_error } + /// Returns the `AggregateError` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-aggregate-error-constructor #[inline] - pub fn aggregate_error(&self) -> &StandardConstructor { + pub const fn aggregate_error(&self) -> &StandardConstructor { &self.aggregate_error } + /// Returns the `Map` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-map-constructor #[inline] - pub fn map(&self) -> &StandardConstructor { + pub const fn map(&self) -> &StandardConstructor { &self.map } + /// Returns the `Set` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-set-constructor #[inline] - pub fn set(&self) -> &StandardConstructor { + pub const fn set(&self) -> &StandardConstructor { &self.set } + /// Returns the `TypedArray` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors #[inline] - pub fn typed_array(&self) -> &StandardConstructor { + pub const fn typed_array(&self) -> &StandardConstructor { &self.typed_array } + /// Returns the `Int8Array` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors #[inline] - pub fn typed_int8_array(&self) -> &StandardConstructor { + pub const fn typed_int8_array(&self) -> &StandardConstructor { &self.typed_int8_array } + /// Returns the `Uint8Array` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors #[inline] - pub fn typed_uint8_array(&self) -> &StandardConstructor { + pub const fn typed_uint8_array(&self) -> &StandardConstructor { &self.typed_uint8_array } + /// Returns the `Uint8ClampedArray` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors #[inline] - pub fn typed_uint8clamped_array(&self) -> &StandardConstructor { + pub const fn typed_uint8clamped_array(&self) -> &StandardConstructor { &self.typed_uint8clamped_array } + /// Returns the `Int16Array` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors #[inline] - pub fn typed_int16_array(&self) -> &StandardConstructor { + pub const fn typed_int16_array(&self) -> &StandardConstructor { &self.typed_int16_array } + /// Returns the `Uint16Array` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors #[inline] - pub fn typed_uint16_array(&self) -> &StandardConstructor { + pub const fn typed_uint16_array(&self) -> &StandardConstructor { &self.typed_uint16_array } + /// Returns the `Uint32Array` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors #[inline] - pub fn typed_uint32_array(&self) -> &StandardConstructor { + pub const fn typed_uint32_array(&self) -> &StandardConstructor { &self.typed_uint32_array } + /// Returns the `Int32Array` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors #[inline] - pub fn typed_int32_array(&self) -> &StandardConstructor { + pub const fn typed_int32_array(&self) -> &StandardConstructor { &self.typed_int32_array } + /// Returns the `BigInt64Array` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors #[inline] - pub fn typed_bigint64_array(&self) -> &StandardConstructor { + pub const fn typed_bigint64_array(&self) -> &StandardConstructor { &self.typed_bigint64_array } + /// Returns the `BigUint64Array` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors #[inline] - pub fn typed_biguint64_array(&self) -> &StandardConstructor { + pub const fn typed_biguint64_array(&self) -> &StandardConstructor { &self.typed_biguint64_array } + /// Returns the `Float32Array` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors #[inline] - pub fn typed_float32_array(&self) -> &StandardConstructor { + pub const fn typed_float32_array(&self) -> &StandardConstructor { &self.typed_float32_array } + /// Returns the `Float64Array` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-typedarray-constructors #[inline] - pub fn typed_float64_array(&self) -> &StandardConstructor { + pub const fn typed_float64_array(&self) -> &StandardConstructor { &self.typed_float64_array } + /// Returns the `ArrayBuffer` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-arraybuffer-constructor #[inline] - pub fn array_buffer(&self) -> &StandardConstructor { + pub const fn array_buffer(&self) -> &StandardConstructor { &self.array_buffer } + /// Returns the `DataView` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-dataview-constructor #[inline] - pub fn data_view(&self) -> &StandardConstructor { + pub const fn data_view(&self) -> &StandardConstructor { &self.data_view } + /// Returns the `Intl.DateTimeFormat` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma402/#sec-intl-datetimeformat-constructor #[inline] - pub fn date_time_format(&self) -> &StandardConstructor { + pub const fn date_time_format(&self) -> &StandardConstructor { &self.date_time_format } + /// Returns the `Promise` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-promise-constructor #[inline] - pub fn promise(&self) -> &StandardConstructor { + pub const fn promise(&self) -> &StandardConstructor { &self.promise } + /// Returns the `WeakRef` constructor. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-weak-ref-constructor #[inline] - pub fn weak_ref(&self) -> &StandardConstructor { + pub const fn weak_ref(&self) -> &StandardConstructor { &self.weak_ref } } @@ -448,7 +709,7 @@ impl IntrinsicObjects { /// Get the cached iterator prototypes. #[inline] - pub fn iterator_prototypes(&self) -> &IteratorPrototypes { + pub const fn iterator_prototypes(&self) -> &IteratorPrototypes { &self.iterator_prototypes } } diff --git a/boa_engine/src/context/mod.rs b/boa_engine/src/context/mod.rs index 1e5df8736b..74a95b5c98 100644 --- a/boa_engine/src/context/mod.rs +++ b/boa_engine/src/context/mod.rs @@ -117,12 +117,14 @@ impl Default for Context { impl Context { /// Create a new [`ContextBuilder`] to specify the [`Interner`] and/or /// the icu data provider. + #[must_use] pub fn builder() -> ContextBuilder { ContextBuilder::default() } + /// Gets the string interner. #[inline] - pub fn interner(&self) -> &Interner { + pub const fn interner(&self) -> &Interner { &self.interner } @@ -134,7 +136,7 @@ impl Context { /// A helper function for getting an immutable reference to the `console` object. #[cfg(feature = "console")] - pub(crate) fn console(&self) -> &Console { + pub(crate) const fn console(&self) -> &Console { &self.console } @@ -207,7 +209,7 @@ impl Context { /// Return the global object. #[inline] - pub fn global_object(&self) -> &JsObject { + pub const fn global_object(&self) -> &JsObject { self.realm.global_object() } @@ -364,11 +366,8 @@ impl Context { /// #[inline] pub(crate) fn has_property(&mut self, obj: &JsValue, key: &PropertyKey) -> JsResult { - if let Some(obj) = obj.as_object() { - obj.__has_property__(key, self) - } else { - Ok(false) - } + obj.as_object() + .map_or(Ok(false), |obj| obj.__has_property__(key, self)) } /// Register a global class of type `T`, where `T` implements `Class`. @@ -557,7 +556,7 @@ impl Context { /// Return the intrinsic constructors and objects. #[inline] - pub fn intrinsics(&self) -> &Intrinsics { + pub const fn intrinsics(&self) -> &Intrinsics { &self.intrinsics } @@ -569,7 +568,7 @@ impl Context { #[cfg(feature = "intl")] #[inline] /// Get the ICU related utilities - pub(crate) fn icu(&self) -> &icu::Icu { + pub(crate) const fn icu(&self) -> &icu::Icu { &self.icu } @@ -623,6 +622,7 @@ impl ContextBuilder { /// This is useful when you want to initialize an [`Interner`] with /// a collection of words before parsing. #[must_use] + #[allow(clippy::missing_const_for_fn)] pub fn interner(mut self, interner: Interner) -> Self { self.interner = Some(interner); self @@ -642,19 +642,21 @@ impl ContextBuilder { /// This function is only available if the `fuzz` feature is enabled. #[cfg(feature = "fuzz")] #[must_use] - pub fn instructions_remaining(mut self, instructions_remaining: usize) -> Self { + pub const fn instructions_remaining(mut self, instructions_remaining: usize) -> Self { self.instructions_remaining = instructions_remaining; self } /// Creates a new [`ContextBuilder`] with a default empty [`Interner`] /// and a default [`BoaProvider`] if the `intl` feature is enabled. + #[must_use] pub fn new() -> Self { Self::default() } /// Builds a new [`Context`] with the provided parameters, and defaults /// all missing parameters to their default values. + #[must_use] pub fn build(self) -> Context { let intrinsics = Intrinsics::default(); let mut context = Context { diff --git a/boa_engine/src/environments/compile.rs b/boa_engine/src/environments/compile.rs index 1ff6f3faf7..571d010f11 100644 --- a/boa_engine/src/environments/compile.rs +++ b/boa_engine/src/environments/compile.rs @@ -57,7 +57,7 @@ impl CompileTimeEnvironment { /// Check if the environment is a function environment. #[inline] - pub(crate) fn is_function(&self) -> bool { + pub(crate) const fn is_function(&self) -> bool { self.function_scope } @@ -169,13 +169,14 @@ impl CompileTimeEnvironment { .borrow() .initialize_mutable_binding(name, function_scope); } - if let Some(binding) = self.bindings.get(&name) { - BindingLocator::declarative(name, self.environment_index, binding.index) - } else { - outer - .borrow() - .initialize_mutable_binding(name, function_scope) - } + self.bindings.get(&name).map_or_else( + || { + outer + .borrow() + .initialize_mutable_binding(name, function_scope) + }, + |binding| BindingLocator::declarative(name, self.environment_index, binding.index), + ) } else if let Some(binding) = self.bindings.get(&name) { BindingLocator::declarative(name, self.environment_index, binding.index) } else { @@ -203,13 +204,10 @@ impl CompileTimeEnvironment { } Some(binding) if binding.strict => BindingLocator::mutate_immutable(name), Some(_) => BindingLocator::silent(name), - None => { - if let Some(outer) = &self.outer { - outer.borrow().set_mutable_binding_recursive(name) - } else { - BindingLocator::global(name) - } - } + None => self.outer.as_ref().map_or_else( + || BindingLocator::global(name), + |outer| outer.borrow().set_mutable_binding_recursive(name), + ), } } } diff --git a/boa_engine/src/environments/runtime.rs b/boa_engine/src/environments/runtime.rs index c06ab96572..c6744d3869 100644 --- a/boa_engine/src/environments/runtime.rs +++ b/boa_engine/src/environments/runtime.rs @@ -1,12 +1,10 @@ -use std::cell::Cell; - use crate::{ environments::CompileTimeEnvironment, error::JsNativeError, object::JsObject, Context, JsValue, }; -use boa_gc::{Finalize, Gc, GcCell, Trace}; - use boa_ast::expression::Identifier; +use boa_gc::{Finalize, Gc, GcCell, Trace}; use rustc_hash::FxHashSet; +use std::cell::Cell; /// A declarative environment holds binding values at runtime. /// @@ -46,7 +44,7 @@ pub(crate) enum EnvironmentSlots { impl EnvironmentSlots { /// Return the slots if they are part of a function environment. - pub(crate) fn as_function_slots(&self) -> Option<&GcCell> { + pub(crate) const fn as_function_slots(&self) -> Option<&GcCell> { if let Self::Function(env) = &self { Some(env) } else { @@ -74,12 +72,12 @@ pub(crate) struct FunctionSlots { impl FunctionSlots { /// Returns the value of the `[[FunctionObject]]` internal slot. - pub(crate) fn function_object(&self) -> &JsObject { + pub(crate) const fn function_object(&self) -> &JsObject { &self.function_object } /// Returns the value of the `[[NewTarget]]` internal slot. - pub(crate) fn new_target(&self) -> Option<&JsObject> { + pub(crate) const fn new_target(&self) -> Option<&JsObject> { self.new_target.as_ref() } @@ -183,7 +181,7 @@ enum ThisBindingStatus { impl DeclarativeEnvironment { /// Returns the internal slot data of the current environment. - pub(crate) fn slots(&self) -> Option<&EnvironmentSlots> { + pub(crate) const fn slots(&self) -> Option<&EnvironmentSlots> { self.slots.as_ref() } @@ -408,11 +406,7 @@ impl DeclarativeEnvironmentStack { ThisBindingStatus::Uninitialized }; - let this = if let Some(this) = this { - this - } else { - JsValue::Null - }; + let this = this.unwrap_or(JsValue::Null); self.stack.push(Gc::new(DeclarativeEnvironment { bindings: GcCell::new(vec![None; num_bindings]), @@ -786,7 +780,7 @@ pub(crate) struct BindingLocator { impl BindingLocator { /// Creates a new declarative binding locator that has knows indices. #[inline] - pub(in crate::environments) fn declarative( + pub(in crate::environments) const fn declarative( name: Identifier, environment_index: usize, binding_index: usize, @@ -803,7 +797,7 @@ impl BindingLocator { /// Creates a binding locator that indicates that the binding is on the global object. #[inline] - pub(in crate::environments) fn global(name: Identifier) -> Self { + pub(in crate::environments) const fn global(name: Identifier) -> Self { Self { name, environment_index: 0, @@ -817,7 +811,7 @@ impl BindingLocator { /// Creates a binding locator that indicates that it was attempted to mutate an immutable binding. /// At runtime this should always produce a type error. #[inline] - pub(in crate::environments) fn mutate_immutable(name: Identifier) -> Self { + pub(in crate::environments) const fn mutate_immutable(name: Identifier) -> Self { Self { name, environment_index: 0, @@ -830,7 +824,7 @@ impl BindingLocator { /// Creates a binding locator that indicates that any action is silently ignored. #[inline] - pub(in crate::environments) fn silent(name: Identifier) -> Self { + pub(in crate::environments) const fn silent(name: Identifier) -> Self { Self { name, environment_index: 0, @@ -843,31 +837,31 @@ impl BindingLocator { /// Returns the name of the binding. #[inline] - pub(crate) fn name(&self) -> Identifier { + pub(crate) const fn name(&self) -> Identifier { self.name } /// Returns if the binding is located on the global object. #[inline] - pub(crate) fn is_global(&self) -> bool { + pub(crate) const fn is_global(&self) -> bool { self.global } /// Returns the environment index of the binding. #[inline] - pub(crate) fn environment_index(&self) -> usize { + pub(crate) const fn environment_index(&self) -> usize { self.environment_index } /// Returns the binding index of the binding. #[inline] - pub(crate) fn binding_index(&self) -> usize { + pub(crate) const fn binding_index(&self) -> usize { self.binding_index } /// Returns if the binding is a silent operation. #[inline] - pub(crate) fn is_silent(&self) -> bool { + pub(crate) const fn is_silent(&self) -> bool { self.silent } diff --git a/boa_engine/src/error.rs b/boa_engine/src/error.rs index c76bee7f33..3b8127aa63 100644 --- a/boa_engine/src/error.rs +++ b/boa_engine/src/error.rs @@ -66,17 +66,35 @@ enum Repr { /// The error type returned by the [`JsError::try_native`] method. #[derive(Debug, Clone, Error)] pub enum TryNativeError { + /// This error is returned when a property of the error object has an invalid type. #[error("invalid type of property `{0}`")] InvalidPropertyType(&'static str), + + /// This error is returned when the message of the error object could not be decoded. #[error("property `message` cannot contain unpaired surrogates")] InvalidMessageEncoding, + + /// This error is returned when a property of the error object is not accessible. #[error("could not access property `{property}`")] InaccessibleProperty { + /// The name of the property that could not be accessed. property: &'static str, + + /// The source error. source: JsError, }, + + /// This error is returned when any inner error of an aggregate error is not accessible. #[error("could not get element `{index}` of property `errors`")] - InvalidErrorsIndex { index: u64, source: JsError }, + InvalidErrorsIndex { + /// The index of the error that could not be accessed. + index: u64, + + /// The source error. + source: JsError, + }, + + /// This error is returned when the error value not an error object. #[error("opaque error of type `{:?}` is not an Error object", .0.get_type())] NotAnErrorObject(JsValue), } @@ -101,7 +119,8 @@ impl JsError { /// /// assert!(error.as_native().is_some()); /// ``` - pub fn from_native(err: JsNativeError) -> Self { + #[must_use] + pub const fn from_native(err: JsNativeError) -> Self { Self { inner: Repr::Native(err), } @@ -117,7 +136,7 @@ impl JsError { /// /// assert!(error.as_opaque().is_some()); /// ``` - pub fn from_opaque(value: JsValue) -> Self { + pub const fn from_opaque(value: JsValue) -> Self { Self { inner: Repr::Opaque(value), } @@ -246,7 +265,7 @@ impl JsError { } })?; for i in 0..length { - error_list.push(JsError::from_opaque( + error_list.push(Self::from_opaque( errors.get(i, context).map_err(|e| { TryNativeError::InvalidErrorsIndex { index: i, @@ -266,7 +285,7 @@ impl JsError { Ok(JsNativeError { kind, message, - cause: cause.map(|v| Box::new(JsError::from_opaque(v))), + cause: cause.map(|v| Box::new(Self::from_opaque(v))), }) } } @@ -289,7 +308,7 @@ impl JsError { /// /// assert!(error.as_opaque().is_some()); /// ``` - pub fn as_opaque(&self) -> Option<&JsValue> { + pub const fn as_opaque(&self) -> Option<&JsValue> { match self.inner { Repr::Native(_) => None, Repr::Opaque(ref v) => Some(v), @@ -311,7 +330,7 @@ impl JsError { /// /// assert!(error.as_native().is_none()); /// ``` - pub fn as_native(&self) -> Option<&JsNativeError> { + pub const fn as_native(&self) -> Option<&JsNativeError> { match self.inner { Repr::Native(ref e) => Some(e), Repr::Opaque(_) => None, @@ -401,6 +420,7 @@ impl JsNativeError { /// JsNativeErrorKind::Aggregate(ref errors) if errors.len() == 2 /// )); /// ``` + #[must_use] pub fn aggregate(errors: Vec) -> Self { Self::new(JsNativeErrorKind::Aggregate(errors), Box::default(), None) } @@ -415,6 +435,7 @@ impl JsNativeError { /// /// assert!(matches!(error.kind, JsNativeErrorKind::Error)); /// ``` + #[must_use] pub fn error() -> Self { Self::new(JsNativeErrorKind::Error, Box::default(), None) } @@ -429,6 +450,7 @@ impl JsNativeError { /// /// assert!(matches!(error.kind, JsNativeErrorKind::Eval)); /// ``` + #[must_use] pub fn eval() -> Self { Self::new(JsNativeErrorKind::Eval, Box::default(), None) } @@ -443,6 +465,7 @@ impl JsNativeError { /// /// assert!(matches!(error.kind, JsNativeErrorKind::Range)); /// ``` + #[must_use] pub fn range() -> Self { Self::new(JsNativeErrorKind::Range, Box::default(), None) } @@ -457,6 +480,7 @@ impl JsNativeError { /// /// assert!(matches!(error.kind, JsNativeErrorKind::Reference)); /// ``` + #[must_use] pub fn reference() -> Self { Self::new(JsNativeErrorKind::Reference, Box::default(), None) } @@ -471,6 +495,7 @@ impl JsNativeError { /// /// assert!(matches!(error.kind, JsNativeErrorKind::Syntax)); /// ``` + #[must_use] pub fn syntax() -> Self { Self::new(JsNativeErrorKind::Syntax, Box::default(), None) } @@ -485,6 +510,7 @@ impl JsNativeError { /// /// assert!(matches!(error.kind, JsNativeErrorKind::Type)); /// ``` + #[must_use] pub fn typ() -> Self { Self::new(JsNativeErrorKind::Type, Box::default(), None) } @@ -499,6 +525,7 @@ impl JsNativeError { /// /// assert!(matches!(error.kind, JsNativeErrorKind::Uri)); /// ``` + #[must_use] pub fn uri() -> Self { Self::new(JsNativeErrorKind::Uri, Box::default(), None) } @@ -506,6 +533,7 @@ impl JsNativeError { /// Creates a new `JsNativeError` that indicates that the context hit its execution limit. This /// is only used in a fuzzing context. #[cfg(feature = "fuzz")] + #[must_use] pub fn no_instructions_remain() -> Self { Self::new( JsNativeErrorKind::NoInstructionsRemain, @@ -568,7 +596,8 @@ impl JsNativeError { /// /// assert_eq!(error.message(), "number too large"); /// ``` - pub fn message(&self) -> &str { + #[must_use] + pub const fn message(&self) -> &str { &self.message } @@ -588,6 +617,7 @@ impl JsNativeError { /// /// assert!(error.cause().unwrap().as_native().is_some()); /// ``` + #[must_use] pub fn cause(&self) -> Option<&JsError> { self.cause.as_deref() } @@ -773,16 +803,16 @@ pub enum JsNativeErrorKind { impl std::fmt::Display for JsNativeErrorKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - JsNativeErrorKind::Aggregate(_) => "AggregateError", - JsNativeErrorKind::Error => "Error", - JsNativeErrorKind::Eval => "EvalError", - JsNativeErrorKind::Range => "RangeError", - JsNativeErrorKind::Reference => "ReferenceError", - JsNativeErrorKind::Syntax => "SyntaxError", - JsNativeErrorKind::Type => "TypeError", - JsNativeErrorKind::Uri => "UriError", + Self::Aggregate(_) => "AggregateError", + Self::Error => "Error", + Self::Eval => "EvalError", + Self::Range => "RangeError", + Self::Reference => "ReferenceError", + Self::Syntax => "SyntaxError", + Self::Type => "TypeError", + Self::Uri => "UriError", #[cfg(feature = "fuzz")] - JsNativeErrorKind::NoInstructionsRemain => "NoInstructionsRemain", + Self::NoInstructionsRemain => "NoInstructionsRemain", } .fmt(f) } diff --git a/boa_engine/src/job.rs b/boa_engine/src/job.rs index f24be132a3..9561e346ba 100644 --- a/boa_engine/src/job.rs +++ b/boa_engine/src/job.rs @@ -1,3 +1,5 @@ +//! This module contains the data structures for the microtask job queue. + use crate::{prelude::JsObject, Context, JsResult, JsValue}; use boa_gc::{Finalize, Trace}; @@ -38,6 +40,10 @@ impl JobCallback { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-hostcalljobcallback + /// + /// # Panics + /// + /// Panics if the `JobCallback` is not callable. pub fn call_job_callback( &self, v: &JsValue, diff --git a/boa_engine/src/lib.rs b/boa_engine/src/lib.rs index 0a5df84236..884ad65677 100644 --- a/boa_engine/src/lib.rs +++ b/boa_engine/src/lib.rs @@ -15,63 +15,75 @@ html_favicon_url = "https://raw.githubusercontent.com/boa-dev/boa/main/assets/logo.svg" )] #![cfg_attr(not(test), forbid(clippy::unwrap_used))] -#![warn( - clippy::perf, - clippy::single_match_else, - clippy::dbg_macro, - clippy::doc_markdown, - clippy::wildcard_imports, - clippy::struct_excessive_bools, - clippy::doc_markdown, - clippy::semicolon_if_nothing_returned, - clippy::pedantic -)] +#![warn(missing_docs, clippy::dbg_macro)] #![deny( - clippy::all, - clippy::cast_lossless, - clippy::redundant_closure_for_method_calls, - clippy::unnested_or_patterns, - clippy::trivially_copy_pass_by_ref, - clippy::needless_pass_by_value, - clippy::match_wildcard_for_single_variants, - clippy::map_unwrap_or, - unused_qualifications, + // 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 + 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, - unreachable_pub, - trivial_numeric_casts, + unused_qualifications, + unused_tuple_struct_fields, + variant_size_differences, + + // rustdoc lints https://doc.rust-lang.org/rustdoc/lints.html rustdoc::broken_intra_doc_links, - missing_debug_implementations, - missing_copy_implementations, - deprecated_in_future, - meta_variable_misuse, - non_ascii_idents, - rust_2018_compatibility, - rust_2018_idioms, - future_incompatible, - nonstandard_style + 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 categories https://doc.rust-lang.org/clippy/ + clippy::all, + clippy::correctness, + clippy::suspicious, + clippy::style, + clippy::complexity, + clippy::perf, + clippy::pedantic, + clippy::nursery, )] #![allow( - clippy::missing_inline_in_public_items, + // Currently throws a false positive regarding dependencies that are only used in benchmarks. + unused_crate_dependencies, clippy::module_name_repetitions, - clippy::cast_possible_truncation, - clippy::cast_sign_loss, - clippy::cast_precision_loss, - clippy::cast_possible_wrap, - clippy::cast_ptr_alignment, - clippy::missing_panics_doc, + clippy::redundant_pub_crate, clippy::too_many_lines, - clippy::unreadable_literal, clippy::cognitive_complexity, - clippy::must_use_candidate, clippy::missing_errors_doc, - clippy::as_conversions, clippy::let_unit_value, - // TODO deny once false positive is fixed (https://github.com/rust-lang/rust-clippy/issues/9626). - clippy::trait_duplication_in_bounds, - // Ignore because `write!(string, ...)` instead of `string.push_str(&format!(...))` can fail. - // We only use it in `ToInternedString` where performance is not an issue. - clippy::format_push_string, + clippy::option_if_let_else, + // Currently derive macros are linted. Should be fixed in 1.66. See https://github.com/rust-lang/rust-clippy/pull/9454 + clippy::use_self, + + // It may be worth to look if we can fix the issues highlighted by these lints. + clippy::cast_possible_truncation, + clippy::cast_sign_loss, + clippy::cast_precision_loss, + clippy::cast_possible_wrap )] extern crate static_assertions as sa; diff --git a/boa_engine/src/object/builtins/jsarray.rs b/boa_engine/src/object/builtins/jsarray.rs index cd6bfb5855..085b731771 100644 --- a/boa_engine/src/object/builtins/jsarray.rs +++ b/boa_engine/src/object/builtins/jsarray.rs @@ -85,6 +85,7 @@ impl JsArray { Array::pop(&self.inner.clone().into(), &[], context) } + /// Calls `Array.prototype.at()`. #[inline] pub fn at(&self, index: T, context: &mut Context) -> JsResult where @@ -93,22 +94,26 @@ impl JsArray { Array::at(&self.inner.clone().into(), &[index.into().into()], context) } + /// Calls `Array.prototype.shift()`. #[inline] pub fn shift(&self, context: &mut Context) -> JsResult { Array::shift(&self.inner.clone().into(), &[], context) } + /// Calls `Array.prototype.unshift()`. #[inline] pub fn unshift(&self, items: &[JsValue], context: &mut Context) -> JsResult { Array::shift(&self.inner.clone().into(), items, context) } + /// Calls `Array.prototype.reverse()`. #[inline] pub fn reverse(&self, context: &mut Context) -> JsResult { Array::reverse(&self.inner.clone().into(), &[], context)?; Ok(self.clone()) } + /// Calls `Array.prototype.concat()`. #[inline] pub fn concat(&self, items: &[JsValue], context: &mut Context) -> JsResult { let object = Array::concat(&self.inner.clone().into(), items, context)? @@ -119,6 +124,7 @@ impl JsArray { Self::from_object(object) } + /// Calls `Array.prototype.join()`. #[inline] pub fn join(&self, separator: Option, context: &mut Context) -> JsResult { Array::join( @@ -133,6 +139,7 @@ impl JsArray { }) } + /// Calls `Array.prototype.fill()`. #[inline] pub fn fill( &self, @@ -156,6 +163,7 @@ impl JsArray { Ok(self.clone()) } + /// Calls `Array.prototype.indexOf()`. #[inline] pub fn index_of( &self, @@ -182,6 +190,7 @@ impl JsArray { } } + /// Calls `Array.prototype.lastIndexOf()`. #[inline] pub fn last_index_of( &self, @@ -208,6 +217,7 @@ impl JsArray { } } + /// Calls `Array.prototype.find()`. #[inline] pub fn find( &self, @@ -222,6 +232,7 @@ impl JsArray { ) } + /// Calls `Array.prototype.filter()`. #[inline] pub fn filter( &self, @@ -241,6 +252,7 @@ impl JsArray { Self::from_object(object) } + /// Calls `Array.prototype.map()`. #[inline] pub fn map( &self, @@ -260,6 +272,7 @@ impl JsArray { Self::from_object(object) } + /// Calls `Array.prototype.every()`. #[inline] pub fn every( &self, @@ -278,6 +291,7 @@ impl JsArray { Ok(result) } + /// Calls `Array.prototype.some()`. #[inline] pub fn some( &self, @@ -296,6 +310,7 @@ impl JsArray { Ok(result) } + /// Calls `Array.prototype.sort()`. #[inline] pub fn sort(&self, compare_fn: Option, context: &mut Context) -> JsResult { Array::sort( @@ -307,6 +322,7 @@ impl JsArray { Ok(self.clone()) } + /// Calls `Array.prototype.slice()`. #[inline] pub fn slice( &self, @@ -326,6 +342,7 @@ impl JsArray { Self::from_object(object) } + /// Calls `Array.prototype.reduce()`. #[inline] pub fn reduce( &self, @@ -340,6 +357,7 @@ impl JsArray { ) } + /// Calls `Array.prototype.reduceRight()`. #[inline] pub fn reduce_right( &self, diff --git a/boa_engine/src/object/builtins/jsdataview.rs b/boa_engine/src/object/builtins/jsdataview.rs index f70f8120a1..f62e5c678f 100644 --- a/boa_engine/src/object/builtins/jsdataview.rs +++ b/boa_engine/src/object/builtins/jsdataview.rs @@ -34,6 +34,7 @@ pub struct JsDataView { } impl JsDataView { + /// Create a new `JsDataView` object from an existing `JsArrayBuffer`. #[inline] pub fn from_js_array_buffer( array_buffer: &JsArrayBuffer, @@ -105,6 +106,7 @@ impl JsDataView { Ok(Self { inner: obj }) } + /// Create a new `JsDataView` object from an existing object. #[inline] pub fn from_object(object: JsObject) -> JsResult { if object.borrow().is_data_view() { diff --git a/boa_engine/src/object/builtins/jsproxy.rs b/boa_engine/src/object/builtins/jsproxy.rs index 75a23ae185..c37419ac4b 100644 --- a/boa_engine/src/object/builtins/jsproxy.rs +++ b/boa_engine/src/object/builtins/jsproxy.rs @@ -27,6 +27,7 @@ pub struct JsProxy { } impl JsProxy { + /// Creates a new [`JsProxyBuilder`] to easily construct a [`JsProxy`]. pub fn builder(target: JsObject) -> JsProxyBuilder { JsProxyBuilder::new(target) } diff --git a/boa_engine/src/object/builtins/jstypedarray.rs b/boa_engine/src/object/builtins/jstypedarray.rs index 2e971db1fe..b19a5b6415 100644 --- a/boa_engine/src/object/builtins/jstypedarray.rs +++ b/boa_engine/src/object/builtins/jstypedarray.rs @@ -49,6 +49,7 @@ impl JsTypedArray { Ok(self.length(context)? == 0) } + /// Calls `TypedArray.prototype.at()`. #[inline] pub fn at(&self, index: T, context: &mut Context) -> JsResult where @@ -57,6 +58,7 @@ impl JsTypedArray { TypedArray::at(&self.inner, &[index.into().into()], context) } + /// Returns `TypedArray.prototype.byteLength`. #[inline] pub fn byte_length(&self, context: &mut Context) -> JsResult { Ok(TypedArray::byte_length(&self.inner, &[], context)? @@ -65,6 +67,7 @@ impl JsTypedArray { .expect("byteLength should return a number")) } + /// Returns `TypedArray.prototype.byteOffset`. #[inline] pub fn byte_offset(&self, context: &mut Context) -> JsResult { Ok(TypedArray::byte_offset(&self.inner, &[], context)? @@ -73,6 +76,7 @@ impl JsTypedArray { .expect("byteLength should return a number")) } + /// Calls `TypedArray.prototype.fill()`. #[inline] pub fn fill( &self, @@ -96,6 +100,7 @@ impl JsTypedArray { Ok(self.clone()) } + /// Calls `TypedArray.prototype.every()`. pub fn every( &self, predicate: JsFunction, @@ -113,6 +118,7 @@ impl JsTypedArray { Ok(result) } + /// Calls `TypedArray.prototype.some()`. #[inline] pub fn some( &self, @@ -131,6 +137,7 @@ impl JsTypedArray { Ok(result) } + /// Calls `TypedArray.prototype.sort()`. #[inline] pub fn sort(&self, compare_fn: Option, context: &mut Context) -> JsResult { TypedArray::sort(&self.inner, &[compare_fn.into_or_undefined()], context)?; @@ -138,6 +145,7 @@ impl JsTypedArray { Ok(self.clone()) } + /// Calls `TypedArray.prototype.filter()`. #[inline] pub fn filter( &self, @@ -154,6 +162,7 @@ impl JsTypedArray { Ok(Self { inner: object }) } + /// Calls `TypedArray.prototype.map()`. #[inline] pub fn map( &self, @@ -170,6 +179,7 @@ impl JsTypedArray { Ok(Self { inner: object }) } + /// Calls `TypedArray.prototype.reduce()`. #[inline] pub fn reduce( &self, @@ -184,6 +194,7 @@ impl JsTypedArray { ) } + /// Calls `TypedArray.prototype.reduceRight()`. #[inline] pub fn reduce_right( &self, @@ -198,12 +209,14 @@ impl JsTypedArray { ) } + /// Calls `TypedArray.prototype.reverse()`. #[inline] pub fn reverse(&self, context: &mut Context) -> JsResult { TypedArray::reverse(&self.inner, &[], context)?; Ok(self.clone()) } + /// Calls `TypedArray.prototype.slice()`. #[inline] pub fn slice( &self, @@ -220,6 +233,7 @@ impl JsTypedArray { Ok(Self { inner: object }) } + /// Calls `TypedArray.prototype.find()`. #[inline] pub fn find( &self, @@ -234,6 +248,7 @@ impl JsTypedArray { ) } + /// Calls `TypedArray.prototype.indexOf()`. #[inline] pub fn index_of( &self, @@ -260,6 +275,7 @@ impl JsTypedArray { } } + /// Calls `TypedArray.prototype.lastIndexOf()`. #[inline] pub fn last_index_of( &self, @@ -286,6 +302,7 @@ impl JsTypedArray { } } + /// Calls `TypedArray.prototype.join()`. #[inline] pub fn join(&self, separator: Option, context: &mut Context) -> JsResult { TypedArray::join(&self.inner, &[separator.into_or_undefined()], context).map(|x| { @@ -333,6 +350,7 @@ macro_rules! JsTypedArrayType { } impl $name { + /// Create the typed array from a [`JsArrayBuffer`]. #[inline] pub fn from_array_buffer( array_buffer: JsArrayBuffer, @@ -360,6 +378,7 @@ macro_rules! JsTypedArrayType { }) } + /// Create the typed array from an iterator. #[inline] pub fn from_iter(elements: I, context: &mut Context) -> JsResult where diff --git a/boa_engine/src/object/internal_methods/arguments.rs b/boa_engine/src/object/internal_methods/arguments.rs index 23636d6624..abbe446083 100644 --- a/boa_engine/src/object/internal_methods/arguments.rs +++ b/boa_engine/src/object/internal_methods/arguments.rs @@ -141,7 +141,7 @@ pub(crate) fn arguments_exotic_define_own_property( } // ii. If Desc.[[Writable]] is present and its value is false, then - if let Some(false) = desc.writable() { + if desc.writable() == Some(false) { // 1. Call map.[[Delete]](P). map.delete(index); } diff --git a/boa_engine/src/object/internal_methods/mod.rs b/boa_engine/src/object/internal_methods/mod.rs index a2e8c88815..470e9d5d0c 100644 --- a/boa_engine/src/object/internal_methods/mod.rs +++ b/boa_engine/src/object/internal_methods/mod.rs @@ -259,9 +259,9 @@ impl JsObject { pub(crate) fn __construct__( &self, args: &[JsValue], - new_target: &JsObject, + new_target: &Self, context: &mut Context, - ) -> JsResult { + ) -> JsResult { let _timer = Profiler::global().start_event("Object::__construct__", "object"); let func = self.borrow().data.internal_methods.__construct__; func.expect("called `[[Construct]]` for object without a `[[Construct]]` internal method")( diff --git a/boa_engine/src/object/internal_methods/proxy.rs b/boa_engine/src/object/internal_methods/proxy.rs index 514e8b83b7..26630084c4 100644 --- a/boa_engine/src/object/internal_methods/proxy.rs +++ b/boa_engine/src/object/internal_methods/proxy.rs @@ -360,7 +360,7 @@ pub(crate) fn proxy_exotic_get_own_property( match &target_desc { Some(desc) if !desc.expect_configurable() => { // b. If resultDesc has a [[Writable]] field and resultDesc.[[Writable]] is false, then - if let Some(false) = result_desc.writable() { + if result_desc.writable() == Some(false) { // i. If targetDesc.[[Writable]] is true, throw a TypeError exception. if desc.expect_writable() { return diff --git a/boa_engine/src/object/jsobject.rs b/boa_engine/src/object/jsobject.rs index a67a7490ee..bb602661b5 100644 --- a/boa_engine/src/object/jsobject.rs +++ b/boa_engine/src/object/jsobject.rs @@ -43,6 +43,7 @@ impl JsObject { /// Create a new empty `JsObject`, with `prototype` set to `JsValue::Null` /// and `data` set to `ObjectData::ordinary` + #[must_use] pub fn empty() -> Self { Self::from_object(Object::default()) } @@ -503,6 +504,12 @@ impl JsObject { self.borrow().is_native_object() } + /// The abstract operation `ToPropertyDescriptor`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-topropertydescriptor pub fn to_property_descriptor(&self, context: &mut Context) -> JsResult { // 1 is implemented on the method `to_property_descriptor` of value @@ -737,7 +744,7 @@ Cannot both specify accessors and a value or writable attribute", ) } - pub(crate) fn inner(&self) -> &Gc> { + pub(crate) const fn inner(&self) -> &Gc> { &self.inner } } @@ -752,7 +759,7 @@ impl AsRef> for JsObject { impl From>> for JsObject { #[inline] fn from(inner: Gc>) -> Self { - JsObject { inner } + Self { inner } } } @@ -848,6 +855,7 @@ impl RecursionLimiter { /// by the hashset. pub fn new(o: &JsObject) -> Self { // We shouldn't have to worry too much about this being moved during Debug::fmt. + #[allow(trivial_casts)] let ptr = (o.as_ref() as *const _) as usize; let (top_level, visited, live) = Self::SEEN.with(|hm| { let mut hm = hm.borrow_mut(); diff --git a/boa_engine/src/object/mod.rs b/boa_engine/src/object/mod.rs index 61a3d8970a..6c0afb39e0 100644 --- a/boa_engine/src/object/mod.rs +++ b/boa_engine/src/object/mod.rs @@ -88,6 +88,9 @@ pub(crate) trait JsObjectType: /// Static `prototype`, usually set on constructors as a key to point to their respective prototype object. pub static PROTOTYPE: &str = "prototype"; +/// A type alias for an object prototype. +/// +/// A `None` values means that the prototype is the `null` value. pub type JsPrototype = Option; /// This trait allows Rust types to be passed around as objects. @@ -104,12 +107,12 @@ pub trait NativeObject: Debug + Any + Trace { impl NativeObject for T { #[inline] fn as_any(&self) -> &dyn Any { - self as &dyn Any + self } #[inline] fn as_mut_any(&mut self) -> &mut dyn Any { - self as &mut dyn Any + self } } @@ -142,10 +145,18 @@ unsafe impl Trace for Object { /// The representation of private object elements. #[derive(Clone, Debug, Trace, Finalize)] pub enum PrivateElement { + /// A private field. Field(JsValue), + + /// A private method. Method(JsObject), + + /// A private element accessor. Accessor { + /// A getter function. getter: Option, + + /// A setter function. setter: Option, }, } @@ -160,40 +171,109 @@ pub struct ObjectData { /// Defines the different types of objects. #[derive(Debug, Finalize)] pub enum ObjectKind { + /// The `AsyncFromSyncIterator` object kind. AsyncFromSyncIterator(AsyncFromSyncIterator), + + /// The `AsyncGenerator` object kind. AsyncGenerator(AsyncGenerator), + + /// The `AsyncGeneratorFunction` object kind. AsyncGeneratorFunction(Function), + + /// The `Array` object kind. Array, + + /// The `ArrayIterator` object kind. ArrayIterator(ArrayIterator), + + /// The `ArrayBuffer` object kind. ArrayBuffer(ArrayBuffer), + + /// The `Map` object kind. Map(OrderedMap), + + /// The `MapIterator` object kind. MapIterator(MapIterator), + + /// The `RegExp` object kind. RegExp(Box), + + /// The `RegExpStringIterator` object kind. RegExpStringIterator(RegExpStringIterator), + + /// The `BigInt` object kind. BigInt(JsBigInt), + + /// The `Boolean` object kind. Boolean(bool), + + /// The `DataView` object kind. DataView(DataView), + + /// The `ForInIterator` object kind. ForInIterator(ForInIterator), + + /// The `Function` object kind. Function(Function), + + /// The `BoundFunction` object kind. BoundFunction(BoundFunction), + + /// The `Generator` object kind. Generator(Generator), + + /// The `GeneratorFunction` object kind. GeneratorFunction(Function), + + /// The `Set` object kind. Set(OrderedSet), + + /// The `SetIterator` object kind. SetIterator(SetIterator), + + /// The `String` object kind. String(JsString), + + /// The `StringIterator` object kind. StringIterator(StringIterator), + + /// The `Number` object kind. Number(f64), + + /// The `Symbol` object kind. Symbol(JsSymbol), + + /// The `Error` object kind. Error(ErrorKind), + + /// The ordinary object kind. Ordinary, + + /// The `Proxy` object kind. Proxy(Proxy), + + /// The `Date` object kind. Date(Date), + + /// The `Global` object kind. Global, + + /// The arguments exotic object kind. Arguments(Arguments), + + /// The rust native object kind. NativeObject(Box), + + /// The integer-indexed exotic object kind. IntegerIndexed(IntegerIndexed), + + /// The `Promise` object kind. Promise(Promise), + + /// The `WeakRef` object kind. WeakRef(WeakGc>), + + /// The `Intl.DateTimeFormat` object kind. #[cfg(feature = "intl")] DateTimeFormat(Box), } @@ -269,6 +349,7 @@ impl ObjectData { } /// Create the `Array` object data and reference its exclusive internal methods + #[must_use] pub fn array() -> Self { Self { kind: ObjectKind::Array, @@ -293,6 +374,7 @@ impl ObjectData { } /// Create the `Map` object data + #[must_use] pub fn map(map: OrderedMap) -> Self { Self { kind: ObjectKind::Map(map), @@ -309,6 +391,7 @@ impl ObjectData { } /// Create the `RegExp` object data + #[must_use] pub fn reg_exp(reg_exp: Box) -> Self { Self { kind: ObjectKind::RegExp(reg_exp), @@ -325,6 +408,7 @@ impl ObjectData { } /// Create the `BigInt` object data + #[must_use] pub fn big_int(big_int: JsBigInt) -> Self { Self { kind: ObjectKind::BigInt(big_int), @@ -333,6 +417,7 @@ impl ObjectData { } /// Create the `Boolean` object data + #[must_use] pub fn boolean(boolean: bool) -> Self { Self { kind: ObjectKind::Boolean(boolean), @@ -409,6 +494,7 @@ impl ObjectData { } /// Create the `Set` object data + #[must_use] pub fn set(set: OrderedSet) -> Self { Self { kind: ObjectKind::Set(set), @@ -425,6 +511,7 @@ impl ObjectData { } /// Create the `String` object data and reference its exclusive internal methods + #[must_use] pub fn string(string: JsString) -> Self { Self { kind: ObjectKind::String(string), @@ -441,6 +528,7 @@ impl ObjectData { } /// Create the `Number` object data + #[must_use] pub fn number(number: f64) -> Self { Self { kind: ObjectKind::Number(number), @@ -449,6 +537,7 @@ impl ObjectData { } /// Create the `Symbol` object data + #[must_use] pub fn symbol(symbol: JsSymbol) -> Self { Self { kind: ObjectKind::Symbol(symbol), @@ -465,6 +554,7 @@ impl ObjectData { } /// Create the `Ordinary` object data + #[must_use] pub fn ordinary() -> Self { Self { kind: ObjectKind::Ordinary, @@ -487,6 +577,7 @@ impl ObjectData { } /// Create the `Date` object data + #[must_use] pub fn date(date: Date) -> Self { Self { kind: ObjectKind::Date(date), @@ -495,6 +586,7 @@ impl ObjectData { } /// Create the `Global` object data + #[must_use] pub fn global() -> Self { Self { kind: ObjectKind::Global, @@ -523,6 +615,7 @@ impl ObjectData { } /// Create the `NativeObject` object data + #[must_use] pub fn native_object(native_object: Box) -> Self { Self { kind: ObjectKind::NativeObject(native_object), @@ -540,6 +633,7 @@ impl ObjectData { /// Create the `DateTimeFormat` object data #[cfg(feature = "intl")] + #[must_use] pub fn date_time_format(date_time_fmt: Box) -> Self { Self { kind: ObjectKind::DateTimeFormat(date_time_fmt), @@ -615,14 +709,15 @@ impl Default for Object { } impl Object { + /// Returns the kind of the object. #[inline] - pub fn kind(&self) -> &ObjectKind { + pub const fn kind(&self) -> &ObjectKind { &self.data.kind } /// Checks if it's an `AsyncFromSyncIterator` object. #[inline] - pub fn is_async_from_sync_iterator(&self) -> bool { + pub const fn is_async_from_sync_iterator(&self) -> bool { matches!( self.data, ObjectData { @@ -634,7 +729,7 @@ impl Object { /// Returns a reference to the `AsyncFromSyncIterator` data on the object. #[inline] - pub fn as_async_from_sync_iterator(&self) -> Option<&AsyncFromSyncIterator> { + pub const fn as_async_from_sync_iterator(&self) -> Option<&AsyncFromSyncIterator> { match self.data { ObjectData { kind: ObjectKind::AsyncFromSyncIterator(ref async_from_sync_iterator), @@ -646,7 +741,7 @@ impl Object { /// Checks if it's an `AsyncGenerator` object. #[inline] - pub fn is_async_generator(&self) -> bool { + pub const fn is_async_generator(&self) -> bool { matches!( self.data, ObjectData { @@ -658,7 +753,7 @@ impl Object { /// Returns a reference to the async generator data on the object. #[inline] - pub fn as_async_generator(&self) -> Option<&AsyncGenerator> { + pub const fn as_async_generator(&self) -> Option<&AsyncGenerator> { match self.data { ObjectData { kind: ObjectKind::AsyncGenerator(ref async_generator), @@ -680,9 +775,9 @@ impl Object { } } - /// Checks if it an `Array` object. + /// Checks if the object is a `Array` object. #[inline] - pub fn is_array(&self) -> bool { + pub const fn is_array(&self) -> bool { matches!( self.data, ObjectData { @@ -692,37 +787,14 @@ impl Object { ) } - /// Checks if it is an `ArrayIterator` object. - #[inline] - pub fn is_array_iterator(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::ArrayIterator(_), - .. - } - ) - } - #[inline] - pub fn as_array_iterator(&self) -> Option<&ArrayIterator> { - match self.data { - ObjectData { - kind: ObjectKind::ArrayIterator(ref iter), - .. - } => Some(iter), - _ => None, - } - } - - #[inline] - pub(crate) fn has_viewed_array_buffer(&self) -> bool { + pub(crate) const fn has_viewed_array_buffer(&self) -> bool { self.is_typed_array() || self.is_data_view() } - /// Checks if it an `DataView` object. + /// Checks if the object is a `DataView` object. #[inline] - pub fn is_data_view(&self) -> bool { + pub const fn is_data_view(&self) -> bool { matches!( self.data, ObjectData { @@ -732,9 +804,9 @@ impl Object { ) } - /// Checks if it an `ArrayBuffer` object. + /// Checks if the object is a `ArrayBuffer` object. #[inline] - pub fn is_array_buffer(&self) -> bool { + pub const fn is_array_buffer(&self) -> bool { matches!( self.data, ObjectData { @@ -744,8 +816,9 @@ impl Object { ) } + /// Gets the array buffer data if the object is a `ArrayBuffer`. #[inline] - pub fn as_array_buffer(&self) -> Option<&ArrayBuffer> { + pub const fn as_array_buffer(&self) -> Option<&ArrayBuffer> { match &self.data { ObjectData { kind: ObjectKind::ArrayBuffer(buffer), @@ -755,6 +828,7 @@ impl Object { } } + /// Gets the mutable array buffer data if the object is a `ArrayBuffer`. #[inline] pub fn as_array_buffer_mut(&mut self) -> Option<&mut ArrayBuffer> { match &mut self.data { @@ -766,6 +840,31 @@ impl Object { } } + /// Checks if the object is a `ArrayIterator` object. + #[inline] + pub const fn is_array_iterator(&self) -> bool { + matches!( + self.data, + ObjectData { + kind: ObjectKind::ArrayIterator(_), + .. + } + ) + } + + /// Gets the array-iterator data if the object is a `ArrayIterator`. + #[inline] + pub const fn as_array_iterator(&self) -> Option<&ArrayIterator> { + match self.data { + ObjectData { + kind: ObjectKind::ArrayIterator(ref iter), + .. + } => Some(iter), + _ => None, + } + } + + /// Gets the mutable array-iterator data if the object is a `ArrayIterator`. #[inline] pub fn as_array_iterator_mut(&mut self) -> Option<&mut ArrayIterator> { match &mut self.data { @@ -777,6 +876,7 @@ impl Object { } } + /// Gets the mutable string-iterator data if the object is a `StringIterator`. #[inline] pub fn as_string_iterator_mut(&mut self) -> Option<&mut StringIterator> { match &mut self.data { @@ -788,6 +888,7 @@ impl Object { } } + /// Gets the mutable regexp-string-iterator data if the object is a `RegExpStringIterator`. #[inline] pub fn as_regexp_string_iterator_mut(&mut self) -> Option<&mut RegExpStringIterator> { match &mut self.data { @@ -799,8 +900,9 @@ impl Object { } } + /// Gets the for-in-iterator data if the object is a `ForInIterator`. #[inline] - pub fn as_for_in_iterator(&self) -> Option<&ForInIterator> { + pub const fn as_for_in_iterator(&self) -> Option<&ForInIterator> { match &self.data { ObjectData { kind: ObjectKind::ForInIterator(iter), @@ -810,6 +912,7 @@ impl Object { } } + /// Gets the mutable for-in-iterator data if the object is a `ForInIterator`. #[inline] pub fn as_for_in_iterator_mut(&mut self) -> Option<&mut ForInIterator> { match &mut self.data { @@ -821,9 +924,9 @@ impl Object { } } - /// Checks if it is a `Map` object.pub + /// Checks if the object is a `Map` object. #[inline] - pub fn is_map(&self) -> bool { + pub const fn is_map(&self) -> bool { matches!( self.data, ObjectData { @@ -833,8 +936,9 @@ impl Object { ) } + /// Gets the map data if the object is a `Map`. #[inline] - pub fn as_map_ref(&self) -> Option<&OrderedMap> { + pub const fn as_map(&self) -> Option<&OrderedMap> { match self.data { ObjectData { kind: ObjectKind::Map(ref map), @@ -844,6 +948,7 @@ impl Object { } } + /// Gets the mutable map data if the object is a `Map`. #[inline] pub fn as_map_mut(&mut self) -> Option<&mut OrderedMap> { match &mut self.data { @@ -855,8 +960,9 @@ impl Object { } } + /// Checks if the object is a `MapIterator` object. #[inline] - pub fn is_map_iterator(&self) -> bool { + pub const fn is_map_iterator(&self) -> bool { matches!( self.data, ObjectData { @@ -866,8 +972,9 @@ impl Object { ) } + /// Gets the map iterator data if the object is a `MapIterator`. #[inline] - pub fn as_map_iterator_ref(&self) -> Option<&MapIterator> { + pub const fn as_map_iterator_ref(&self) -> Option<&MapIterator> { match &self.data { ObjectData { kind: ObjectKind::MapIterator(iter), @@ -877,6 +984,7 @@ impl Object { } } + /// Gets the mutable map iterator data if the object is a `MapIterator`. #[inline] pub fn as_map_iterator_mut(&mut self) -> Option<&mut MapIterator> { match &mut self.data { @@ -888,8 +996,9 @@ impl Object { } } + /// Checks if the object is a `Set` object. #[inline] - pub fn is_set(&self) -> bool { + pub const fn is_set(&self) -> bool { matches!( self.data, ObjectData { @@ -899,20 +1008,9 @@ impl Object { ) } - /// Checks if it is an `SetIterator` object. - #[inline] - pub fn is_set_iterator(&self) -> bool { - matches!( - self.data, - ObjectData { - kind: ObjectKind::SetIterator(_), - .. - } - ) - } - + /// Gets the set data if the object is a `Set`. #[inline] - pub fn as_set_ref(&self) -> Option<&OrderedSet> { + pub const fn as_set(&self) -> Option<&OrderedSet> { match self.data { ObjectData { kind: ObjectKind::Set(ref set), @@ -922,6 +1020,7 @@ impl Object { } } + /// Gets the mutable set data if the object is a `Set`. #[inline] pub fn as_set_mut(&mut self) -> Option<&mut OrderedSet> { match &mut self.data { @@ -933,6 +1032,19 @@ impl Object { } } + /// Checks if the object is a `SetIterator` object. + #[inline] + pub const fn is_set_iterator(&self) -> bool { + matches!( + self.data, + ObjectData { + kind: ObjectKind::SetIterator(_), + .. + } + ) + } + + /// Gets the mutable set iterator data if the object is a `SetIterator`. #[inline] pub fn as_set_iterator_mut(&mut self) -> Option<&mut SetIterator> { match &mut self.data { @@ -944,9 +1056,9 @@ impl Object { } } - /// Checks if it a `String` object. + /// Checks if the object is a `String` object. #[inline] - pub fn is_string(&self) -> bool { + pub const fn is_string(&self) -> bool { matches!( self.data, ObjectData { @@ -956,6 +1068,7 @@ impl Object { ) } + /// Gets the string data if the object is a `String`. #[inline] pub fn as_string(&self) -> Option { match self.data { @@ -967,9 +1080,9 @@ impl Object { } } - /// Checks if it a `Function` object. + /// Checks if the object is a `Function` object. #[inline] - pub fn is_function(&self) -> bool { + pub const fn is_function(&self) -> bool { matches!( self.data, ObjectData { @@ -979,8 +1092,9 @@ impl Object { ) } + /// Gets the function data if the object is a `Function`. #[inline] - pub fn as_function(&self) -> Option<&Function> { + pub const fn as_function(&self) -> Option<&Function> { match self.data { ObjectData { kind: @@ -991,6 +1105,7 @@ impl Object { } } + /// Gets the mutable function data if the object is a `Function`. #[inline] pub fn as_function_mut(&mut self) -> Option<&mut Function> { match self.data { @@ -1004,8 +1119,9 @@ impl Object { } } + /// Gets the bound function data if the object is a `BoundFunction`. #[inline] - pub fn as_bound_function(&self) -> Option<&BoundFunction> { + pub const fn as_bound_function(&self) -> Option<&BoundFunction> { match self.data { ObjectData { kind: ObjectKind::BoundFunction(ref bound_function), @@ -1015,9 +1131,9 @@ impl Object { } } - /// Checks if it's a `Generator` object. + /// Checks if the object is a `Generator` object. #[inline] - pub fn is_generator(&self) -> bool { + pub const fn is_generator(&self) -> bool { matches!( self.data, ObjectData { @@ -1027,9 +1143,9 @@ impl Object { ) } - /// Returns a reference to the generator data on the object. + /// Gets the generator data if the object is a `Generator`. #[inline] - pub fn as_generator(&self) -> Option<&Generator> { + pub const fn as_generator(&self) -> Option<&Generator> { match self.data { ObjectData { kind: ObjectKind::Generator(ref generator), @@ -1039,7 +1155,7 @@ impl Object { } } - /// Returns a mutable reference to the generator data on the object. + /// Gets the mutable generator data if the object is a `Generator`. #[inline] pub fn as_generator_mut(&mut self) -> Option<&mut Generator> { match self.data { @@ -1051,9 +1167,9 @@ impl Object { } } - /// Checks if it a Symbol object. + /// Checks if the object is a `Symbol` object. #[inline] - pub fn is_symbol(&self) -> bool { + pub const fn is_symbol(&self) -> bool { matches!( self.data, ObjectData { @@ -1063,6 +1179,7 @@ impl Object { ) } + /// Gets the error data if the object is a `Symbol`. #[inline] pub fn as_symbol(&self) -> Option { match self.data { @@ -1074,9 +1191,9 @@ impl Object { } } - /// Checks if it an Error object. + /// Checks if the object is a `Error` object. #[inline] - pub fn is_error(&self) -> bool { + pub const fn is_error(&self) -> bool { matches!( self.data, ObjectData { @@ -1086,8 +1203,9 @@ impl Object { ) } + /// Gets the error data if the object is a `Error`. #[inline] - pub fn as_error(&self) -> Option { + pub const fn as_error(&self) -> Option { match self.data { ObjectData { kind: ObjectKind::Error(e), @@ -1097,9 +1215,9 @@ impl Object { } } - /// Checks if it a Boolean object. + /// Checks if the object is a `Boolean` object. #[inline] - pub fn is_boolean(&self) -> bool { + pub const fn is_boolean(&self) -> bool { matches!( self.data, ObjectData { @@ -1109,8 +1227,9 @@ impl Object { ) } + /// Gets the boolean data if the object is a `Boolean`. #[inline] - pub fn as_boolean(&self) -> Option { + pub const fn as_boolean(&self) -> Option { match self.data { ObjectData { kind: ObjectKind::Boolean(boolean), @@ -1120,9 +1239,9 @@ impl Object { } } - /// Checks if it a `Number` object. + /// Checks if the object is a `Number` object. #[inline] - pub fn is_number(&self) -> bool { + pub const fn is_number(&self) -> bool { matches!( self.data, ObjectData { @@ -1132,8 +1251,9 @@ impl Object { ) } + /// Gets the number data if the object is a `Number`. #[inline] - pub fn as_number(&self) -> Option { + pub const fn as_number(&self) -> Option { match self.data { ObjectData { kind: ObjectKind::Number(number), @@ -1143,9 +1263,9 @@ impl Object { } } - /// Checks if it a `BigInt` object. + /// Checks if the object is a `BigInt` object. #[inline] - pub fn is_bigint(&self) -> bool { + pub const fn is_bigint(&self) -> bool { matches!( self.data, ObjectData { @@ -1155,8 +1275,9 @@ impl Object { ) } + /// Gets the bigint data if the object is a `BigInt`. #[inline] - pub fn as_bigint(&self) -> Option<&JsBigInt> { + pub const fn as_bigint(&self) -> Option<&JsBigInt> { match self.data { ObjectData { kind: ObjectKind::BigInt(ref bigint), @@ -1166,8 +1287,9 @@ impl Object { } } + /// Checks if the object is a `Date` object. #[inline] - pub fn is_date(&self) -> bool { + pub const fn is_date(&self) -> bool { matches!( self.data, ObjectData { @@ -1177,8 +1299,9 @@ impl Object { ) } + /// Gets the date data if the object is a `Date`. #[inline] - pub fn as_date(&self) -> Option<&Date> { + pub const fn as_date(&self) -> Option<&Date> { match self.data { ObjectData { kind: ObjectKind::Date(ref date), @@ -1188,6 +1311,7 @@ impl Object { } } + /// Gets the mutable date data if the object is a `Date`. #[inline] pub fn as_date_mut(&mut self) -> Option<&mut Date> { match self.data { @@ -1201,7 +1325,7 @@ impl Object { /// Checks if it a `RegExp` object. #[inline] - pub fn is_regexp(&self) -> bool { + pub const fn is_regexp(&self) -> bool { matches!( self.data, ObjectData { @@ -1213,7 +1337,7 @@ impl Object { /// Gets the regexp data if the object is a regexp. #[inline] - pub fn as_regexp(&self) -> Option<&RegExp> { + pub const fn as_regexp(&self) -> Option<&RegExp> { match self.data { ObjectData { kind: ObjectKind::RegExp(ref regexp), @@ -1225,7 +1349,7 @@ impl Object { /// Checks if it a `TypedArray` object. #[inline] - pub fn is_typed_array(&self) -> bool { + pub const fn is_typed_array(&self) -> bool { matches!( self.data, ObjectData { @@ -1235,8 +1359,9 @@ impl Object { ) } + /// Gets the data view data if the object is a `DataView`. #[inline] - pub fn as_data_view(&self) -> Option<&DataView> { + pub const fn as_data_view(&self) -> Option<&DataView> { match &self.data { ObjectData { kind: ObjectKind::DataView(data_view), @@ -1246,6 +1371,7 @@ impl Object { } } + /// Gets the mutable data view data if the object is a `DataView`. #[inline] pub fn as_data_view_mut(&mut self) -> Option<&mut DataView> { match &mut self.data { @@ -1259,7 +1385,7 @@ impl Object { /// Checks if it is an `Arguments` object. #[inline] - pub fn is_arguments(&self) -> bool { + pub const fn is_arguments(&self) -> bool { matches!( self.data, ObjectData { @@ -1271,7 +1397,7 @@ impl Object { /// Gets the mapped arguments data if this is a mapped arguments object. #[inline] - pub fn as_mapped_arguments(&self) -> Option<&ParameterMap> { + pub const fn as_mapped_arguments(&self) -> Option<&ParameterMap> { match self.data { ObjectData { kind: ObjectKind::Arguments(Arguments::Mapped(ref args)), @@ -1295,7 +1421,7 @@ impl Object { /// Gets the typed array data (integer indexed object) if this is a typed array. #[inline] - pub fn as_typed_array(&self) -> Option<&IntegerIndexed> { + pub const fn as_typed_array(&self) -> Option<&IntegerIndexed> { match self.data { ObjectData { kind: ObjectKind::IntegerIndexed(ref integer_indexed_object), @@ -1319,7 +1445,7 @@ impl Object { /// Checks if it an ordinary object. #[inline] - pub fn is_ordinary(&self) -> bool { + pub const fn is_ordinary(&self) -> bool { matches!( self.data, ObjectData { @@ -1331,7 +1457,7 @@ impl Object { /// Checks if it's an proxy object. #[inline] - pub fn is_proxy(&self) -> bool { + pub const fn is_proxy(&self) -> bool { matches!( self.data, ObjectData { @@ -1341,8 +1467,9 @@ impl Object { ) } + /// Gets the proxy data if the object is a `Proxy`. #[inline] - pub fn as_proxy(&self) -> Option<&Proxy> { + pub const fn as_proxy(&self) -> Option<&Proxy> { match self.data { ObjectData { kind: ObjectKind::Proxy(ref proxy), @@ -1352,6 +1479,7 @@ impl Object { } } + /// Gets the mutable proxy data if the object is a `Proxy`. #[inline] pub fn as_proxy_mut(&mut self) -> Option<&mut Proxy> { match self.data { @@ -1365,7 +1493,7 @@ impl Object { /// Gets the prototype instance of this object. #[inline] - pub fn prototype(&self) -> &JsPrototype { + pub const fn prototype(&self) -> &JsPrototype { &self.prototype } @@ -1390,7 +1518,7 @@ impl Object { /// Returns `true` if it holds an Rust type that implements `NativeObject`. #[inline] - pub fn is_native_object(&self) -> bool { + pub const fn is_native_object(&self) -> bool { matches!( self.data, ObjectData { @@ -1400,6 +1528,7 @@ impl Object { ) } + /// Gets the native object data if the object is a `NativeObject`. #[inline] pub fn as_native_object(&self) -> Option<&dyn NativeObject> { match self.data { @@ -1413,7 +1542,7 @@ impl Object { /// Checks if it is a `Promise` object. #[inline] - pub fn is_promise(&self) -> bool { + pub const fn is_promise(&self) -> bool { matches!( self.data, ObjectData { @@ -1423,9 +1552,9 @@ impl Object { ) } - /// Gets the promise data if the object is a promise. + /// Gets the promise data if the object is a `Promise`. #[inline] - pub fn as_promise(&self) -> Option<&Promise> { + pub const fn as_promise(&self) -> Option<&Promise> { match self.data { ObjectData { kind: ObjectKind::Promise(ref promise), @@ -1435,6 +1564,7 @@ impl Object { } } + /// Gets the mutable promise data if the object is a `Promise`. #[inline] pub fn as_promise_mut(&mut self) -> Option<&mut Promise> { match self.data { @@ -1448,7 +1578,7 @@ impl Object { /// Gets the `WeakRef`data if the object is a `WeakRef`. #[inline] - pub fn as_weak_ref(&self) -> Option<&WeakGc>> { + pub const fn as_weak_ref(&self) -> Option<&WeakGc>> { match self.data { ObjectData { kind: ObjectKind::WeakRef(ref weak_ref), @@ -1505,8 +1635,9 @@ impl Object { } } + /// Returns the properties of the object. #[inline] - pub fn properties(&self) -> &PropertyMap { + pub const fn properties(&self) -> &PropertyMap { &self.properties } @@ -1755,7 +1886,7 @@ impl<'context> FunctionBuilder<'context> { /// The default is `0`. #[inline] #[must_use] - pub fn length(mut self, length: usize) -> Self { + pub const fn length(mut self, length: usize) -> Self { self.length = length; self } diff --git a/boa_engine/src/object/operations.rs b/boa_engine/src/object/operations.rs index 8d340fda8c..cced664067 100644 --- a/boa_engine/src/object/operations.rs +++ b/boa_engine/src/object/operations.rs @@ -32,12 +32,14 @@ pub enum IntegrityLevel { impl IntegrityLevel { /// Returns `true` if the integrity level is sealed. - pub fn is_sealed(&self) -> bool { + #[must_use] + pub const fn is_sealed(&self) -> bool { matches!(self, Self::Sealed) } /// Returns `true` if the integrity level is frozen. - pub fn is_frozen(&self) -> bool { + #[must_use] + pub const fn is_frozen(&self) -> bool { matches!(self, Self::Frozen) } } @@ -335,9 +337,9 @@ impl JsObject { pub fn construct( &self, args: &[JsValue], - new_target: Option<&JsObject>, + new_target: Option<&Self>, context: &mut Context, - ) -> JsResult { + ) -> JsResult { // 1. If newTarget is not present, set newTarget to F. let new_target = new_target.unwrap_or(self); // 2. If argumentsList is not present, set argumentsList to a new empty List. @@ -515,13 +517,14 @@ impl JsObject { } // 7. If IsConstructor(S) is true, return S. - // 8. Throw a TypeError exception. - match s.as_object() { - Some(obj) if obj.is_constructor() => Ok(obj.clone()), - _ => Err(JsNativeError::typ() - .with_message("property 'constructor' is not a constructor") - .into()), + if let Some(s) = s.as_constructor() { + return Ok(s.clone()); } + + // 8. Throw a TypeError exception. + Err(JsNativeError::typ() + .with_message("property 'constructor' is not a constructor") + .into()) } /// It is used to iterate over names of object's keys. diff --git a/boa_engine/src/object/property_map.rs b/boa_engine/src/object/property_map.rs index c06bb1e412..63f26667c8 100644 --- a/boa_engine/src/object/property_map.rs +++ b/boa_engine/src/object/property_map.rs @@ -221,20 +221,31 @@ impl IndexedProperties { } } +/// A [`PropertyMap`] contains all the properties of an object. +/// +/// The property values are stored in different data structures based on keys. #[derive(Default, Debug, Trace, Finalize)] pub struct PropertyMap { + /// Properties stored with integers as keys. indexed_properties: IndexedProperties, - /// Properties + + /// Properties stored with `String`s a keys. string_properties: OrderedHashMap, - /// Symbol Properties + + /// Properties stored with `Symbol`s a keys. symbol_properties: OrderedHashMap, } impl PropertyMap { + /// Create a new [`PropertyMap`]. + #[must_use] + #[inline] pub fn new() -> Self { Self::default() } + /// Get the property with the given key from the [`PropertyMap`]. + #[must_use] pub fn get(&self, key: &PropertyKey) -> Option { match key { PropertyKey::Index(index) => self.indexed_properties.get(*index), @@ -243,6 +254,7 @@ impl PropertyMap { } } + /// Insert the given property descriptor with the given key [`PropertyMap`]. pub fn insert( &mut self, key: &PropertyKey, @@ -259,6 +271,7 @@ impl PropertyMap { } } + /// Remove the property with the given key from the [`PropertyMap`]. pub fn remove(&mut self, key: &PropertyKey) -> Option { match key { PropertyKey::Index(index) => self.indexed_properties.remove(*index), @@ -273,7 +286,7 @@ impl PropertyMap { } /// Returns the vec of dense indexed properties if they exist. - pub(crate) fn dense_indexed_properties(&self) -> Option<&Vec> { + pub(crate) const fn dense_indexed_properties(&self) -> Option<&Vec> { if let IndexedProperties::Dense(properties) = &self.indexed_properties { Some(properties) } else { @@ -285,6 +298,7 @@ impl PropertyMap { /// /// This iterator does not recurse down the prototype chain. #[inline] + #[must_use] pub fn iter(&self) -> Iter<'_> { Iter { indexed_properties: self.indexed_properties.iter(), @@ -297,6 +311,7 @@ impl PropertyMap { /// /// This iterator does not recurse down the prototype chain. #[inline] + #[must_use] pub fn keys(&self) -> Keys<'_> { Keys(self.iter()) } @@ -305,6 +320,7 @@ impl PropertyMap { /// /// This iterator does not recurse down the prototype chain. #[inline] + #[must_use] pub fn values(&self) -> Values<'_> { Values(self.iter()) } @@ -314,6 +330,7 @@ impl PropertyMap { /// /// This iterator does not recurse down the prototype chain. #[inline] + #[must_use] pub fn symbol_properties(&self) -> SymbolProperties<'_> { SymbolProperties(self.symbol_properties.0.iter()) } @@ -322,6 +339,7 @@ impl PropertyMap { /// /// This iterator does not recurse down the prototype chain. #[inline] + #[must_use] pub fn symbol_property_keys(&self) -> SymbolPropertyKeys<'_> { SymbolPropertyKeys(self.symbol_properties.0.keys()) } @@ -330,6 +348,7 @@ impl PropertyMap { /// /// This iterator does not recurse down the prototype chain. #[inline] + #[must_use] pub fn symbol_property_values(&self) -> SymbolPropertyValues<'_> { SymbolPropertyValues(self.symbol_properties.0.values()) } @@ -338,6 +357,7 @@ impl PropertyMap { /// /// This iterator does not recurse down the prototype chain. #[inline] + #[must_use] pub fn index_properties(&self) -> IndexProperties<'_> { self.indexed_properties.iter() } @@ -346,6 +366,7 @@ impl PropertyMap { /// /// This iterator does not recurse down the prototype chain. #[inline] + #[must_use] pub fn index_property_keys(&self) -> IndexPropertyKeys<'_> { self.indexed_properties.keys() } @@ -354,6 +375,7 @@ impl PropertyMap { /// /// This iterator does not recurse down the prototype chain. #[inline] + #[must_use] pub fn index_property_values(&self) -> IndexPropertyValues<'_> { self.indexed_properties.values() } @@ -362,6 +384,7 @@ impl PropertyMap { /// /// This iterator does not recurse down the prototype chain. #[inline] + #[must_use] pub fn string_properties(&self) -> StringProperties<'_> { StringProperties(self.string_properties.0.iter()) } @@ -370,6 +393,7 @@ impl PropertyMap { /// /// This iterator does not recurse down the prototype chain. #[inline] + #[must_use] pub fn string_property_keys(&self) -> StringPropertyKeys<'_> { StringPropertyKeys(self.string_properties.0.keys()) } @@ -378,11 +402,14 @@ impl PropertyMap { /// /// This iterator does not recurse down the prototype chain. #[inline] + #[must_use] pub fn string_property_values(&self) -> StringPropertyValues<'_> { StringPropertyValues(self.string_properties.0.values()) } + /// Returns `true` if the given key is contained in the [`PropertyMap`]. #[inline] + #[must_use] pub fn contains_key(&self, key: &PropertyKey) -> bool { match key { PropertyKey::Index(index) => self.indexed_properties.contains_key(*index), @@ -392,7 +419,7 @@ impl PropertyMap { } #[inline] - pub(crate) fn string_property_map(&self) -> &GlobalPropertyMap { + pub(crate) const fn string_property_map(&self) -> &GlobalPropertyMap { &self.string_properties.0 } @@ -410,7 +437,7 @@ pub struct Iter<'a> { symbol_properties: indexmap::map::Iter<'a, JsSymbol, PropertyDescriptor>, } -impl<'a> Iterator for Iter<'a> { +impl Iterator for Iter<'_> { type Item = (PropertyKey, PropertyDescriptor); fn next(&mut self) -> Option { if let Some((key, value)) = self.indexed_properties.next() { @@ -437,7 +464,7 @@ impl FusedIterator for Iter<'_> {} #[derive(Debug, Clone)] pub struct Keys<'a>(Iter<'a>); -impl<'a> Iterator for Keys<'a> { +impl Iterator for Keys<'_> { type Item = PropertyKey; fn next(&mut self) -> Option { let (key, _) = self.0.next()?; @@ -458,7 +485,7 @@ impl FusedIterator for Keys<'_> {} #[derive(Debug, Clone)] pub struct Values<'a>(Iter<'a>); -impl<'a> Iterator for Values<'a> { +impl Iterator for Values<'_> { type Item = PropertyDescriptor; fn next(&mut self) -> Option { let (_, value) = self.0.next()?; @@ -556,14 +583,17 @@ impl ExactSizeIterator for SymbolPropertyValues<'_> { impl FusedIterator for SymbolPropertyValues<'_> {} -/// An iterator over the indexed property entries of an `Object` +/// An iterator over the indexed property entries of an `Object`. #[derive(Debug, Clone)] pub enum IndexProperties<'a> { + /// An iterator over dense, Vec backed indexed property entries of an `Object`. Dense(std::iter::Enumerate>), + + /// An iterator over sparse, HashMap backed indexed property entries of an `Object`. Sparse(hash_map::Iter<'a, u32, PropertyDescriptor>), } -impl<'a> Iterator for IndexProperties<'a> { +impl Iterator for IndexProperties<'_> { type Item = (u32, PropertyDescriptor); #[inline] @@ -608,11 +638,14 @@ impl FusedIterator for IndexProperties<'_> {} /// An iterator over the index keys (`u32`) of an `Object`. #[derive(Debug, Clone)] pub enum IndexPropertyKeys<'a> { + /// An iterator over dense, Vec backed indexed property entries of an `Object`. Dense(std::ops::Range), + + /// An iterator over sparse, HashMap backed indexed property entries of an `Object`. Sparse(hash_map::Keys<'a, u32, PropertyDescriptor>), } -impl<'a> Iterator for IndexPropertyKeys<'a> { +impl Iterator for IndexPropertyKeys<'_> { type Item = u32; #[inline] @@ -647,11 +680,14 @@ impl FusedIterator for IndexPropertyKeys<'_> {} /// An iterator over the index values (`Property`) of an `Object`. #[derive(Debug, Clone)] pub enum IndexPropertyValues<'a> { + /// An iterator over dense, Vec backed indexed property entries of an `Object`. Dense(std::slice::Iter<'a, JsValue>), + + /// An iterator over sparse, HashMap backed indexed property entries of an `Object`. Sparse(hash_map::Values<'a, u32, PropertyDescriptor>), } -impl<'a> Iterator for IndexPropertyValues<'a> { +impl Iterator for IndexPropertyValues<'_> { type Item = PropertyDescriptor; #[inline] diff --git a/boa_engine/src/property/attribute/mod.rs b/boa_engine/src/property/attribute/mod.rs index eb64056c76..426762ab73 100644 --- a/boa_engine/src/property/attribute/mod.rs +++ b/boa_engine/src/property/attribute/mod.rs @@ -55,7 +55,8 @@ impl Attribute { /// Gets the `writable` flag. #[inline] - pub fn writable(self) -> bool { + #[must_use] + pub const fn writable(self) -> bool { self.contains(Self::WRITABLE) } @@ -71,7 +72,8 @@ impl Attribute { /// Gets the `enumerable` flag. #[inline] - pub fn enumerable(self) -> bool { + #[must_use] + pub const fn enumerable(self) -> bool { self.contains(Self::ENUMERABLE) } @@ -87,7 +89,8 @@ impl Attribute { /// Gets the `configurable` flag. #[inline] - pub fn configurable(self) -> bool { + #[must_use] + pub const fn configurable(self) -> bool { self.contains(Self::CONFIGURABLE) } } diff --git a/boa_engine/src/property/mod.rs b/boa_engine/src/property/mod.rs index d6f313e99c..caa46425ea 100644 --- a/boa_engine/src/property/mod.rs +++ b/boa_engine/src/property/mod.rs @@ -15,11 +15,12 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty //! [section]: https://tc39.es/ecma262/#sec-property-attributes +mod attribute; + use crate::{js_string, JsString, JsSymbol, JsValue}; use boa_gc::{Finalize, Trace}; use std::fmt; -mod attribute; pub use attribute::Attribute; /// This represents a JavaScript Property AKA The Property Descriptor. @@ -51,16 +52,28 @@ pub struct PropertyDescriptor { kind: DescriptorKind, } +/// `DescriptorKind` represents the different kinds of property descriptors. #[derive(Debug, Clone, Trace, Finalize)] pub enum DescriptorKind { + /// A data property descriptor. Data { + /// The value of the property. value: Option, + + /// Whether the property is writable. writable: Option, }, + + /// An accessor property descriptor. Accessor { + /// The getter of the property. get: Option, + + /// The setter of the property. set: Option, }, + + /// A generic property descriptor. Generic, } @@ -71,150 +84,179 @@ impl Default for DescriptorKind { } impl PropertyDescriptor { - /// An accessor Property Descriptor is one that includes any fields named either `[[Get]]` or `[[Set]]`. + /// An accessor property descriptor is one that includes any fields named either `[[Get]]` or `[[Set]]`. /// /// More information: /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-isaccessordescriptor #[inline] - pub fn is_accessor_descriptor(&self) -> bool { + pub const fn is_accessor_descriptor(&self) -> bool { matches!(self.kind, DescriptorKind::Accessor { .. }) } - /// A data Property Descriptor is one that includes any fields named either `[[Value]]` or `[[Writable]]`. + /// A data property descriptor is one that includes any fields named either `[[Value]]` or `[[Writable]]`. /// /// More information: /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-isdatadescriptor #[inline] - pub fn is_data_descriptor(&self) -> bool { + pub const fn is_data_descriptor(&self) -> bool { matches!(self.kind, DescriptorKind::Data { .. }) } - /// A generic Property Descriptor is one that is neither a data descriptor nor an accessor descriptor. + /// A generic property descriptor is one that is neither a data descriptor nor an accessor descriptor. /// /// More information: /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-isgenericdescriptor #[inline] - pub fn is_generic_descriptor(&self) -> bool { + pub const fn is_generic_descriptor(&self) -> bool { matches!(self.kind, DescriptorKind::Generic) } + /// Returns if the property descriptor is empty. #[inline] - pub fn is_empty(&self) -> bool { + pub const fn is_empty(&self) -> bool { self.is_generic_descriptor() && self.enumerable.is_none() && self.configurable.is_none() } + /// Returns if the property descriptor is enumerable. + /// Returns `None` if the `enumerable` field is not set. #[inline] - pub fn enumerable(&self) -> Option { + pub const fn enumerable(&self) -> Option { self.enumerable } + /// Returns if the property descriptor is configurable. + /// Returns `None` if the `configurable` field is not set. #[inline] - pub fn configurable(&self) -> Option { + pub const fn configurable(&self) -> Option { self.configurable } + /// Returns if the property descriptor is writable. + /// Returns `None` if the `writable` field is not set or the property descriptor is not a data descriptor. #[inline] - pub fn writable(&self) -> Option { + pub const fn writable(&self) -> Option { match self.kind { DescriptorKind::Data { writable, .. } => writable, _ => None, } } + /// Returns the value of the property descriptor. + /// Returns `None` if the value is not set or the property descriptor is not a data descriptor. #[inline] - pub fn value(&self) -> Option<&JsValue> { + pub const fn value(&self) -> Option<&JsValue> { match &self.kind { DescriptorKind::Data { value, .. } => value.as_ref(), _ => None, } } + /// Returns the getter of the property descriptor. + /// Returns `None` if the getter is not set or the property descriptor is not an accessor descriptor. #[inline] - pub fn get(&self) -> Option<&JsValue> { + pub const fn get(&self) -> Option<&JsValue> { match &self.kind { DescriptorKind::Accessor { get, .. } => get.as_ref(), _ => None, } } + /// Returns the setter of the property descriptor. + /// Returns `None` if the setter is not set or the property descriptor is not an accessor descriptor. #[inline] - pub fn set(&self) -> Option<&JsValue> { + pub const fn set(&self) -> Option<&JsValue> { match &self.kind { DescriptorKind::Accessor { set, .. } => set.as_ref(), _ => None, } } + /// Returns if the property descriptor is enumerable. + /// + /// # Panics + /// + /// Panics if the `enumerable` field is not set. #[inline] pub fn expect_enumerable(&self) -> bool { - if let Some(enumerable) = self.enumerable { - enumerable - } else { - panic!("[[enumerable]] field not in property descriptor") - } + self.enumerable + .expect("[[enumerable]] field not in property descriptor") } + /// Returns if the property descriptor is configurable. + /// + /// # Panics + /// + /// Panics if the `configurable` field is not set. #[inline] pub fn expect_configurable(&self) -> bool { - if let Some(configurable) = self.configurable { - configurable - } else { - panic!("[[configurable]] field not in property descriptor") - } + self.configurable + .expect("[[configurable]] field not in property descriptor") } + /// Returns if the property descriptor is writable. + /// + /// # Panics + /// + /// Panics if the `writable` field is not set. #[inline] pub fn expect_writable(&self) -> bool { - if let Some(writable) = self.writable() { - writable - } else { - panic!("[[writable]] field not in property descriptor") - } + self.writable() + .expect("[[writable]] field not in property descriptor") } + /// Returns the value of the property descriptor. + /// + /// # Panics + /// + /// Panics if the `value` field is not set. #[inline] pub fn expect_value(&self) -> &JsValue { - if let Some(value) = self.value() { - value - } else { - panic!("[[value]] field not in property descriptor") - } + self.value() + .expect("[[value]] field not in property descriptor") } + /// Returns the getter of the property descriptor. + /// + /// # Panics + /// + /// Panics if the `getter` field is not set. #[inline] pub fn expect_get(&self) -> &JsValue { - if let Some(get) = self.get() { - get - } else { - panic!("[[get]] field not in property descriptor") - } + self.get() + .expect("[[get]] field not in property descriptor") } + /// Returns the setter of the property descriptor. + /// + /// # Panics + /// + /// Panics if the `setter` field is not set. #[inline] pub fn expect_set(&self) -> &JsValue { - if let Some(set) = self.set() { - set - } else { - panic!("[[set]] field not in property descriptor") - } + self.set() + .expect("[[set]] field not in property descriptor") } + /// Returns the kind of the property descriptor. #[inline] - pub fn kind(&self) -> &DescriptorKind { + pub const fn kind(&self) -> &DescriptorKind { &self.kind } + /// Creates a new [`PropertyDescriptorBuilder`]. #[inline] + #[must_use] pub fn builder() -> PropertyDescriptorBuilder { PropertyDescriptorBuilder::new() } + /// Creates an accessor property descriptor with default values. #[inline] #[must_use] pub fn into_accessor_defaulted(mut self) -> Self { @@ -227,6 +269,7 @@ impl PropertyDescriptor { .build() } + /// Creates a data property descriptor with default values. #[must_use] pub fn into_data_defaulted(mut self) -> Self { self.kind = DescriptorKind::Data { @@ -238,6 +281,7 @@ impl PropertyDescriptor { .build() } + /// Creates an generic property descriptor with default values. #[inline] #[must_use] pub fn complete_property_descriptor(self) -> Self { @@ -246,6 +290,12 @@ impl PropertyDescriptor { .build() } + /// Fills the fields of the `PropertyDescriptor` that are not set + /// with fields from the given `PropertyDescriptor`. + /// + /// # Panics + /// + /// Panics if the given `PropertyDescriptor` is not compatible with this one. #[inline] pub fn fill_with(&mut self, desc: &Self) { match (&mut self.kind, &desc.kind) { @@ -290,16 +340,20 @@ impl PropertyDescriptor { } } +/// A builder for [`PropertyDescriptor`]. #[derive(Default, Debug, Clone)] pub struct PropertyDescriptorBuilder { inner: PropertyDescriptor, } impl PropertyDescriptorBuilder { + /// Creates a new [`PropertyDescriptorBuilder`]. + #[must_use] pub fn new() -> Self { Self::default() } + /// Sets the `value` field of the property descriptor. #[must_use] pub fn value>(mut self, value: V) -> Self { match self.inner.kind { @@ -317,6 +371,7 @@ impl PropertyDescriptorBuilder { self } + /// Sets the `writable` field of the property descriptor. #[must_use] pub fn writable(mut self, writable: bool) -> Self { match self.inner.kind { @@ -335,6 +390,7 @@ impl PropertyDescriptorBuilder { self } + /// Sets the `get` field of the property descriptor. #[must_use] pub fn get>(mut self, get: V) -> Self { match self.inner.kind { @@ -350,6 +406,7 @@ impl PropertyDescriptorBuilder { self } + /// Sets the `set` field of the property descriptor. #[must_use] pub fn set>(mut self, set: V) -> Self { match self.inner.kind { @@ -365,22 +422,25 @@ impl PropertyDescriptorBuilder { self } + /// Optionally sets the `enumerable` field of the property descriptor. #[must_use] - pub fn maybe_enumerable(mut self, enumerable: Option) -> Self { + pub const fn maybe_enumerable(mut self, enumerable: Option) -> Self { if let Some(enumerable) = enumerable { self = self.enumerable(enumerable); } self } + /// Optionally sets the `configurable` field of the property descriptor. #[must_use] - pub fn maybe_configurable(mut self, configurable: Option) -> Self { + pub const fn maybe_configurable(mut self, configurable: Option) -> Self { if let Some(configurable) = configurable { self = self.configurable(configurable); } self } + /// Optionally sets the `value` field of the property descriptor. #[must_use] pub fn maybe_value>(mut self, value: Option) -> Self { if let Some(value) = value { @@ -389,6 +449,7 @@ impl PropertyDescriptorBuilder { self } + /// Optionally sets the `writable` field of the property descriptor. #[must_use] pub fn maybe_writable(mut self, writable: Option) -> Self { if let Some(writable) = writable { @@ -397,6 +458,7 @@ impl PropertyDescriptorBuilder { self } + /// Optionally sets the `get` field of the property descriptor. #[must_use] pub fn maybe_get>(mut self, get: Option) -> Self { if let Some(get) = get { @@ -405,6 +467,7 @@ impl PropertyDescriptorBuilder { self } + /// Optionally sets the `set` field of the property descriptor. #[must_use] pub fn maybe_set>(mut self, set: Option) -> Self { if let Some(set) = set { @@ -413,18 +476,21 @@ impl PropertyDescriptorBuilder { self } + /// Sets the `enumerable` field of the property descriptor. #[must_use] - pub fn enumerable(mut self, enumerable: bool) -> Self { + pub const fn enumerable(mut self, enumerable: bool) -> Self { self.inner.enumerable = Some(enumerable); self } + /// Sets the `configurable` field of the property descriptor. #[must_use] - pub fn configurable(mut self, configurable: bool) -> Self { + pub const fn configurable(mut self, configurable: bool) -> Self { self.inner.configurable = Some(configurable); self } + /// Fill any missing fields in the property descriptor. #[must_use] pub fn complete_with_defaults(mut self) -> Self { match self.inner.kind { @@ -466,10 +532,13 @@ impl PropertyDescriptorBuilder { self } - pub fn inner(&self) -> &PropertyDescriptor { + /// Returns a reference to the currently built [`PropertyDescriptor`]. + pub const fn inner(&self) -> &PropertyDescriptor { &self.inner } + /// Consumes the builder and returns the [`PropertyDescriptor`]. + #[allow(clippy::missing_const_for_fn)] pub fn build(self) -> PropertyDescriptor { self.inner } @@ -490,52 +559,51 @@ impl From for PropertyDescriptor { /// [spec]: https://tc39.es/ecma262/#sec-ispropertykey #[derive(PartialEq, Debug, Clone, Eq, Hash)] pub enum PropertyKey { + /// A string property key. String(JsString), + + /// A symbol property key. Symbol(JsSymbol), + + /// A numeric property key. Index(u32), } impl From for PropertyKey { #[inline] fn from(string: JsString) -> Self { - if let Some(index) = string.to_std_string().ok().and_then(|s| s.parse().ok()) { - Self::Index(index) - } else { - Self::String(string) - } + string + .to_std_string() + .ok() + .and_then(|s| s.parse().ok()) + .map_or(Self::String(string), Self::Index) } } impl From<&str> for PropertyKey { #[inline] fn from(string: &str) -> Self { - if let Ok(index) = string.parse() { - Self::Index(index) - } else { - Self::String(string.into()) - } + string + .parse() + .map_or_else(|_| Self::String(string.into()), Self::Index) } } impl From for PropertyKey { #[inline] fn from(string: String) -> Self { - if let Ok(index) = string.parse() { - Self::Index(index) - } else { - Self::String(string.into()) - } + string + .parse() + .map_or_else(|_| Self::String(string.into()), Self::Index) } } impl From> for PropertyKey { #[inline] fn from(string: Box) -> Self { - if let Ok(index) = string.parse() { - Self::Index(index) - } else { - Self::String(string.as_ref().into()) - } + string + .parse() + .map_or_else(|_| Self::String(string.as_ref().into()), Self::Index) } } @@ -564,11 +632,7 @@ impl From<&PropertyKey> for JsValue { PropertyKey::String(ref string) => string.clone().into(), PropertyKey::Symbol(ref symbol) => symbol.clone().into(), PropertyKey::Index(index) => { - if let Ok(integer) = i32::try_from(*index) { - Self::new(integer) - } else { - Self::new(*index) - } + i32::try_from(*index).map_or_else(|_| Self::new(*index), Self::new) } } } @@ -605,51 +669,36 @@ impl From for PropertyKey { impl From for PropertyKey { fn from(value: usize) -> Self { - if let Ok(index) = u32::try_from(value) { - Self::Index(index) - } else { - Self::String(js_string!(value.to_string())) - } + u32::try_from(value) + .map_or_else(|_| Self::String(js_string!(value.to_string())), Self::Index) } } impl From for PropertyKey { fn from(value: i64) -> Self { - if let Ok(index) = u32::try_from(value) { - Self::Index(index) - } else { - Self::String(js_string!(value.to_string())) - } + u32::try_from(value) + .map_or_else(|_| Self::String(js_string!(value.to_string())), Self::Index) } } impl From for PropertyKey { fn from(value: u64) -> Self { - if let Ok(index) = u32::try_from(value) { - Self::Index(index) - } else { - Self::String(js_string!(value.to_string())) - } + u32::try_from(value) + .map_or_else(|_| Self::String(js_string!(value.to_string())), Self::Index) } } impl From for PropertyKey { fn from(value: isize) -> Self { - if let Ok(index) = u32::try_from(value) { - Self::Index(index) - } else { - Self::String(js_string!(value.to_string())) - } + u32::try_from(value) + .map_or_else(|_| Self::String(js_string!(value.to_string())), Self::Index) } } impl From for PropertyKey { fn from(value: i32) -> Self { - if let Ok(index) = u32::try_from(value) { - Self::Index(index) - } else { - Self::String(js_string!(value.to_string())) - } + u32::try_from(value) + .map_or_else(|_| Self::String(js_string!(value.to_string())), Self::Index) } } diff --git a/boa_engine/src/realm.rs b/boa_engine/src/realm.rs index ab8cf8dfbd..31d076cf4c 100644 --- a/boa_engine/src/realm.rs +++ b/boa_engine/src/realm.rs @@ -25,6 +25,7 @@ pub struct Realm { } impl Realm { + /// Create a new Realm. #[inline] pub fn create(global_prototype: JsPrototype) -> Self { let _timer = Profiler::global().start_event("Realm::create", "realm"); @@ -46,7 +47,7 @@ impl Realm { } #[inline] - pub(crate) fn global_object(&self) -> &JsObject { + pub(crate) const fn global_object(&self) -> &JsObject { &self.global_object } diff --git a/boa_engine/src/string/mod.rs b/boa_engine/src/string/mod.rs index 2932e76b9b..1dd888a1b5 100644 --- a/boa_engine/src/string/mod.rs +++ b/boa_engine/src/string/mod.rs @@ -112,13 +112,17 @@ macro_rules! js_string { /// [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum CodePoint { + /// A valid Unicode scalar value. Unicode(char), + + /// An unpaired surrogate. UnpairedSurrogate(u16), } impl CodePoint { /// Get the number of UTF-16 code units needed to encode this code point. - pub fn code_unit_count(self) -> usize { + #[must_use] + pub const fn code_unit_count(self) -> usize { match self { Self::Unicode(c) => c.len_utf16(), Self::UnpairedSurrogate(_) => 1, @@ -126,6 +130,7 @@ impl CodePoint { } /// Convert the code point to its [`u32`] representation. + #[must_use] pub fn as_u32(self) -> u32 { match self { Self::Unicode(c) => u32::from(c), @@ -135,7 +140,8 @@ impl CodePoint { /// If the code point represents a valid 'Unicode scalar value', returns its [`char`] /// representation, otherwise returns [`None`] on unpaired surrogates. - pub fn as_char(self) -> Option { + #[must_use] + pub const fn as_char(self) -> Option { match self { Self::Unicode(c) => Some(c), Self::UnpairedSurrogate(_) => None, @@ -151,8 +157,8 @@ impl CodePoint { /// code point. pub fn encode_utf16(self, dst: &mut [u16]) -> &mut [u16] { match self { - CodePoint::Unicode(c) => c.encode_utf16(dst), - CodePoint::UnpairedSurrogate(surr) => { + Self::Unicode(c) => c.encode_utf16(dst), + Self::UnpairedSurrogate(surr) => { dst[0] = surr; &mut dst[0..=0] } @@ -210,7 +216,7 @@ impl TaggedJsString { /// `inner` must point to a valid instance of [`RawJsString`], which should be deallocated only /// by [`JsString`]. #[inline] - unsafe fn new_heap(inner: NonNull) -> Self { + const unsafe fn new_heap(inner: NonNull) -> Self { Self(inner) } @@ -297,17 +303,20 @@ unsafe impl Trace for JsString { impl JsString { /// Obtains the underlying [`&[u16]`][slice] slice of a [`JsString`] + #[must_use] pub fn as_slice(&self) -> &[u16] { self } /// Creates a new [`JsString`] from the concatenation of `x` and `y`. + #[must_use] pub fn concat(x: &[u16], y: &[u16]) -> Self { Self::concat_array(&[x, y]) } /// Creates a new [`JsString`] from the concatenation of every element of /// `strings`. + #[must_use] pub fn concat_array(strings: &[&[u16]]) -> Self { let mut full_count = 0usize; for &string in strings { @@ -356,6 +365,7 @@ impl JsString { /// Decodes a [`JsString`] into a [`String`], replacing invalid data with its escaped representation /// in 4 digit hexadecimal. + #[must_use] pub fn to_std_string_escaped(&self) -> String { self.to_string_escaped() } @@ -500,10 +510,10 @@ impl JsString { } // Slow path - let mut value = 0.0; + let mut value: f64 = 0.0; for c in s { if let Some(digit) = char::from(c).to_digit(base) { - value = value * f64::from(base) + f64::from(digit); + value = value.mul_add(f64::from(base), f64::from(digit)); } else { return f64::NAN; } @@ -577,6 +587,7 @@ impl JsString { debug_assert_eq!(offset, DATA_OFFSET); + #[allow(clippy::cast_ptr_alignment)] // SAFETY: // The layout size of `RawJsString` is never zero, since it has to store // the length of the string and the reference count. @@ -767,7 +778,7 @@ impl From<&[u16]> for JsString { impl From> for JsString { fn from(vec: Vec) -> Self { - JsString::from(&vec[..]) + Self::from(&vec[..]) } } diff --git a/boa_engine/src/symbol.rs b/boa_engine/src/symbol.rs index c0b69c841f..26792b99ba 100644 --- a/boa_engine/src/symbol.rs +++ b/boa_engine/src/symbol.rs @@ -120,6 +120,7 @@ impl WellKnownSymbols { /// A method that returns the default `AsyncIterator` for an object. /// Called by the semantics of the `for-await-of` statement. #[inline] + #[must_use] pub fn async_iterator() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.async_iterator.clone()) } @@ -130,6 +131,7 @@ impl WellKnownSymbols { /// recognizes an object as one of the `constructor`'s instances. /// Called by the semantics of the instanceof operator. #[inline] + #[must_use] pub fn has_instance() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.has_instance.clone()) } @@ -140,6 +142,7 @@ impl WellKnownSymbols { /// an object should be flattened to its array elements /// by `Array.prototype.concat`. #[inline] + #[must_use] pub fn is_concat_spreadable() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.is_concat_spreadable.clone()) } @@ -149,6 +152,7 @@ impl WellKnownSymbols { /// A method that returns the default Iterator for an object. /// Called by the semantics of the `for-of` statement. #[inline] + #[must_use] pub fn iterator() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.iterator.clone()) } @@ -158,6 +162,7 @@ impl WellKnownSymbols { /// A regular expression method that matches the regular expression /// against a string. Called by the `String.prototype.match` method. #[inline] + #[must_use] pub fn r#match() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.r#match.clone()) } @@ -168,6 +173,7 @@ impl WellKnownSymbols { /// matches of the regular expression against a string. /// Called by the `String.prototype.matchAll` method. #[inline] + #[must_use] pub fn match_all() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.match_all.clone()) } @@ -177,6 +183,7 @@ impl WellKnownSymbols { /// A regular expression method that replaces matched substrings /// of a string. Called by the `String.prototype.replace` method. #[inline] + #[must_use] pub fn replace() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.replace.clone()) } @@ -187,6 +194,7 @@ impl WellKnownSymbols { /// string that matches the regular expression. /// Called by the `String.prototype.search` method. #[inline] + #[must_use] pub fn search() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.search.clone()) } @@ -196,6 +204,7 @@ impl WellKnownSymbols { /// A function valued property that is the `constructor` function /// that is used to create derived objects. #[inline] + #[must_use] pub fn species() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.species.clone()) } @@ -206,6 +215,7 @@ impl WellKnownSymbols { /// that match the regular expression. /// Called by the `String.prototype.split` method. #[inline] + #[must_use] pub fn split() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.split.clone()) } @@ -215,6 +225,7 @@ impl WellKnownSymbols { /// A method that converts an object to a corresponding primitive value. /// Called by the `ToPrimitive` (`Value::to_primitive`) abstract operation. #[inline] + #[must_use] pub fn to_primitive() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.to_primitive.clone()) } @@ -225,6 +236,7 @@ impl WellKnownSymbols { /// string description of an object. /// Accessed by the built-in method `Object.prototype.toString`. #[inline] + #[must_use] pub fn to_string_tag() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.to_string_tag.clone()) } @@ -234,6 +246,7 @@ impl WellKnownSymbols { /// An object valued property whose own and inherited property names are property /// names that are excluded from the `with` environment bindings of the associated object. #[inline] + #[must_use] pub fn unscopables() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.unscopables.clone()) } @@ -261,6 +274,7 @@ unsafe impl Trace for JsSymbol { impl JsSymbol { /// Create a new symbol. #[inline] + #[must_use] pub fn new(description: Option) -> Self { let hash = SYMBOL_HASH_COUNT.with(|count| { let hash = count.get(); @@ -283,6 +297,7 @@ impl JsSymbol { /// Returns the `Symbol`s description. #[inline] + #[must_use] pub fn description(&self) -> Option { self.inner.description.clone() } @@ -291,6 +306,7 @@ impl JsSymbol { /// /// The hash is guaranteed to be unique. #[inline] + #[must_use] pub fn hash(&self) -> u64 { self.inner.hash } @@ -301,6 +317,7 @@ impl JsSymbol { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-symboldescriptivestring + #[must_use] pub fn descriptive_string(&self) -> JsString { self.inner.description.as_ref().map_or_else( || js_string!("Symbol()"), diff --git a/boa_engine/src/tests.rs b/boa_engine/src/tests.rs index 7e97b8e52a..0e0b3a7e00 100644 --- a/boa_engine/src/tests.rs +++ b/boa_engine/src/tests.rs @@ -1070,14 +1070,16 @@ fn to_length() { assert_eq!(JsValue::new(20.9).to_length(&mut context).unwrap(), 20); assert_eq!(JsValue::new(-20.9).to_length(&mut context).unwrap(), 0); assert_eq!( - JsValue::new(100000000000.0) + JsValue::new(100_000_000_000.0) .to_length(&mut context) .unwrap(), - 100000000000 + 100_000_000_000 ); assert_eq!( - JsValue::new(4010101101.0).to_length(&mut context).unwrap(), - 4010101101 + JsValue::new(4_010_101_101.0) + .to_length(&mut context) + .unwrap(), + 4_010_101_101 ); } @@ -1112,43 +1114,43 @@ fn to_int32() { check_to_int32!(-0.6 => 0); check_to_int32!(-1.6 => -1); - check_to_int32!(2147483647.0 => 2147483647); - check_to_int32!(2147483648.0 => -2147483648); - check_to_int32!(2147483649.0 => -2147483647); - - check_to_int32!(4294967295.0 => -1); - check_to_int32!(4294967296.0 => 0); - check_to_int32!(4294967297.0 => 1); - - check_to_int32!(-2147483647.0 => -2147483647); - check_to_int32!(-2147483648.0 => -2147483648); - check_to_int32!(-2147483649.0 => 2147483647); - - check_to_int32!(-4294967295.0 => 1); - check_to_int32!(-4294967296.0 => 0); - check_to_int32!(-4294967297.0 => -1); - - check_to_int32!(2147483648.25 => -2147483648); - check_to_int32!(2147483648.5 => -2147483648); - check_to_int32!(2147483648.75 => -2147483648); - check_to_int32!(4294967295.25 => -1); - check_to_int32!(4294967295.5 => -1); - check_to_int32!(4294967295.75 => -1); - check_to_int32!(3000000000.25 => -1294967296); - check_to_int32!(3000000000.5 => -1294967296); - check_to_int32!(3000000000.75 => -1294967296); - - check_to_int32!(-2147483648.25 => -2147483648); - check_to_int32!(-2147483648.5 => -2147483648); - check_to_int32!(-2147483648.75 => -2147483648); - check_to_int32!(-4294967295.25 => 1); - check_to_int32!(-4294967295.5 => 1); - check_to_int32!(-4294967295.75 => 1); - check_to_int32!(-3000000000.25 => 1294967296); - check_to_int32!(-3000000000.5 => 1294967296); - check_to_int32!(-3000000000.75 => 1294967296); - - let base = 2f64.powf(64.0); + check_to_int32!(2_147_483_647.0 => 2_147_483_647); + check_to_int32!(2_147_483_648.0 => -2_147_483_648); + check_to_int32!(2_147_483_649.0 => -2_147_483_647); + + check_to_int32!(4_294_967_295.0 => -1); + check_to_int32!(4_294_967_296.0 => 0); + check_to_int32!(4_294_967_297.0 => 1); + + check_to_int32!(-2_147_483_647.0 => -2_147_483_647); + check_to_int32!(-2_147_483_648.0 => -2_147_483_648); + check_to_int32!(-2_147_483_649.0 => 2_147_483_647); + + check_to_int32!(-4_294_967_295.0 => 1); + check_to_int32!(-4_294_967_296.0 => 0); + check_to_int32!(-4_294_967_297.0 => -1); + + check_to_int32!(2_147_483_648.25 => -2_147_483_648); + check_to_int32!(2_147_483_648.5 => -2_147_483_648); + check_to_int32!(2_147_483_648.75 => -2_147_483_648); + check_to_int32!(4_294_967_295.25 => -1); + check_to_int32!(4_294_967_295.5 => -1); + check_to_int32!(4_294_967_295.75 => -1); + check_to_int32!(3_000_000_000.25 => -1_294_967_296); + check_to_int32!(3_000_000_000.5 => -1_294_967_296); + check_to_int32!(3_000_000_000.75 => -1_294_967_296); + + check_to_int32!(-2_147_483_648.25 => -2_147_483_648); + check_to_int32!(-2_147_483_648.5 => -2_147_483_648); + check_to_int32!(-2_147_483_648.75 => -2_147_483_648); + check_to_int32!(-4_294_967_295.25 => 1); + check_to_int32!(-4_294_967_295.5 => 1); + check_to_int32!(-4_294_967_295.75 => 1); + check_to_int32!(-3_000_000_000.25 => 1_294_967_296); + check_to_int32!(-3_000_000_000.5 => 1_294_967_296); + check_to_int32!(-3_000_000_000.75 => 1_294_967_296); + + let base = 2f64.powi(64); check_to_int32!(base + 0.0 => 0); check_to_int32!(base + 1117.0 => 0); check_to_int32!(base + 2234.0 => 4096); @@ -1180,16 +1182,16 @@ fn to_int32() { check_to_int32!(base + 31276.0 => 32768); // bignum is (2^53 - 1) * 2^31 - highest number with bit 31 set. - let bignum = 2f64.powf(84.0) - 2f64.powf(31.0); - check_to_int32!(bignum => -2147483648); - check_to_int32!(-bignum => -2147483648); + let bignum = 2f64.powi(84) - 2f64.powi(31); + check_to_int32!(bignum => -2_147_483_648); + check_to_int32!(-bignum => -2_147_483_648); check_to_int32!(2.0 * bignum => 0); check_to_int32!(-(2.0 * bignum) => 0); - check_to_int32!(bignum - 2f64.powf(31.0) => 0); - check_to_int32!(-(bignum - 2f64.powf(31.0)) => 0); + check_to_int32!(bignum - 2f64.powi(31) => 0); + check_to_int32!(-(bignum - 2f64.powi(31)) => 0); // max_fraction is largest number below 1. - let max_fraction = 1.0 - 2f64.powf(-53.0); + let max_fraction = 1.0 - 2f64.powi(-53); check_to_int32!(max_fraction => 0); check_to_int32!(-max_fraction => 0); } diff --git a/boa_engine/src/value/conversions.rs b/boa_engine/src/value/conversions.rs index 771acbbf8c..20324908b2 100644 --- a/boa_engine/src/value/conversions.rs +++ b/boa_engine/src/value/conversions.rs @@ -1,4 +1,4 @@ -use super::{Display, JsBigInt, JsObject, JsString, JsSymbol, JsValue, Profiler}; +use super::{JsBigInt, JsObject, JsString, JsSymbol, JsValue, Profiler}; impl From<&Self> for JsValue { #[inline] @@ -33,15 +33,6 @@ impl From for JsValue { } } -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] -pub struct TryFromCharError; - -impl Display for TryFromCharError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Could not convert value to a char type") - } -} - impl From for JsValue { #[allow(clippy::float_cmp)] #[inline] @@ -97,11 +88,7 @@ impl From for JsValue { impl From for JsValue { #[inline] fn from(value: u32) -> Self { - if let Ok(integer) = i32::try_from(value) { - Self::Integer(integer) - } else { - Self::Rational(value.into()) - } + i32::try_from(value).map_or_else(|_| Self::Rational(value.into()), Self::Integer) } } @@ -122,33 +109,21 @@ impl From for JsValue { impl From for JsValue { #[inline] fn from(value: usize) -> Self { - if let Ok(value) = i32::try_from(value) { - Self::Integer(value) - } else { - Self::Rational(value as f64) - } + i32::try_from(value).map_or(Self::Rational(value as f64), Self::Integer) } } impl From for JsValue { #[inline] fn from(value: u64) -> Self { - if let Ok(value) = i32::try_from(value) { - Self::Integer(value) - } else { - Self::Rational(value as f64) - } + i32::try_from(value).map_or(Self::Rational(value as f64), Self::Integer) } } impl From for JsValue { #[inline] fn from(value: i64) -> Self { - if let Ok(value) = i32::try_from(value) { - Self::Integer(value) - } else { - Self::Rational(value as f64) - } + i32::try_from(value).map_or(Self::Rational(value as f64), Self::Integer) } } diff --git a/boa_engine/src/value/display.rs b/boa_engine/src/value/display.rs index d9efe58c99..38afff909f 100644 --- a/boa_engine/src/value/display.rs +++ b/boa_engine/src/value/display.rs @@ -20,7 +20,7 @@ impl ValueDisplay<'_> { /// By default this is `false`. #[inline] #[must_use] - pub fn internals(mut self, yes: bool) -> Self { + pub const fn internals(mut self, yes: bool) -> Self { self.internals = yes; self } diff --git a/boa_engine/src/value/equality.rs b/boa_engine/src/value/equality.rs index 58f6cee9de..742ad47daa 100644 --- a/boa_engine/src/value/equality.rs +++ b/boa_engine/src/value/equality.rs @@ -64,16 +64,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)) => match b.to_big_int() { - Some(ref b) => a == b, - None => false, - }, + (Self::BigInt(ref a), Self::String(ref b)) => { + b.to_big_int().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)) => match a.to_big_int() { - Some(ref a) => a == b, - None => false, - }, + (Self::String(ref a), Self::BigInt(ref b)) => { + a.to_big_int().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), @@ -170,16 +168,12 @@ impl JsValue { match (x, y) { // 2. If Type(x) is Number or BigInt, then // a. Return ! Type(x)::SameValueZero(x, y). - (JsValue::BigInt(x), JsValue::BigInt(y)) => JsBigInt::same_value_zero(x, y), + (Self::BigInt(x), Self::BigInt(y)) => JsBigInt::same_value_zero(x, y), - (JsValue::Rational(x), JsValue::Rational(y)) => Number::same_value_zero(*x, *y), - (JsValue::Rational(x), JsValue::Integer(y)) => { - Number::same_value_zero(*x, f64::from(*y)) - } - (JsValue::Integer(x), JsValue::Rational(y)) => { - Number::same_value_zero(f64::from(*x), *y) - } - (JsValue::Integer(x), JsValue::Integer(y)) => x == y, + (Self::Rational(x), Self::Rational(y)) => Number::same_value_zero(*x, *y), + (Self::Rational(x), Self::Integer(y)) => Number::same_value_zero(*x, f64::from(*y)), + (Self::Integer(x), Self::Rational(y)) => Number::same_value_zero(f64::from(*x), *y), + (Self::Integer(x), Self::Integer(y)) => x == y, // 3. Return ! SameValueNonNumeric(x, y). (_, _) => Self::same_value_non_numeric(x, y), diff --git a/boa_engine/src/value/integer.rs b/boa_engine/src/value/integer.rs index 64b757dd85..5b9e078c85 100644 --- a/boa_engine/src/value/integer.rs +++ b/boa_engine/src/value/integer.rs @@ -3,14 +3,24 @@ use std::cmp::Ordering; /// Represents the result of the `ToIntegerOrInfinity` operation #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum IntegerOrInfinity { + /// Positive infinity. PositiveInfinity, + + /// An integer. Integer(i64), + + /// Negative infinity. NegativeInfinity, } impl IntegerOrInfinity { /// Clamps an `IntegerOrInfinity` between two `i64`, effectively converting /// it to an i64. + /// + /// # Panics + /// + /// Panics if `min > max`. + #[must_use] pub fn clamp_finite(self, min: i64, max: i64) -> i64 { assert!(min <= max); @@ -22,7 +32,8 @@ impl IntegerOrInfinity { } /// Gets the wrapped `i64` if the variant is an `Integer`. - pub fn as_integer(self) -> Option { + #[must_use] + pub const fn as_integer(self) -> Option { match self { Self::Integer(i) => Some(i), _ => None, @@ -100,7 +111,7 @@ pub(crate) enum IntegerOrNan { impl IntegerOrNan { /// Gets the wrapped `i64` if the variant is an `Integer`. - pub(crate) fn as_integer(self) -> Option { + pub(crate) const fn as_integer(self) -> Option { match self { Self::Integer(i) => Some(i), Self::Nan => None, diff --git a/boa_engine/src/value/mod.rs b/boa_engine/src/value/mod.rs index b40acc5482..ddca745d04 100644 --- a/boa_engine/src/value/mod.rs +++ b/boa_engine/src/value/mod.rs @@ -2,6 +2,16 @@ //! //! Javascript values, utility methods and conversion between Javascript values and Rust values. +mod conversions; +mod equality; +mod hash; +mod integer; +mod operations; +mod serde_json; +mod r#type; + +pub(crate) mod display; + #[cfg(test)] mod tests; @@ -29,16 +39,8 @@ use std::{ ops::Sub, }; -mod conversions; -pub(crate) mod display; -mod equality; -mod hash; -mod integer; -mod operations; -mod serde_json; -mod r#type; +pub(crate) use conversions::*; -pub use conversions::*; pub use display::ValueDisplay; pub use integer::IntegerOrInfinity; pub use operations::*; @@ -99,42 +101,48 @@ impl JsValue { /// Creates a new `undefined` value. #[inline] - pub fn undefined() -> Self { + #[must_use] + pub const fn undefined() -> Self { Self::Undefined } /// Creates a new `null` value. #[inline] - pub fn null() -> Self { + #[must_use] + pub const fn null() -> Self { Self::Null } /// Creates a new number with `NaN` value. #[inline] - pub fn nan() -> Self { + #[must_use] + pub const fn nan() -> Self { Self::Rational(f64::NAN) } /// Creates a new number with `Infinity` value. #[inline] - pub fn positive_infinity() -> Self { + #[must_use] + pub const fn positive_infinity() -> Self { Self::Rational(f64::INFINITY) } /// Creates a new number with `-Infinity` value. #[inline] - pub fn negative_infinity() -> Self { + #[must_use] + pub const fn negative_infinity() -> Self { Self::Rational(f64::NEG_INFINITY) } - /// Returns true if the value is an object + /// Returns true if the value is an object. #[inline] - pub fn is_object(&self) -> bool { + pub const fn is_object(&self) -> bool { matches!(self, Self::Object(_)) } + /// Returns the object if the value is object, otherwise `None`. #[inline] - pub fn as_object(&self) -> Option<&JsObject> { + pub const fn as_object(&self) -> Option<&JsObject> { match *self { Self::Object(ref o) => Some(o), _ => None, @@ -152,6 +160,7 @@ impl JsValue { matches!(self, Self::Object(obj) if obj.is_callable()) } + /// Returns the callable value if the value is callable, otherwise `None`. #[inline] pub fn as_callable(&self) -> Option<&JsObject> { self.as_object().filter(|obj| obj.is_callable()) @@ -163,6 +172,7 @@ impl JsValue { matches!(self, Self::Object(obj) if obj.is_constructor()) } + /// Returns the constructor if the value is a constructor, otherwise `None`. #[inline] pub fn as_constructor(&self) -> Option<&JsObject> { self.as_object().filter(|obj| obj.is_constructor()) @@ -174,6 +184,7 @@ impl JsValue { matches!(self, Self::Object(obj) if obj.is_promise()) } + /// Returns the promise if the value is a promise, otherwise `None`. #[inline] pub fn as_promise(&self) -> Option<&JsObject> { self.as_object().filter(|obj| obj.is_promise()) @@ -181,10 +192,11 @@ impl JsValue { /// Returns true if the value is a symbol. #[inline] - pub fn is_symbol(&self) -> bool { + pub const fn is_symbol(&self) -> bool { matches!(self, Self::Symbol(_)) } + /// Returns the symbol if the value is a symbol, otherwise `None`. pub fn as_symbol(&self) -> Option { match self { Self::Symbol(symbol) => Some(symbol.clone()), @@ -194,25 +206,25 @@ impl JsValue { /// Returns true if the value is undefined. #[inline] - pub fn is_undefined(&self) -> bool { + pub const fn is_undefined(&self) -> bool { matches!(self, Self::Undefined) } /// Returns true if the value is null. #[inline] - pub fn is_null(&self) -> bool { + pub const fn is_null(&self) -> bool { matches!(self, Self::Null) } /// Returns true if the value is null or undefined. #[inline] - pub fn is_null_or_undefined(&self) -> bool { + pub const fn is_null_or_undefined(&self) -> bool { matches!(self, Self::Null | Self::Undefined) } /// Returns true if the value is a 64-bit floating-point number. #[inline] - pub fn is_double(&self) -> bool { + pub const fn is_double(&self) -> bool { matches!(self, Self::Rational(_)) } @@ -233,10 +245,11 @@ impl JsValue { /// Returns true if the value is a number. #[inline] - pub fn is_number(&self) -> bool { + pub const fn is_number(&self) -> bool { matches!(self, Self::Rational(_) | Self::Integer(_)) } + /// Returns the number if the value is a number, otherwise `None`. #[inline] pub fn as_number(&self) -> Option { match *self { @@ -248,13 +261,13 @@ impl JsValue { /// Returns true if the value is a string. #[inline] - pub fn is_string(&self) -> bool { + pub const fn is_string(&self) -> bool { matches!(self, Self::String(_)) } - /// Returns the string if the values is a string, otherwise `None`. + /// Returns the string if the value is a string, otherwise `None`. #[inline] - pub fn as_string(&self) -> Option<&JsString> { + pub const fn as_string(&self) -> Option<&JsString> { match self { Self::String(ref string) => Some(string), _ => None, @@ -263,12 +276,13 @@ impl JsValue { /// Returns true if the value is a boolean. #[inline] - pub fn is_boolean(&self) -> bool { + pub const fn is_boolean(&self) -> bool { matches!(self, Self::Boolean(_)) } + /// Returns the boolean if the value is a boolean, otherwise `None`. #[inline] - pub fn as_boolean(&self) -> Option { + pub const fn as_boolean(&self) -> Option { match self { Self::Boolean(boolean) => Some(*boolean), _ => None, @@ -277,13 +291,13 @@ impl JsValue { /// Returns true if the value is a bigint. #[inline] - pub fn is_bigint(&self) -> bool { + pub const fn is_bigint(&self) -> bool { matches!(self, Self::BigInt(_)) } /// Returns an optional reference to a `BigInt` if the value is a `BigInt` primitive. #[inline] - pub fn as_bigint(&self) -> Option<&JsBigInt> { + pub const fn as_bigint(&self) -> Option<&JsBigInt> { match self { Self::BigInt(bigint) => Some(bigint), _ => None, @@ -404,18 +418,17 @@ impl JsValue { Self::Undefined => Err(JsNativeError::typ() .with_message("cannot convert undefined to a BigInt") .into()), - Self::String(ref string) => { - if let Some(value) = string.to_big_int() { - Ok(value) - } else { + Self::String(ref string) => string.to_big_int().map_or_else( + || { Err(JsNativeError::syntax() .with_message(format!( "cannot convert string '{}' to bigint primitive", string.to_std_string_escaped() )) .into()) - } - } + }, + Ok, + ), Self::Boolean(true) => Ok(JsBigInt::one()), Self::Boolean(false) => Ok(JsBigInt::zero()), Self::Integer(_) | Self::Rational(_) => Err(JsNativeError::typ() @@ -447,7 +460,7 @@ impl JsValue { /// println!("{}", value.display()); /// ``` #[inline] - pub fn display(&self) -> ValueDisplay<'_> { + pub const fn display(&self) -> ValueDisplay<'_> { ValueDisplay { value: self, internals: false, @@ -588,8 +601,10 @@ impl JsValue { /// See: pub fn to_u32(&self, context: &mut Context) -> JsResult { // This is the fast path, if the value is Integer we can just return it. - if let JsValue::Integer(number) = *self { - return Ok(number as u32); + if let Self::Integer(number) = *self { + if let Ok(number) = u32::try_from(number) { + return Ok(number); + } } let number = self.to_number(context)?; @@ -601,7 +616,7 @@ impl JsValue { /// See: pub fn to_i32(&self, context: &mut Context) -> JsResult { // This is the fast path, if the value is Integer we can just return it. - if let JsValue::Integer(number) = *self { + if let Self::Integer(number) = *self { return Ok(number); } let number = self.to_number(context)?; @@ -940,6 +955,12 @@ impl JsValue { } } + /// The abstract operation `ToPropertyDescriptor`. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-topropertydescriptor #[inline] pub fn to_property_descriptor(&self, context: &mut Context) -> JsResult { // 1. If Type(Obj) is not Object, throw a TypeError exception. @@ -991,13 +1012,8 @@ impl JsValue { // The main part of the function is implemented for JsObject. // 1. If Type(argument) is not Object, return false. - if let Some(object) = self.as_object() { - object.is_array_abstract() - } - // 4. Return false. - else { - Ok(false) - } + self.as_object() + .map_or(Ok(false), JsObject::is_array_abstract) } } @@ -1010,8 +1026,13 @@ impl Default for JsValue { /// The preferred type to convert an object to a primitive `Value`. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum PreferredType { + /// Prefer to convert to a `String` primitive. String, + + /// Prefer to convert to a `Number` primitive. Number, + + /// Do not prefer a type to convert to. Default, } diff --git a/boa_engine/src/value/operations.rs b/boa_engine/src/value/operations.rs index 45dcaaf22d..f4b693acc6 100644 --- a/boa_engine/src/value/operations.rs +++ b/boa_engine/src/value/operations.rs @@ -10,6 +10,7 @@ use crate::{ }; impl JsValue { + /// Perform the binary `+` operator on the value and return the result. #[inline] pub fn add(&self, other: &Self, context: &mut Context) -> JsResult { Ok(match (self, other) { @@ -50,6 +51,7 @@ impl JsValue { }) } + /// Perform the binary `-` operator on the value and return the result. #[inline] pub fn sub(&self, other: &Self, context: &mut Context) -> JsResult { Ok(match (self, other) { @@ -76,6 +78,7 @@ impl JsValue { }) } + /// Perform the binary `*` operator on the value and return the result. #[inline] pub fn mul(&self, other: &Self, context: &mut Context) -> JsResult { Ok(match (self, other) { @@ -102,6 +105,7 @@ impl JsValue { }) } + /// Perform the binary `/` operator on the value and return the result. #[inline] pub fn div(&self, other: &Self, context: &mut Context) -> JsResult { Ok(match (self, other) { @@ -143,6 +147,7 @@ impl JsValue { }) } + /// Perform the binary `%` operator on the value and return the result. #[inline] pub fn rem(&self, other: &Self, context: &mut Context) -> JsResult { Ok(match (self, other) { @@ -194,6 +199,7 @@ impl JsValue { }) } + /// Perform the binary `**` operator on the value and return the result. #[inline] pub fn pow(&self, other: &Self, context: &mut Context) -> JsResult { Ok(match (self, other) { @@ -221,6 +227,7 @@ impl JsValue { }) } + /// Perform the binary `&` operator on the value and return the result. #[inline] pub fn bitand(&self, other: &Self, context: &mut Context) -> JsResult { Ok(match (self, other) { @@ -251,6 +258,7 @@ impl JsValue { }) } + /// Perform the binary `|` operator on the value and return the result. #[inline] pub fn bitor(&self, other: &Self, context: &mut Context) -> JsResult { Ok(match (self, other) { @@ -281,6 +289,7 @@ impl JsValue { }) } + /// Perform the binary `^` operator on the value and return the result. #[inline] pub fn bitxor(&self, other: &Self, context: &mut Context) -> JsResult { Ok(match (self, other) { @@ -311,6 +320,7 @@ impl JsValue { }) } + /// Perform the binary `<<` operator on the value and return the result. #[inline] pub fn shl(&self, other: &Self, context: &mut Context) -> JsResult { Ok(match (self, other) { @@ -343,6 +353,7 @@ impl JsValue { }) } + /// Perform the binary `>>` operator on the value and return the result. #[inline] pub fn shr(&self, other: &Self, context: &mut Context) -> JsResult { Ok(match (self, other) { @@ -375,6 +386,7 @@ impl JsValue { }) } + /// Perform the binary `>>>` operator on the value and return the result. #[inline] pub fn ushr(&self, other: &Self, context: &mut Context) -> JsResult { Ok(match (self, other) { @@ -449,14 +461,15 @@ impl JsValue { } } + /// Returns the negated value. #[inline] pub fn neg(&self, context: &mut Context) -> JsResult { Ok(match *self { Self::Symbol(_) | Self::Undefined => Self::new(f64::NAN), - Self::Object(_) => Self::new(match self.to_numeric_number(context) { - Ok(num) => -num, - Err(_) => f64::NAN, - }), + Self::Object(_) => Self::new( + self.to_numeric_number(context) + .map_or(f64::NAN, std::ops::Neg::neg), + ), Self::String(ref str) => Self::new(-str.to_number()), Self::Rational(num) => Self::new(-num), Self::Integer(num) if num == 0 => Self::new(-f64::from(0)), @@ -467,8 +480,9 @@ impl JsValue { }) } + /// Returns the negated boolean value. #[inline] - pub fn not(&self, _: &mut Context) -> JsResult { + pub fn not(&self) -> JsResult { Ok(!self.to_boolean()) } @@ -518,20 +532,12 @@ impl JsValue { match (px, py) { (Self::String(ref x), Self::String(ref y)) => (x < y).into(), - (Self::BigInt(ref x), Self::String(ref y)) => { - if let Some(y) = y.to_big_int() { - (*x < y).into() - } else { - AbstractRelation::Undefined - } - } - (Self::String(ref x), Self::BigInt(ref y)) => { - if let Some(x) = x.to_big_int() { - (x < *y).into() - } else { - AbstractRelation::Undefined - } - } + (Self::BigInt(ref x), Self::String(ref y)) => y + .to_big_int() + .map_or(AbstractRelation::Undefined, |y| (*x < y).into()), + (Self::String(ref x), Self::BigInt(ref y)) => x + .to_big_int() + .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), (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => (x < y).into(), diff --git a/boa_engine/src/value/serde_json.rs b/boa_engine/src/value/serde_json.rs index ec5a6d3c62..5023f28857 100644 --- a/boa_engine/src/value/serde_json.rs +++ b/boa_engine/src/value/serde_json.rs @@ -104,6 +104,10 @@ impl JsValue { /// # /// # assert_eq!(json, back_to_json); /// ``` + /// + /// # Panics + /// + /// Panics if the `JsValue` is `Undefined`. pub fn to_json(&self, context: &mut Context) -> JsResult { match self { Self::Null => Ok(Value::Null), @@ -279,6 +283,6 @@ mod tests { let pow: u32 = serde_json::from_value(pow.to_json(&mut context).unwrap()).unwrap(); - assert_eq!(pow, 60466176); + assert_eq!(pow, 60_466_176); } } diff --git a/boa_engine/src/value/tests.rs b/boa_engine/src/value/tests.rs index 11a379a112..ab46bc3a73 100644 --- a/boa_engine/src/value/tests.rs +++ b/boa_engine/src/value/tests.rs @@ -208,27 +208,30 @@ fn to_string() { assert_eq!(f64_to_str(f64::INFINITY), "Infinity"); assert_eq!(f64_to_str(f64::NEG_INFINITY), "-Infinity"); assert_eq!(f64_to_str(90.12), "90.12"); - assert_eq!(f64_to_str(111111111111111111111.0), "111111111111111110000"); assert_eq!( - f64_to_str(1111111111111111111111.0), + f64_to_str(111_111_111_111_111_111_111.0), + "111111111111111110000" + ); + assert_eq!( + f64_to_str(1_111_111_111_111_111_111_111.0), "1.1111111111111111e+21" ); assert_eq!(f64_to_str(-90.12), "-90.12"); assert_eq!( - f64_to_str(-111111111111111111111.0), + f64_to_str(-111_111_111_111_111_111_111.0), "-111111111111111110000" ); assert_eq!( - f64_to_str(-1111111111111111111111.0), + f64_to_str(-1_111_111_111_111_111_111_111.0), "-1.1111111111111111e+21" ); - assert_eq!(f64_to_str(0.0000001), "1e-7"); - assert_eq!(f64_to_str(0.000001), "0.000001"); - assert_eq!(f64_to_str(0.0000002), "2e-7"); - assert_eq!(f64_to_str(-0.0000001), "-1e-7"); + assert_eq!(f64_to_str(0.000_000_1), "1e-7"); + assert_eq!(f64_to_str(0.000_001), "0.000001"); + assert_eq!(f64_to_str(0.000_000_2), "2e-7"); + assert_eq!(f64_to_str(-0.000_000_1), "-1e-7"); assert_eq!(f64_to_str(3e50), "3e+50"); } diff --git a/boa_engine/src/value/type.rs b/boa_engine/src/value/type.rs index 61e100bae6..c5cf348632 100644 --- a/boa_engine/src/value/type.rs +++ b/boa_engine/src/value/type.rs @@ -3,13 +3,28 @@ use super::JsValue; /// Possible types of values as defined at . #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Type { + /// The "undefined" type. Undefined, + + /// The "null" type. Null, + + /// The "boolean" type. Boolean, + + /// The "number" type. Number, + + /// The "string" type. String, + + /// The "symbol" type. Symbol, + + /// The "bigint" type. BigInt, + + /// The "object" type. Object, } @@ -20,7 +35,7 @@ impl JsValue { /// . /// /// Check [`JsValue::type_of`] if you need to call the `typeof` operator. - pub fn get_type(&self) -> Type { + pub const fn get_type(&self) -> Type { match *self { Self::Rational(_) | Self::Integer(_) => Type::Number, Self::String(_) => Type::String, diff --git a/boa_engine/src/vm/call_frame.rs b/boa_engine/src/vm/call_frame.rs index b12782be2e..764280e158 100644 --- a/boa_engine/src/vm/call_frame.rs +++ b/boa_engine/src/vm/call_frame.rs @@ -5,6 +5,7 @@ use crate::{object::JsObject, vm::CodeBlock}; use boa_gc::{Finalize, Gc, Trace}; +/// A `CallFrame` holds the state of a function call. #[derive(Clone, Debug, Finalize, Trace)] pub struct CallFrame { pub(crate) code: Gc, diff --git a/boa_engine/src/vm/code_block.rs b/boa_engine/src/vm/code_block.rs index b79ddf3b4b..074591a7e6 100644 --- a/boa_engine/src/vm/code_block.rs +++ b/boa_engine/src/vm/code_block.rs @@ -117,6 +117,7 @@ pub struct CodeBlock { impl CodeBlock { /// Constructs a new `CodeBlock`. + #[must_use] pub fn new(name: Sym, length: u32, strict: bool) -> Self { Self { code: Vec::new(), @@ -147,9 +148,12 @@ impl CodeBlock { where T: Readable, { + // Safety: + // The function caller must ensure that the read is in bounds. + // // This has to be an unaligned read because we can't guarantee that // the types are aligned. - self.code.as_ptr().add(offset).cast::().read_unaligned() + unsafe { self.code.as_ptr().add(offset).cast::().read_unaligned() } } /// Read type T from code. @@ -1085,15 +1089,14 @@ impl JsObject { std::mem::swap(&mut environments, &mut context.realm.environments); std::mem::swap(&mut context.vm.stack, &mut stack); - let prototype = if let Some(prototype) = this_function_object + let prototype = this_function_object .get("prototype", context) .expect("GeneratorFunction must have a prototype property") .as_object() - { - prototype.clone() - } else { - context.intrinsics().constructors().generator().prototype() - }; + .map_or_else( + || context.intrinsics().constructors().generator().prototype(), + Clone::clone, + ); let generator = Self::from_proto_and_data( prototype, @@ -1224,19 +1227,20 @@ impl JsObject { std::mem::swap(&mut environments, &mut context.realm.environments); std::mem::swap(&mut context.vm.stack, &mut stack); - let prototype = if let Some(prototype) = this_function_object + let prototype = this_function_object .get("prototype", context) .expect("AsyncGeneratorFunction must have a prototype property") .as_object() - { - prototype.clone() - } else { - context - .intrinsics() - .constructors() - .async_generator() - .prototype() - }; + .map_or_else( + || { + context + .intrinsics() + .constructors() + .async_generator() + .prototype() + }, + Clone::clone, + ); let generator = Self::from_proto_and_data( prototype, @@ -1272,7 +1276,7 @@ impl JsObject { args: &[JsValue], this_target: &JsValue, context: &mut Context, - ) -> JsResult { + ) -> JsResult { let this_function_object = self.clone(); let create_this = |context| { diff --git a/boa_engine/src/vm/flowgraph/color.rs b/boa_engine/src/vm/flowgraph/color.rs index 5346d73bd0..ade9ad73ff 100644 --- a/boa_engine/src/vm/flowgraph/color.rs +++ b/boa_engine/src/vm/flowgraph/color.rs @@ -16,13 +16,21 @@ pub enum Color { /// Represents the color purple. Purple, /// Represents a RGB color. - Rgb { r: u8, g: u8, b: u8 }, + Rgb { + /// Red. + r: u8, + /// Green. + g: u8, + /// Blue. + b: u8, + }, } impl Color { /// Function for converting HSV to RGB color format. #[allow(clippy::many_single_char_names)] #[inline] + #[must_use] pub fn hsv_to_rgb(h: f64, s: f64, v: f64) -> Self { let h_i = (h * 6.0) as i64; let f = h * 6.0 - h_i as f64; @@ -50,8 +58,9 @@ impl Color { /// This funcition takes a random value and converts it to /// a pleasant to look at RGB color. #[inline] + #[must_use] pub fn from_random_number(mut random: f64) -> Self { - const GOLDEN_RATIO_CONJUGATE: f64 = 0.618033988749895; + const GOLDEN_RATIO_CONJUGATE: f64 = 0.618_033_988_749_895; random += GOLDEN_RATIO_CONJUGATE; random %= 1.0; @@ -60,6 +69,7 @@ impl Color { /// Check if the color is [`Self::None`]. #[inline] + #[must_use] pub fn is_none(&self) -> bool { *self == Self::None } diff --git a/boa_engine/src/vm/flowgraph/edge.rs b/boa_engine/src/vm/flowgraph/edge.rs index aeeb7e06d0..cd1a6cdae6 100644 --- a/boa_engine/src/vm/flowgraph/edge.rs +++ b/boa_engine/src/vm/flowgraph/edge.rs @@ -40,7 +40,7 @@ pub struct Edge { impl Edge { /// Construct a new edge. #[inline] - pub(super) fn new( + pub(super) const fn new( from: usize, to: usize, label: Option>, diff --git a/boa_engine/src/vm/flowgraph/graph.rs b/boa_engine/src/vm/flowgraph/graph.rs index 67b90fa2bc..b400e98a72 100644 --- a/boa_engine/src/vm/flowgraph/graph.rs +++ b/boa_engine/src/vm/flowgraph/graph.rs @@ -3,9 +3,16 @@ use crate::vm::flowgraph::{Color, Edge, EdgeStyle, EdgeType, Node, NodeShape}; /// This represents the direction of flow in the flowgraph. #[derive(Debug, Clone, Copy)] pub enum Direction { + /// Represents a top to bottom direction. TopToBottom, + + /// Represents a bottom to top direction. BottomToTop, + + /// Represents a left to right direction. LeftToRight, + + /// Represents a right to left direction. RightToLeft, } @@ -241,6 +248,7 @@ pub struct Graph { impl Graph { /// Construct a [`Graph`] #[inline] + #[must_use] pub fn new(direction: Direction) -> Self { Graph { subgraphs: Vec::default(), @@ -262,6 +270,7 @@ impl Graph { /// Output the graph into the graphviz format. #[inline] + #[must_use] pub fn to_graphviz_format(&self) -> String { let mut result = String::new(); result += "digraph {\n"; @@ -284,6 +293,7 @@ impl Graph { /// Output the graph into the mermaid format. #[inline] + #[must_use] pub fn to_mermaid_format(&self) -> String { let mut result = String::new(); let rankdir = match self.direction { diff --git a/boa_engine/src/vm/flowgraph/mod.rs b/boa_engine/src/vm/flowgraph/mod.rs index fe0324f40d..7365ee72d7 100644 --- a/boa_engine/src/vm/flowgraph/mod.rs +++ b/boa_engine/src/vm/flowgraph/mod.rs @@ -1,10 +1,8 @@ //! This module is responsible for generating the vm instruction flowgraph. -use std::mem::size_of; - -use boa_interner::{Interner, Sym}; - use crate::vm::{CodeBlock, Opcode}; +use boa_interner::{Interner, Sym}; +use std::mem::size_of; mod color; mod edge; @@ -20,11 +18,12 @@ impl CodeBlock { /// Output the [`CodeBlock`] VM instructions into a [`Graph`]. #[inline] pub fn to_graph(&self, interner: &Interner, graph: &mut SubGraph) { - let mut name = interner.resolve_expect(self.name).to_string(); // Have to remove any invalid graph chars like `<` or `>`. - if self.name == Sym::MAIN { - name = "__main__".to_string(); - } + let name = if self.name == Sym::MAIN { + "__main__".to_string() + } else { + interner.resolve_expect(self.name).to_string() + }; graph.set_label(name); diff --git a/boa_engine/src/vm/flowgraph/node.rs b/boa_engine/src/vm/flowgraph/node.rs index 2f3794eb28..36a8fb16ea 100644 --- a/boa_engine/src/vm/flowgraph/node.rs +++ b/boa_engine/src/vm/flowgraph/node.rs @@ -3,7 +3,7 @@ use crate::vm::flowgraph::Color; /// Reperesents the shape of a node in the flowgraph. #[derive(Debug, Clone, Copy)] pub enum NodeShape { - // Represents the default shape used in the graph. + /// Represents the default shape used in the graph. None, /// Represents a rectangular node shape. Record, diff --git a/boa_engine/src/vm/mod.rs b/boa_engine/src/vm/mod.rs index 4f1cc67138..08426c73e8 100644 --- a/boa_engine/src/vm/mod.rs +++ b/boa_engine/src/vm/mod.rs @@ -216,17 +216,11 @@ impl Context { format!("{}μs", duration.as_micros()), opcode.as_str(), match self.vm.stack.last() { + Some(value) if value.is_callable() => "[function]".to_string(), + Some(value) if value.is_object() => "[object]".to_string(), + Some(value) => value.display().to_string(), None => "".to_string(), - Some(value) => { - if value.is_callable() { - "[function]".to_string() - } else if value.is_object() { - "[object]".to_string() - } else { - value.display().to_string() - } - } - }, + } ); result diff --git a/boa_engine/src/vm/opcode/mod.rs b/boa_engine/src/vm/opcode/mod.rs index fe3c814e54..b7030374a0 100644 --- a/boa_engine/src/vm/opcode/mod.rs +++ b/boa_engine/src/vm/opcode/mod.rs @@ -103,6 +103,7 @@ macro_rules! generate_impl { $(,)? } ) => { + /// The opcodes of the vm. $(#[$outer])* pub enum $Type { $( @@ -118,11 +119,16 @@ macro_rules! generate_impl { /// # Safety /// /// Does not check if `u8` type is a valid `Opcode`. + #[must_use] pub unsafe fn from_raw(value: u8) -> Self { - std::mem::transmute(value) + // Safety: + // The caller is responsible for ensuring that the value is a valid opcode. + unsafe { std::mem::transmute(value) } } - pub fn as_str(self) -> &'static str { + /// Name of this opcode. + #[must_use] + pub const fn as_str(self) -> &'static str { match self { $( Self::$Variant => $Variant::NAME @@ -130,8 +136,9 @@ macro_rules! generate_impl { } } - /// Name of the profiler event for this opcode - pub fn as_instruction_str(self) -> &'static str { + /// Name of the profiler event for this opcode. + #[must_use] + pub const fn as_instruction_str(self) -> &'static str { match self { $( Self::$Variant => $Variant::INSTRUCTION