From 52bc15bc2320cd6cbc661a138ae955ceb0c9597a Mon Sep 17 00:00:00 2001 From: Anuvrat Singh Date: Thu, 7 Jul 2022 17:26:23 +0000 Subject: [PATCH] Safe wrapper for `JsSet` (#2162) This PR adds a safe wrapper around JavaScript `JsSet` from `builtins::set`, and is being tracked at #2098. Implements following methods - [x] `Set.prototype.size` - [x] `Set.prototype.add(value)` - [x] `Set.prototype.clear()` - [x] `Set.prototype.delete(value)` - [x] `Set.prototype.has(value)` - [x] `Set.prototype.forEach(callbackFn[, thisArg])` Implement wrapper for `builtins::set_iterator`, to be used by following. - [x] `Set.prototype.values()` - [x] `Set.prototype.keys()` - [x] `Set.prototype.entries()` *Note: Are there any other functions that should be added? Also adds `set_create()` and made `get_size()` public in `builtins::set`. --- boa_engine/src/bigint.rs | 2 +- boa_engine/src/builtins/array/mod.rs | 10 +- boa_engine/src/builtins/array_buffer/tests.rs | 2 +- boa_engine/src/builtins/date/mod.rs | 4 +- boa_engine/src/builtins/error/eval.rs | 2 +- boa_engine/src/builtins/error/syntax.rs | 2 +- boa_engine/src/builtins/error/uri.rs | 2 +- boa_engine/src/builtins/intl/mod.rs | 2 +- boa_engine/src/builtins/json/tests.rs | 2 +- boa_engine/src/builtins/number/mod.rs | 6 +- boa_engine/src/builtins/set/mod.rs | 31 ++- boa_engine/src/builtins/string/mod.rs | 2 +- .../typed_array/integer_indexed_object.rs | 2 +- boa_engine/src/bytecompiler.rs | 2 +- boa_engine/src/class.rs | 4 +- boa_engine/src/object/jsarray.rs | 2 +- boa_engine/src/object/jsobject.rs | 22 +++ boa_engine/src/object/jsset.rs | 185 ++++++++++++++++++ boa_engine/src/object/jsset_iterator.rs | 56 ++++++ boa_engine/src/object/mod.rs | 16 ++ boa_engine/src/object/operations.rs | 2 +- boa_engine/src/symbol.rs | 2 +- boa_engine/src/syntax/ast/op.rs | 2 +- boa_engine/src/syntax/ast/position.rs | 4 +- boa_engine/src/syntax/lexer/template.rs | 2 +- boa_engine/src/syntax/lexer/token.rs | 2 +- boa_engine/src/value/display.rs | 4 +- boa_engine/src/value/mod.rs | 8 +- boa_engine/src/value/tests.rs | 10 +- boa_examples/src/bin/classes.rs | 2 +- boa_examples/src/bin/closures.rs | 2 +- boa_examples/src/bin/jsset.rs | 53 +++++ 32 files changed, 403 insertions(+), 46 deletions(-) create mode 100644 boa_engine/src/object/jsset.rs create mode 100644 boa_engine/src/object/jsset_iterator.rs create mode 100644 boa_examples/src/bin/jsset.rs diff --git a/boa_engine/src/bigint.rs b/boa_engine/src/bigint.rs index 61463e51d0..727f56f4e7 100644 --- a/boa_engine/src/bigint.rs +++ b/boa_engine/src/bigint.rs @@ -86,7 +86,7 @@ impl JsBigInt { }) } - /// This function takes a string and conversts it to `BigInt` type. + /// This function takes a string and converts it to `BigInt` type. /// /// More information: /// - [ECMAScript reference][spec] diff --git a/boa_engine/src/builtins/array/mod.rs b/boa_engine/src/builtins/array/mod.rs index 829f601e1b..c6db96b744 100644 --- a/boa_engine/src/builtins/array/mod.rs +++ b/boa_engine/src/builtins/array/mod.rs @@ -949,8 +949,8 @@ impl Array { while lower != middle { // a. Let upper be len - lower - 1. let upper = len - lower - 1; - // Skiped: b. Let upperP be ! ToString(𝔽(upper)). - // Skiped: c. Let lowerP be ! ToString(𝔽(lower)). + // Skipped: b. Let upperP be ! ToString(𝔽(upper)). + // Skipped: c. Let lowerP be ! ToString(𝔽(lower)). // d. Let lowerExists be ? HasProperty(O, lowerP). let lower_exists = o.has_property(lower, context)?; // e. If lowerExists is true, then @@ -2369,7 +2369,7 @@ impl Array { // 10. If ySmaller is true, return 1𝔽. // 11. Return +0𝔽. - // NOTE: skipped IsLessThan because it just makes a lexicographic comparation + // NOTE: skipped IsLessThan because it just makes a lexicographic comparison // when x and y are strings Ok(x_str.cmp(&y_str)) }; @@ -2808,7 +2808,7 @@ impl Array { // 4. Else, let k be min(relativeStart, len). IntegerOrInfinity::Integer(i) => Ok(min(i, len as i64) as usize), - // Special case - postive infinity. `len` is always smaller than +inf, thus from (4) + // Special case - positive infinity. `len` is always smaller than +inf, thus from (4) IntegerOrInfinity::PositiveInfinity => Ok(len), } } @@ -2836,7 +2836,7 @@ impl Array { // Both `as` casts are safe as both variables are non-negative IntegerOrInfinity::Integer(i) => Ok(min(i, len as i64) as usize), - // Special case - postive infinity. `len` is always smaller than +inf, thus from (4) + // Special case - positive infinity. `len` is always smaller than +inf, thus from (4) IntegerOrInfinity::PositiveInfinity => Ok(len), } } diff --git a/boa_engine/src/builtins/array_buffer/tests.rs b/boa_engine/src/builtins/array_buffer/tests.rs index 8965b8145a..a78451be75 100644 --- a/boa_engine/src/builtins/array_buffer/tests.rs +++ b/boa_engine/src/builtins/array_buffer/tests.rs @@ -1,7 +1,7 @@ use super::*; #[test] -fn ut_sunnyy_day_create_byte_data_block() { +fn ut_sunny_day_create_byte_data_block() { let mut context = Context::default(); assert!(create_byte_data_block(100, &mut context).is_ok()); diff --git a/boa_engine/src/builtins/date/mod.rs b/boa_engine/src/builtins/date/mod.rs index b7c3da00b7..f4c9fce333 100644 --- a/boa_engine/src/builtins/date/mod.rs +++ b/boa_engine/src/builtins/date/mod.rs @@ -302,12 +302,12 @@ impl Date { let duration_hour = Duration::milliseconds(hour.checked_mul(MILLIS_PER_HOUR)?); let duration_minute = Duration::milliseconds(minute.checked_mul(MILLIS_PER_MINUTE)?); let duration_second = Duration::milliseconds(second.checked_mul(MILLIS_PER_SECOND)?); - let duration_milisecond = Duration::milliseconds(millisecond); + let duration_millisecond = Duration::milliseconds(millisecond); let duration = duration_hour .checked_add(&duration_minute)? .checked_add(&duration_second)? - .checked_add(&duration_milisecond)?; + .checked_add(&duration_millisecond)?; NaiveDate::from_ymd_opt(year, month + 1, day + 1) .and_then(|dt| dt.and_hms(0, 0, 0).checked_add_signed(duration)) diff --git a/boa_engine/src/builtins/error/eval.rs b/boa_engine/src/builtins/error/eval.rs index 4cade0f185..563749207c 100644 --- a/boa_engine/src/builtins/error/eval.rs +++ b/boa_engine/src/builtins/error/eval.rs @@ -25,7 +25,7 @@ use tap::{Conv, Pipe}; use super::Error; -/// JavaScript `EvalError` impleentation. +/// JavaScript `EvalError` implementation. #[derive(Debug, Clone, Copy)] pub(crate) struct EvalError; diff --git a/boa_engine/src/builtins/error/syntax.rs b/boa_engine/src/builtins/error/syntax.rs index d0d2a33bcd..c7dfb012d3 100644 --- a/boa_engine/src/builtins/error/syntax.rs +++ b/boa_engine/src/builtins/error/syntax.rs @@ -25,7 +25,7 @@ use tap::{Conv, Pipe}; use super::Error; -/// JavaScript `SyntaxError` impleentation. +/// JavaScript `SyntaxError` implementation. #[derive(Debug, Clone, Copy)] pub(crate) struct SyntaxError; diff --git a/boa_engine/src/builtins/error/uri.rs b/boa_engine/src/builtins/error/uri.rs index f00866d2a7..21469f338d 100644 --- a/boa_engine/src/builtins/error/uri.rs +++ b/boa_engine/src/builtins/error/uri.rs @@ -24,7 +24,7 @@ use tap::{Conv, Pipe}; use super::Error; -/// JavaScript `URIError` impleentation. +/// JavaScript `URIError` implementation. #[derive(Debug, Clone, Copy)] pub(crate) struct UriError; diff --git a/boa_engine/src/builtins/intl/mod.rs b/boa_engine/src/builtins/intl/mod.rs index d4a23ee12c..889d31e831 100644 --- a/boa_engine/src/builtins/intl/mod.rs +++ b/boa_engine/src/builtins/intl/mod.rs @@ -824,7 +824,7 @@ pub(crate) fn default_number_option( /// Abstract operation `CanonicalizeUnicodeLocaleId ( locale )`. /// -/// This function differs sligthly from the specification by modifying in-place +/// This function differs slightly from the specification by modifying in-place /// the provided [`Locale`] instead of creating a new canonicalized copy. /// /// More information: diff --git a/boa_engine/src/builtins/json/tests.rs b/boa_engine/src/builtins/json/tests.rs index b4779f516f..0fa74a01a5 100644 --- a/boa_engine/src/builtins/json/tests.rs +++ b/boa_engine/src/builtins/json/tests.rs @@ -139,7 +139,7 @@ fn json_stringify_array_converts_symbol_to_null() { assert_eq!(actual, expected); } #[test] -fn json_stringify_function_replacer_propogate_error() { +fn json_stringify_function_replacer_propagate_error() { let mut context = Context::default(); let actual = forward( diff --git a/boa_engine/src/builtins/number/mod.rs b/boa_engine/src/builtins/number/mod.rs index dcbf38cf72..e39ade669c 100644 --- a/boa_engine/src/builtins/number/mod.rs +++ b/boa_engine/src/builtins/number/mod.rs @@ -346,7 +346,7 @@ impl Number { /// represented by these digits is rounded using string /// manipulation. /// - Else, zeroes are appended to the string. - /// - Additionnally, sometimes the exponent was wrongly computed and + /// - Additionally, sometimes the exponent was wrongly computed and /// while up-rounding we find that we need an extra digit. When this /// happens, we return true so that the calling context can adjust /// the exponent. The string is kept at an exact length of `precision`. @@ -358,7 +358,7 @@ impl Number { let to_round = digits.split_off(precision); let mut digit = digits .pop() - .expect("already checked that lenght is bigger than precision") + .expect("already checked that length is bigger than precision") as u8; if let Some(first) = to_round.chars().next() { if first > '4' { @@ -601,7 +601,7 @@ impl Number { // Reconstruct digit. let digit_0 = (c as char) .to_digit(10) - .expect("charactre was not a valid digit"); + .expect("character was not a valid digit"); if digit_0 + 1 >= u32::from(radix) { continue; } diff --git a/boa_engine/src/builtins/set/mod.rs b/boa_engine/src/builtins/set/mod.rs index 1ff23fb706..282f2f7d3d 100644 --- a/boa_engine/src/builtins/set/mod.rs +++ b/boa_engine/src/builtins/set/mod.rs @@ -1,4 +1,4 @@ -//! This module implements the global `Set` objest. +//! This module implements the global `Set` object. //! //! The JavaScript `Set` class is a global object that is used in the construction of sets; which //! are high-level, collections of values. @@ -164,6 +164,30 @@ impl Set { Ok(set.into()) } + /// Utility for constructing `Set` objects. + pub(crate) fn set_create(prototype: Option, context: &mut Context) -> JsObject { + let prototype = + prototype.unwrap_or_else(|| context.intrinsics().constructors().set().prototype()); + + JsObject::from_proto_and_data(prototype, ObjectData::set(OrderedSet::new())) + } + + /// Utility for constructing `Set` objects from an iterator of `JsValue`'s. + pub(crate) fn create_set_from_list(elements: I, context: &mut Context) -> JsObject + where + I: IntoIterator, + { + // Create empty Set + let set = Self::set_create(None, context); + // For each element e of elements, do + for elem in elements { + Self::add(&set.clone().into(), &[elem], context) + .expect("adding new element shouldn't error out"); + } + + set + } + /// `get Set [ @@species ]` /// /// The Set[Symbol.species] accessor property returns the Set constructor. @@ -323,6 +347,7 @@ impl Set { let callback_arg = &args[0]; let this_arg = args.get_or_undefined(1); + // TODO: if condition should also check that we are not in strict mode let this_arg = if this_arg.is_undefined() { context.global_object().clone().into() @@ -417,8 +442,8 @@ impl Set { Self::get_size(this, context).map(JsValue::from) } - /// Helper function to get the size of the set. - fn get_size(set: &JsValue, context: &mut Context) -> JsResult { + /// Helper function to get the size of the `Set` object. + pub(crate) fn get_size(set: &JsValue, context: &mut Context) -> JsResult { set.as_object() .and_then(|obj| obj.borrow().as_set_ref().map(OrderedSet::size)) .ok_or_else(|| context.construct_type_error("'this' is not a Set")) diff --git a/boa_engine/src/builtins/string/mod.rs b/boa_engine/src/builtins/string/mod.rs index c5d58acf32..6033325e65 100644 --- a/boa_engine/src/builtins/string/mod.rs +++ b/boa_engine/src/builtins/string/mod.rs @@ -237,7 +237,7 @@ impl String { /// Abstract operation `thisStringValue( value )` /// - /// More informacion: + /// More information: /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#thisstringvalue 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 f36982126f..8d1e100bd6 100644 --- a/boa_engine/src/builtins/typed_array/integer_indexed_object.rs +++ b/boa_engine/src/builtins/typed_array/integer_indexed_object.rs @@ -56,7 +56,7 @@ impl IntegerIndexed { /// `IntegerIndexedObjectCreate ( prototype )` /// - /// Create a new `JsObject` from a prototype and a `IntergetIndexedObject` + /// Create a new `JsObject` from a prototype and a `IntegerIndexedObject` /// /// More information: /// - [ECMAScript reference][spec] diff --git a/boa_engine/src/bytecompiler.rs b/boa_engine/src/bytecompiler.rs index dd427fb0ac..1731147b2e 100644 --- a/boa_engine/src/bytecompiler.rs +++ b/boa_engine/src/bytecompiler.rs @@ -362,7 +362,7 @@ impl<'b> ByteCompiler<'b> { #[inline] fn pop_loop_control_info(&mut self) { - let loop_info = self.jump_info.pop().expect("no jump informatiojn found"); + let loop_info = self.jump_info.pop().expect("no jump information found"); assert!(loop_info.kind == JumpControlInfoKind::Loop); diff --git a/boa_engine/src/class.rs b/boa_engine/src/class.rs index 94f8145269..cbbb8a2eda 100644 --- a/boa_engine/src/class.rs +++ b/boa_engine/src/class.rs @@ -39,7 +39,7 @@ //! Ok(animal) //! } //! -//! /// This is where the object is intitialized. +//! /// This is where the object is initialized. //! fn init(class: &mut ClassBuilder) -> JsResult<()> { //! class.method("speak", 0, |this, _args, _ctx| { //! if let Some(object) = this.as_object() { @@ -74,7 +74,7 @@ pub trait Class: NativeObject + Sized { const NAME: &'static str; /// The amount of arguments the class `constructor` takes, default is `0`. const LENGTH: usize = 0; - /// The attibutes the class will be binded with, default is `writable`, `enumerable`, `configurable`. + /// The attributes the class will be binded with, default is `writable`, `enumerable`, `configurable`. const ATTRIBUTES: Attribute = Attribute::all(); /// The constructor of the class. diff --git a/boa_engine/src/object/jsarray.rs b/boa_engine/src/object/jsarray.rs index 4f1d0715bc..01fdab4964 100644 --- a/boa_engine/src/object/jsarray.rs +++ b/boa_engine/src/object/jsarray.rs @@ -23,7 +23,7 @@ impl JsArray { Self { inner } } - /// Create an array from a `IntoIterator` convertable object. + /// Create an array from a `IntoIterator` convertible object. #[inline] pub fn from_iter(elements: I, context: &mut Context) -> Self where diff --git a/boa_engine/src/object/jsobject.rs b/boa_engine/src/object/jsobject.rs index de34cfb93a..4f1b2f4809 100644 --- a/boa_engine/src/object/jsobject.rs +++ b/boa_engine/src/object/jsobject.rs @@ -335,6 +335,28 @@ impl JsObject { self.borrow().is_map_iterator() } + /// Checks if it is a `Set` object + /// + /// # Panics + /// + /// Panics if the object is currently mutably borrowed. + #[inline] + #[track_caller] + pub fn is_set(&self) -> bool { + self.borrow().is_set() + } + + /// Checks if it is a `SetIterator` object + /// + /// # Panics + /// + /// Panics if the object is currently mutably borrowed. + #[inline] + #[track_caller] + pub fn is_set_iterator(&self) -> bool { + self.borrow().is_set_iterator() + } + /// Checks if it's a `String` object. /// /// # Panics diff --git a/boa_engine/src/object/jsset.rs b/boa_engine/src/object/jsset.rs new file mode 100644 index 0000000000..4f521a6637 --- /dev/null +++ b/boa_engine/src/object/jsset.rs @@ -0,0 +1,185 @@ +use std::ops::Deref; + +use boa_gc::{Finalize, Trace}; + +use crate::{ + builtins::Set, + object::{JsFunction, JsObject, JsObjectType, JsSetIterator}, + Context, JsResult, JsValue, +}; + +// This is an wrapper for `JsSet` +#[derive(Debug, Clone, Trace, Finalize)] +pub struct JsSet { + inner: JsObject, +} + +impl JsSet { + /// Create a new empty set. + /// + /// Doesn't matches JavaScript `new Set()` as it doesn't takes an iterator + /// similar to Rust initialization. + #[inline] + pub fn new(context: &mut Context) -> Self { + let inner = Set::set_create(None, context); + + Self { inner } + } + + /// Returns the size of the `Set` as an integer. + /// + /// Same as JavaScript's `set.size`. + #[inline] + pub fn size(&self, context: &mut Context) -> JsResult { + Set::get_size(&self.inner.clone().into(), context) + } + + /// Appends value to the Set object. + /// Returns the Set object with added value. + /// + /// Same as JavaScript's `set.add(value)`. + #[inline] + pub fn add(&self, value: T, context: &mut Context) -> JsResult + where + T: Into, + { + self.add_items(&[value.into()], context) + } + + /// Adds slice as a single element. + /// Returns the Set object with added slice. + /// + /// Same as JavaScript's `set.add(["one", "two", "three"])` + #[inline] + pub fn add_items(&self, items: &[JsValue], context: &mut Context) -> JsResult { + Set::add(&self.inner.clone().into(), items, context) + } + + /// Removes all elements from the Set object. + /// Returns `Undefined`. + /// + /// Same as JavaScript's `set.clear()`. + #[inline] + pub fn clear(&self, context: &mut Context) -> JsResult { + Set::clear(&self.inner.clone().into(), &[JsValue::Null], context) + } + + /// Removes the element associated to the value. + /// Returns a boolean asserting whether an element was + /// successfully removed or not. + /// + /// Same as JavaScript's `set.delete(value)`. + #[inline] + pub fn delete(&self, value: T, context: &mut Context) -> JsResult + where + T: Into, + { + match Set::delete(&self.inner.clone().into(), &[value.into()], context)? { + JsValue::Boolean(bool) => Ok(bool), + _ => Err(JsValue::Undefined), + } + } + + /// Returns a boolean asserting whether an element is present + /// with the given value in the Set object or not. + /// + /// Same as JavaScript's `set.has(value)`. + #[inline] + pub fn has(&self, value: T, context: &mut Context) -> JsResult + where + T: Into, + { + match Set::has(&self.inner.clone().into(), &[value.into()], context)? { + JsValue::Boolean(bool) => Ok(bool), + _ => Err(JsValue::Undefined), + } + } + + /// Returns a new iterator object that yields the values + /// for each element in the Set object in insertion order. + /// + /// Same as JavaScript's `set.values()`. + #[inline] + pub fn values(&self, context: &mut Context) -> JsResult { + let iterator_object = Set::values(&self.inner.clone().into(), &[JsValue::Null], context)? + .get_iterator(context, None, None)?; + + JsSetIterator::from_object(iterator_object.iterator().clone(), context) + } + + /// Alias for `Set.prototype.values()` + /// Returns a new iterator object that yields the values + /// for each element in the Set object in insertion order. + /// + /// Same as JavaScript's `set.keys()`. + #[inline] + pub fn keys(&self, context: &mut Context) -> JsResult { + let iterator_object = Set::values(&self.inner.clone().into(), &[JsValue::Null], context)? + .get_iterator(context, None, None)?; + + JsSetIterator::from_object(iterator_object.iterator().clone(), context) + } + + /// Calls callbackFn once for each value present in the Set object, + /// in insertion order. + /// Returns `Undefined`. + /// + /// Same as JavaScript's `set.forEach(values)`. + #[inline] + pub fn for_each( + &self, + callback: JsFunction, + this_arg: JsValue, + context: &mut Context, + ) -> JsResult { + Set::for_each( + &self.inner.clone().into(), + &[callback.into(), this_arg], + context, + ) + } + + /// Utility: Creates `JsSet` from `JsObject`, if not a Set throw `TypeError`. + #[inline] + pub fn from_object(object: JsObject, context: &mut Context) -> JsResult { + if object.borrow().is_set() { + Ok(Self { inner: object }) + } else { + context.throw_error("Object is not a Set") + } + } + + /// Utility: Creates a `JsSet` from a `` convertible object. + #[inline] + pub fn from_iter(elements: I, context: &mut Context) -> Self + where + I: IntoIterator, + { + let inner = Set::create_set_from_list(elements, context); + Self { inner } + } +} + +impl From for JsObject { + #[inline] + fn from(o: JsSet) -> Self { + o.inner.clone() + } +} + +impl From for JsValue { + #[inline] + fn from(o: JsSet) -> Self { + o.inner.clone().into() + } +} + +impl Deref for JsSet { + type Target = JsObject; + #[inline] + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl JsObjectType for JsSet {} diff --git a/boa_engine/src/object/jsset_iterator.rs b/boa_engine/src/object/jsset_iterator.rs new file mode 100644 index 0000000000..64f06ad0f8 --- /dev/null +++ b/boa_engine/src/object/jsset_iterator.rs @@ -0,0 +1,56 @@ +use std::ops::Deref; + +use boa_gc::{Finalize, Trace}; + +use crate::{ + builtins::SetIterator, + object::{JsObject, JsObjectType}, + Context, JsResult, JsValue, +}; + +/// JavaScript `SetIterator` rust object +#[derive(Debug, Clone, Finalize, Trace)] +pub struct JsSetIterator { + inner: JsObject, +} + +impl JsSetIterator { + /// Create a `JsSetIterator` from a `JsObject`. + /// If object is not a `SetIterator`, throw `TypeError`. + pub fn from_object(object: JsObject, context: &mut Context) -> JsResult { + if object.borrow().is_set_iterator() { + Ok(Self { inner: object }) + } else { + context.throw_type_error("object is not a SetIterator") + } + } + /// Advances the `JsSetIterator` and gets the next result in the `JsSet`. + pub fn next(&self, context: &mut Context) -> JsResult { + SetIterator::next(&self.inner.clone().into(), &[JsValue::Null], context) + } +} + +impl From for JsObject { + #[inline] + fn from(o: JsSetIterator) -> Self { + o.inner.clone() + } +} + +impl From for JsValue { + #[inline] + fn from(o: JsSetIterator) -> Self { + o.inner.clone().into() + } +} + +impl Deref for JsSetIterator { + type Target = JsObject; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl JsObjectType for JsSetIterator {} diff --git a/boa_engine/src/object/mod.rs b/boa_engine/src/object/mod.rs index 4c3dd10b98..9c7c403af3 100644 --- a/boa_engine/src/object/mod.rs +++ b/boa_engine/src/object/mod.rs @@ -67,6 +67,8 @@ mod jsmap; mod jsmap_iterator; mod jsobject; mod jsproxy; +mod jsset; +mod jsset_iterator; mod jstypedarray; mod operations; mod property_map; @@ -76,6 +78,8 @@ pub use jsfunction::*; pub use jsmap::*; pub use jsmap_iterator::*; pub use jsproxy::*; +pub use jsset::*; +pub use jsset_iterator::*; pub use jstypedarray::*; pub(crate) trait JsObjectType: @@ -752,6 +756,18 @@ impl Object { ) } + /// Checks if it is an `SetIterator` object. + #[inline] + pub fn is_set_iterator(&self) -> bool { + matches!( + self.data, + ObjectData { + kind: ObjectKind::SetIterator(_), + .. + } + ) + } + #[inline] pub fn as_set_ref(&self) -> Option<&OrderedSet> { match self.data { diff --git a/boa_engine/src/object/operations.rs b/boa_engine/src/object/operations.rs index 744ef15d61..2ff5aa4bc8 100644 --- a/boa_engine/src/object/operations.rs +++ b/boa_engine/src/object/operations.rs @@ -42,7 +42,7 @@ impl IntegrityLevel { } impl JsObject { - /// Cehck if object is extensible. + /// Check if object is extensible. /// /// More information: /// - [ECMAScript reference][spec] diff --git a/boa_engine/src/symbol.rs b/boa_engine/src/symbol.rs index f869c6a0a6..7c949d9621 100644 --- a/boa_engine/src/symbol.rs +++ b/boa_engine/src/symbol.rs @@ -214,7 +214,7 @@ impl WellKnownSymbols { /// The `Symbol.toPrimitive` well known symbol. /// /// A method that converts an object to a corresponding primitive value. - /// Called by the `ToPrimitive` (`Value::to_primitve`) abstract operation. + /// Called by the `ToPrimitive` (`Value::to_primitive`) abstract operation. #[inline] pub fn to_primitive() -> JsSymbol { WELL_KNOW_SYMBOLS.with(|symbols| symbols.to_primitive.clone()) diff --git a/boa_engine/src/syntax/ast/op.rs b/boa_engine/src/syntax/ast/op.rs index 859fb7b027..b8a7da1a71 100644 --- a/boa_engine/src/syntax/ast/op.rs +++ b/boa_engine/src/syntax/ast/op.rs @@ -745,7 +745,7 @@ pub enum BinOp { /// see: [`BitOp`](enum.BitOp.html). Bit(BitOp), - /// Comparitive operation. + /// Comparative operation. /// /// see: [`CompOp`](enum.CompOp.html). Comp(CompOp), diff --git a/boa_engine/src/syntax/ast/position.rs b/boa_engine/src/syntax/ast/position.rs index 97b7168fba..c7b2a29389 100644 --- a/boa_engine/src/syntax/ast/position.rs +++ b/boa_engine/src/syntax/ast/position.rs @@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize}; /// /// Stores both the column number and the line number. /// -/// Note that spans are of the form [begining, end) i.e. that the begining position is inclusive +/// Note that spans are of the form [beginning, end) i.e. that the beginning position is inclusive /// and the end position is exclusive. See test `check_positions` from `syntax/lexer/tests.rs` for /// an example. /// @@ -168,7 +168,7 @@ mod tests { assert!(Position::new(11, 49) > Position::new(10, 50)); } - /// Checks that the position getters actually retreive correct values. + /// Checks that the position getters actually retrieve correct values. #[test] fn position_getters() { let pos = Position::new(10, 50); diff --git a/boa_engine/src/syntax/lexer/template.rs b/boa_engine/src/syntax/lexer/template.rs index 7825138c11..3985a7a77c 100644 --- a/boa_engine/src/syntax/lexer/template.rs +++ b/boa_engine/src/syntax/lexer/template.rs @@ -41,7 +41,7 @@ impl TemplateString { self.raw } - /// Creats a new cooked template string. Returns a lexer error if it fails to cook the + /// Creates a new cooked template string. Returns a lexer error if it fails to cook the /// template string. /// /// More information: diff --git a/boa_engine/src/syntax/lexer/token.rs b/boa_engine/src/syntax/lexer/token.rs index b2aeb29141..0cdae5ec61 100644 --- a/boa_engine/src/syntax/lexer/token.rs +++ b/boa_engine/src/syntax/lexer/token.rs @@ -54,7 +54,7 @@ impl Token { } } -/// Represents the type differenct types of numeric literals. +/// Represents the type different types of numeric literals. #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] #[derive(Clone, PartialEq, Debug)] pub enum Numeric { diff --git a/boa_engine/src/value/display.rs b/boa_engine/src/value/display.rs index ccc4d6049d..5430cc09bb 100644 --- a/boa_engine/src/value/display.rs +++ b/boa_engine/src/value/display.rs @@ -237,7 +237,7 @@ pub(crate) fn display_obj(v: &JsValue, print_internals: bool) -> String { }; // If the current object is referenced in a different branch, - // it will not cause an infinte printing loop, so it is safe to be printed again + // it will not cause an infinite printing loop, so it is safe to be printed again encounters.remove(&addr); let closing_indent = String::from_utf8(vec![b' '; indent.wrapping_sub(4)]) @@ -300,7 +300,7 @@ impl Display for ValueDisplay<'_> { /// This is different from the ECMAScript compliant number to string, in the printing of `-0`. /// -/// This function prints `-0` as `-0` instead of pasitive `0` as the specification says. +/// This function prints `-0` as `-0` instead of positive `0` as the specification says. /// This is done to make it easer for the user of the REPL to identify what is a `-0` vs `0`, /// since the REPL is not bound to the ECMAScript specification we can do this. fn format_rational(v: f64, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/boa_engine/src/value/mod.rs b/boa_engine/src/value/mod.rs index c69f4f528c..a1ff9dc4a9 100644 --- a/boa_engine/src/value/mod.rs +++ b/boa_engine/src/value/mod.rs @@ -211,13 +211,13 @@ impl JsValue { #[inline] #[allow(clippy::float_cmp)] pub fn is_integer(&self) -> bool { - // If it can fit in a i32 and the trucated version is + // If it can fit in a i32 and the truncated version is // equal to the original then it is an integer. - let is_racional_intiger = |n: f64| n == f64::from(n as i32); + let is_rational_integer = |n: f64| n == f64::from(n as i32); match *self { Self::Integer(_) => true, - Self::Rational(n) if is_racional_intiger(n) => true, + Self::Rational(n) if is_rational_integer(n) => true, _ => false, } } @@ -979,7 +979,7 @@ impl Default for JsValue { } } -/// The preffered type to convert an object to a primitive `Value`. +/// The preferred type to convert an object to a primitive `Value`. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum PreferredType { String, diff --git a/boa_engine/src/value/tests.rs b/boa_engine/src/value/tests.rs index 1b880c40cf..39e2bc4b4f 100644 --- a/boa_engine/src/value/tests.rs +++ b/boa_engine/src/value/tests.rs @@ -612,7 +612,7 @@ fn to_primitive() { } /// Test cyclic conversions that previously caused stack overflows -/// Relevant mitigations for these are in `JsObject::ordinary_to_primitive` and +/// Relevant mitigation for these are in `JsObject::ordinary_to_primitive` and /// `JsObject::to_json` mod cyclic_conversions { use super::*; @@ -843,7 +843,7 @@ mod abstract_relational_comparison { } #[test] - fn negative_infnity_less_than_bigint() { + fn negative_infinity_less_than_bigint() { let mut context = Context::default(); check_comparison!(context, "-Infinity < -10000000000n" => true); check_comparison!(context, "-Infinity < (-1n << 100n)" => true); @@ -989,7 +989,7 @@ mod abstract_relational_comparison { } #[test] - fn negative_infnity_less_than_or_equal_bigint() { + fn negative_infinity_less_than_or_equal_bigint() { let mut context = Context::default(); check_comparison!(context, "-Infinity <= -10000000000n" => true); check_comparison!(context, "-Infinity <= (-1n << 100n)" => true); @@ -1138,7 +1138,7 @@ mod abstract_relational_comparison { } #[test] - fn negative_infnity_greater_than_bigint() { + fn negative_infinity_greater_than_bigint() { let mut context = Context::default(); check_comparison!(context, "-Infinity > -10000000000n" => false); check_comparison!(context, "-Infinity > (-1n << 100n)" => false); @@ -1287,7 +1287,7 @@ mod abstract_relational_comparison { } #[test] - fn negative_infnity_greater_or_equal_than_bigint() { + fn negative_infinity_greater_or_equal_than_bigint() { let mut context = Context::default(); check_comparison!(context, "-Infinity >= -10000000000n" => false); check_comparison!(context, "-Infinity >= (-1n << 100n)" => false); diff --git a/boa_examples/src/bin/classes.rs b/boa_examples/src/bin/classes.rs index 88c19e2d49..951284ce81 100644 --- a/boa_examples/src/bin/classes.rs +++ b/boa_examples/src/bin/classes.rs @@ -18,7 +18,7 @@ use boa_gc::{Finalize, Trace}; struct Person { /// The name of the person. name: String, - /// The age of the preson. + /// The age of the person. age: u32, } diff --git a/boa_examples/src/bin/closures.rs b/boa_examples/src/bin/closures.rs index 9eae78b4b8..2c453e4070 100644 --- a/boa_examples/src/bin/closures.rs +++ b/boa_examples/src/bin/closures.rs @@ -80,7 +80,7 @@ fn main() -> Result<(), JsValue> { println!("{message}"); println!(); - // We convert `message` into `Jsvalue` to be able to return it. + // We convert `message` into `JsValue` to be able to return it. Ok(message.into()) }, // Here is where we move `clone_variable` into the closure. diff --git a/boa_examples/src/bin/jsset.rs b/boa_examples/src/bin/jsset.rs new file mode 100644 index 0000000000..1300320dc2 --- /dev/null +++ b/boa_examples/src/bin/jsset.rs @@ -0,0 +1,53 @@ +// This example shows how to manipulate a Javascript Set using Rust code. +#![allow(clippy::bool_assert_comparison)] +use boa_engine::{object::JsSet, Context, JsValue}; + +fn main() -> Result<(), JsValue> { + // New `Context` for a new Javascript executor. + let context = &mut Context::default(); + + // Create an empty set. + let set = JsSet::new(context); + + assert_eq!(set.size(context)?, 0); + set.add(5, context)?; + assert_eq!(set.size(context)?, 1); + set.add(10, context)?; + assert_eq!(set.size(context)?, 2); + set.clear(context)?; + assert_eq!(set.size(context)?, 0); + + set.add("one", context)?; + set.add("two", context)?; + set.add("three", context)?; + + assert!(set.has("one", context)?); + assert_eq!(set.has("One", context)?, false); + + set.delete("two", context)?; + + assert_eq!(set.has("two", context)?, false); + + set.clear(context)?; + + assert_eq!(set.has("one", context)?, false); + assert_eq!(set.has("three", context)?, false); + assert_eq!(set.size(context)?, 0); + + // Add a slice into a set; + set.add_items( + &[JsValue::new(1), JsValue::new(2), JsValue::new(3)], + context, + )?; + // Will return 1, as one slice was added. + assert_eq!(set.size(context)?, 1); + + // Make a new set from a slice + let slice_set = JsSet::from_iter([JsValue::new(1), JsValue::new(2), JsValue::new(3)], context); + // Will return 3, as each element of slice was added into the set. + assert_eq!(slice_set.size(context)?, 3); + + set.clear(context)?; + + Ok(()) +}