Browse Source

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`.
pull/2173/head
Anuvrat Singh 2 years ago
parent
commit
52bc15bc23
  1. 2
      boa_engine/src/bigint.rs
  2. 10
      boa_engine/src/builtins/array/mod.rs
  3. 2
      boa_engine/src/builtins/array_buffer/tests.rs
  4. 4
      boa_engine/src/builtins/date/mod.rs
  5. 2
      boa_engine/src/builtins/error/eval.rs
  6. 2
      boa_engine/src/builtins/error/syntax.rs
  7. 2
      boa_engine/src/builtins/error/uri.rs
  8. 2
      boa_engine/src/builtins/intl/mod.rs
  9. 2
      boa_engine/src/builtins/json/tests.rs
  10. 6
      boa_engine/src/builtins/number/mod.rs
  11. 31
      boa_engine/src/builtins/set/mod.rs
  12. 2
      boa_engine/src/builtins/string/mod.rs
  13. 2
      boa_engine/src/builtins/typed_array/integer_indexed_object.rs
  14. 2
      boa_engine/src/bytecompiler.rs
  15. 4
      boa_engine/src/class.rs
  16. 2
      boa_engine/src/object/jsarray.rs
  17. 22
      boa_engine/src/object/jsobject.rs
  18. 185
      boa_engine/src/object/jsset.rs
  19. 56
      boa_engine/src/object/jsset_iterator.rs
  20. 16
      boa_engine/src/object/mod.rs
  21. 2
      boa_engine/src/object/operations.rs
  22. 2
      boa_engine/src/symbol.rs
  23. 2
      boa_engine/src/syntax/ast/op.rs
  24. 4
      boa_engine/src/syntax/ast/position.rs
  25. 2
      boa_engine/src/syntax/lexer/template.rs
  26. 2
      boa_engine/src/syntax/lexer/token.rs
  27. 4
      boa_engine/src/value/display.rs
  28. 8
      boa_engine/src/value/mod.rs
  29. 10
      boa_engine/src/value/tests.rs
  30. 2
      boa_examples/src/bin/classes.rs
  31. 2
      boa_examples/src/bin/closures.rs
  32. 53
      boa_examples/src/bin/jsset.rs

2
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: /// More information:
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]

10
boa_engine/src/builtins/array/mod.rs

@ -949,8 +949,8 @@ impl Array {
while lower != middle { while lower != middle {
// a. Let upper be len - lower - 1. // a. Let upper be len - lower - 1.
let upper = len - lower - 1; let upper = len - lower - 1;
// Skiped: b. Let upperP be ! ToString(𝔽(upper)). // Skipped: b. Let upperP be ! ToString(𝔽(upper)).
// Skiped: c. Let lowerP be ! ToString(𝔽(lower)). // Skipped: c. Let lowerP be ! ToString(𝔽(lower)).
// d. Let lowerExists be ? HasProperty(O, lowerP). // d. Let lowerExists be ? HasProperty(O, lowerP).
let lower_exists = o.has_property(lower, context)?; let lower_exists = o.has_property(lower, context)?;
// e. If lowerExists is true, then // e. If lowerExists is true, then
@ -2369,7 +2369,7 @@ impl Array {
// 10. If ySmaller is true, return 1𝔽. // 10. If ySmaller is true, return 1𝔽.
// 11. Return +0𝔽. // 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 // when x and y are strings
Ok(x_str.cmp(&y_str)) Ok(x_str.cmp(&y_str))
}; };
@ -2808,7 +2808,7 @@ impl Array {
// 4. Else, let k be min(relativeStart, len). // 4. Else, let k be min(relativeStart, len).
IntegerOrInfinity::Integer(i) => Ok(min(i, len as i64) as usize), 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), IntegerOrInfinity::PositiveInfinity => Ok(len),
} }
} }
@ -2836,7 +2836,7 @@ impl Array {
// Both `as` casts are safe as both variables are non-negative // Both `as` casts are safe as both variables are non-negative
IntegerOrInfinity::Integer(i) => Ok(min(i, len as i64) as usize), 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), IntegerOrInfinity::PositiveInfinity => Ok(len),
} }
} }

2
boa_engine/src/builtins/array_buffer/tests.rs

@ -1,7 +1,7 @@
use super::*; use super::*;
#[test] #[test]
fn ut_sunnyy_day_create_byte_data_block() { fn ut_sunny_day_create_byte_data_block() {
let mut context = Context::default(); let mut context = Context::default();
assert!(create_byte_data_block(100, &mut context).is_ok()); assert!(create_byte_data_block(100, &mut context).is_ok());

4
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_hour = Duration::milliseconds(hour.checked_mul(MILLIS_PER_HOUR)?);
let duration_minute = Duration::milliseconds(minute.checked_mul(MILLIS_PER_MINUTE)?); let duration_minute = Duration::milliseconds(minute.checked_mul(MILLIS_PER_MINUTE)?);
let duration_second = Duration::milliseconds(second.checked_mul(MILLIS_PER_SECOND)?); 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 let duration = duration_hour
.checked_add(&duration_minute)? .checked_add(&duration_minute)?
.checked_add(&duration_second)? .checked_add(&duration_second)?
.checked_add(&duration_milisecond)?; .checked_add(&duration_millisecond)?;
NaiveDate::from_ymd_opt(year, month + 1, day + 1) NaiveDate::from_ymd_opt(year, month + 1, day + 1)
.and_then(|dt| dt.and_hms(0, 0, 0).checked_add_signed(duration)) .and_then(|dt| dt.and_hms(0, 0, 0).checked_add_signed(duration))

2
boa_engine/src/builtins/error/eval.rs

@ -25,7 +25,7 @@ use tap::{Conv, Pipe};
use super::Error; use super::Error;
/// JavaScript `EvalError` impleentation. /// JavaScript `EvalError` implementation.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub(crate) struct EvalError; pub(crate) struct EvalError;

2
boa_engine/src/builtins/error/syntax.rs

@ -25,7 +25,7 @@ use tap::{Conv, Pipe};
use super::Error; use super::Error;
/// JavaScript `SyntaxError` impleentation. /// JavaScript `SyntaxError` implementation.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub(crate) struct SyntaxError; pub(crate) struct SyntaxError;

2
boa_engine/src/builtins/error/uri.rs

@ -24,7 +24,7 @@ use tap::{Conv, Pipe};
use super::Error; use super::Error;
/// JavaScript `URIError` impleentation. /// JavaScript `URIError` implementation.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub(crate) struct UriError; pub(crate) struct UriError;

2
boa_engine/src/builtins/intl/mod.rs

@ -824,7 +824,7 @@ pub(crate) fn default_number_option(
/// Abstract operation `CanonicalizeUnicodeLocaleId ( locale )`. /// 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. /// the provided [`Locale`] instead of creating a new canonicalized copy.
/// ///
/// More information: /// More information:

2
boa_engine/src/builtins/json/tests.rs

@ -139,7 +139,7 @@ fn json_stringify_array_converts_symbol_to_null() {
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
#[test] #[test]
fn json_stringify_function_replacer_propogate_error() { fn json_stringify_function_replacer_propagate_error() {
let mut context = Context::default(); let mut context = Context::default();
let actual = forward( let actual = forward(

6
boa_engine/src/builtins/number/mod.rs

@ -346,7 +346,7 @@ impl Number {
/// represented by these digits is rounded using string /// represented by these digits is rounded using string
/// manipulation. /// manipulation.
/// - Else, zeroes are appended to the string. /// - 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 /// while up-rounding we find that we need an extra digit. When this
/// happens, we return true so that the calling context can adjust /// happens, we return true so that the calling context can adjust
/// the exponent. The string is kept at an exact length of `precision`. /// 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 to_round = digits.split_off(precision);
let mut digit = digits let mut digit = digits
.pop() .pop()
.expect("already checked that lenght is bigger than precision") .expect("already checked that length is bigger than precision")
as u8; as u8;
if let Some(first) = to_round.chars().next() { if let Some(first) = to_round.chars().next() {
if first > '4' { if first > '4' {
@ -601,7 +601,7 @@ impl Number {
// Reconstruct digit. // Reconstruct digit.
let digit_0 = (c as char) let digit_0 = (c as char)
.to_digit(10) .to_digit(10)
.expect("charactre was not a valid digit"); .expect("character was not a valid digit");
if digit_0 + 1 >= u32::from(radix) { if digit_0 + 1 >= u32::from(radix) {
continue; continue;
} }

31
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 //! The JavaScript `Set` class is a global object that is used in the construction of sets; which
//! are high-level, collections of values. //! are high-level, collections of values.
@ -164,6 +164,30 @@ impl Set {
Ok(set.into()) Ok(set.into())
} }
/// Utility for constructing `Set` objects.
pub(crate) fn set_create(prototype: Option<JsObject>, 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<I>(elements: I, context: &mut Context) -> JsObject
where
I: IntoIterator<Item = JsValue>,
{
// 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 ]` /// `get Set [ @@species ]`
/// ///
/// The Set[Symbol.species] accessor property returns the Set constructor. /// The Set[Symbol.species] accessor property returns the Set constructor.
@ -323,6 +347,7 @@ impl Set {
let callback_arg = &args[0]; let callback_arg = &args[0];
let this_arg = args.get_or_undefined(1); let this_arg = args.get_or_undefined(1);
// TODO: if condition should also check that we are not in strict mode // TODO: if condition should also check that we are not in strict mode
let this_arg = if this_arg.is_undefined() { let this_arg = if this_arg.is_undefined() {
context.global_object().clone().into() context.global_object().clone().into()
@ -417,8 +442,8 @@ impl Set {
Self::get_size(this, context).map(JsValue::from) Self::get_size(this, context).map(JsValue::from)
} }
/// Helper function to get the size of the set. /// Helper function to get the size of the `Set` object.
fn get_size(set: &JsValue, context: &mut Context) -> JsResult<usize> { pub(crate) fn get_size(set: &JsValue, context: &mut Context) -> JsResult<usize> {
set.as_object() set.as_object()
.and_then(|obj| obj.borrow().as_set_ref().map(OrderedSet::size)) .and_then(|obj| obj.borrow().as_set_ref().map(OrderedSet::size))
.ok_or_else(|| context.construct_type_error("'this' is not a Set")) .ok_or_else(|| context.construct_type_error("'this' is not a Set"))

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

@ -237,7 +237,7 @@ impl String {
/// Abstract operation `thisStringValue( value )` /// Abstract operation `thisStringValue( value )`
/// ///
/// More informacion: /// More information:
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#thisstringvalue /// [spec]: https://tc39.es/ecma262/#thisstringvalue

2
boa_engine/src/builtins/typed_array/integer_indexed_object.rs

@ -56,7 +56,7 @@ impl IntegerIndexed {
/// `IntegerIndexedObjectCreate ( prototype )` /// `IntegerIndexedObjectCreate ( prototype )`
/// ///
/// Create a new `JsObject` from a prototype and a `IntergetIndexedObject` /// Create a new `JsObject` from a prototype and a `IntegerIndexedObject`
/// ///
/// More information: /// More information:
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]

2
boa_engine/src/bytecompiler.rs

@ -362,7 +362,7 @@ impl<'b> ByteCompiler<'b> {
#[inline] #[inline]
fn pop_loop_control_info(&mut self) { 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); assert!(loop_info.kind == JumpControlInfoKind::Loop);

4
boa_engine/src/class.rs

@ -39,7 +39,7 @@
//! Ok(animal) //! Ok(animal)
//! } //! }
//! //!
//! /// This is where the object is intitialized. //! /// This is where the object is initialized.
//! fn init(class: &mut ClassBuilder) -> JsResult<()> { //! fn init(class: &mut ClassBuilder) -> JsResult<()> {
//! class.method("speak", 0, |this, _args, _ctx| { //! class.method("speak", 0, |this, _args, _ctx| {
//! if let Some(object) = this.as_object() { //! if let Some(object) = this.as_object() {
@ -74,7 +74,7 @@ pub trait Class: NativeObject + Sized {
const NAME: &'static str; const NAME: &'static str;
/// The amount of arguments the class `constructor` takes, default is `0`. /// The amount of arguments the class `constructor` takes, default is `0`.
const LENGTH: usize = 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(); const ATTRIBUTES: Attribute = Attribute::all();
/// The constructor of the class. /// The constructor of the class.

2
boa_engine/src/object/jsarray.rs

@ -23,7 +23,7 @@ impl JsArray {
Self { inner } Self { inner }
} }
/// Create an array from a `IntoIterator<Item = JsValue>` convertable object. /// Create an array from a `IntoIterator<Item = JsValue>` convertible object.
#[inline] #[inline]
pub fn from_iter<I>(elements: I, context: &mut Context) -> Self pub fn from_iter<I>(elements: I, context: &mut Context) -> Self
where where

22
boa_engine/src/object/jsobject.rs

@ -335,6 +335,28 @@ impl JsObject {
self.borrow().is_map_iterator() 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. /// Checks if it's a `String` object.
/// ///
/// # Panics /// # Panics

185
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<usize> {
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<T>(&self, value: T, context: &mut Context) -> JsResult<JsValue>
where
T: Into<JsValue>,
{
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<JsValue> {
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<JsValue> {
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<T>(&self, value: T, context: &mut Context) -> JsResult<bool>
where
T: Into<JsValue>,
{
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<T>(&self, value: T, context: &mut Context) -> JsResult<bool>
where
T: Into<JsValue>,
{
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<JsSetIterator> {
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<JsSetIterator> {
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<JsValue> {
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<Self> {
if object.borrow().is_set() {
Ok(Self { inner: object })
} else {
context.throw_error("Object is not a Set")
}
}
/// Utility: Creates a `JsSet` from a `<IntoIterator<Item = JsValue>` convertible object.
#[inline]
pub fn from_iter<I>(elements: I, context: &mut Context) -> Self
where
I: IntoIterator<Item = JsValue>,
{
let inner = Set::create_set_from_list(elements, context);
Self { inner }
}
}
impl From<JsSet> for JsObject {
#[inline]
fn from(o: JsSet) -> Self {
o.inner.clone()
}
}
impl From<JsSet> 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 {}

56
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<Self> {
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<JsValue> {
SetIterator::next(&self.inner.clone().into(), &[JsValue::Null], context)
}
}
impl From<JsSetIterator> for JsObject {
#[inline]
fn from(o: JsSetIterator) -> Self {
o.inner.clone()
}
}
impl From<JsSetIterator> 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 {}

16
boa_engine/src/object/mod.rs

@ -67,6 +67,8 @@ mod jsmap;
mod jsmap_iterator; mod jsmap_iterator;
mod jsobject; mod jsobject;
mod jsproxy; mod jsproxy;
mod jsset;
mod jsset_iterator;
mod jstypedarray; mod jstypedarray;
mod operations; mod operations;
mod property_map; mod property_map;
@ -76,6 +78,8 @@ pub use jsfunction::*;
pub use jsmap::*; pub use jsmap::*;
pub use jsmap_iterator::*; pub use jsmap_iterator::*;
pub use jsproxy::*; pub use jsproxy::*;
pub use jsset::*;
pub use jsset_iterator::*;
pub use jstypedarray::*; pub use jstypedarray::*;
pub(crate) trait JsObjectType: 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] #[inline]
pub fn as_set_ref(&self) -> Option<&OrderedSet<JsValue>> { pub fn as_set_ref(&self) -> Option<&OrderedSet<JsValue>> {
match self.data { match self.data {

2
boa_engine/src/object/operations.rs

@ -42,7 +42,7 @@ impl IntegrityLevel {
} }
impl JsObject { impl JsObject {
/// Cehck if object is extensible. /// Check if object is extensible.
/// ///
/// More information: /// More information:
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]

2
boa_engine/src/symbol.rs

@ -214,7 +214,7 @@ impl WellKnownSymbols {
/// The `Symbol.toPrimitive` well known symbol. /// The `Symbol.toPrimitive` well known symbol.
/// ///
/// A method that converts an object to a corresponding primitive value. /// 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] #[inline]
pub fn to_primitive() -> JsSymbol { pub fn to_primitive() -> JsSymbol {
WELL_KNOW_SYMBOLS.with(|symbols| symbols.to_primitive.clone()) WELL_KNOW_SYMBOLS.with(|symbols| symbols.to_primitive.clone())

2
boa_engine/src/syntax/ast/op.rs

@ -745,7 +745,7 @@ pub enum BinOp {
/// see: [`BitOp`](enum.BitOp.html). /// see: [`BitOp`](enum.BitOp.html).
Bit(BitOp), Bit(BitOp),
/// Comparitive operation. /// Comparative operation.
/// ///
/// see: [`CompOp`](enum.CompOp.html). /// see: [`CompOp`](enum.CompOp.html).
Comp(CompOp), Comp(CompOp),

4
boa_engine/src/syntax/ast/position.rs

@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
/// ///
/// Stores both the column number and the line number. /// 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 /// and the end position is exclusive. See test `check_positions` from `syntax/lexer/tests.rs` for
/// an example. /// an example.
/// ///
@ -168,7 +168,7 @@ mod tests {
assert!(Position::new(11, 49) > Position::new(10, 50)); 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] #[test]
fn position_getters() { fn position_getters() {
let pos = Position::new(10, 50); let pos = Position::new(10, 50);

2
boa_engine/src/syntax/lexer/template.rs

@ -41,7 +41,7 @@ impl TemplateString {
self.raw 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. /// template string.
/// ///
/// More information: /// More information:

2
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))] #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub enum Numeric { pub enum Numeric {

4
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, // 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); encounters.remove(&addr);
let closing_indent = String::from_utf8(vec![b' '; indent.wrapping_sub(4)]) 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 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`, /// 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. /// 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 { fn format_rational(v: f64, f: &mut fmt::Formatter<'_>) -> fmt::Result {

8
boa_engine/src/value/mod.rs

@ -211,13 +211,13 @@ impl JsValue {
#[inline] #[inline]
#[allow(clippy::float_cmp)] #[allow(clippy::float_cmp)]
pub fn is_integer(&self) -> bool { 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. // 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 { match *self {
Self::Integer(_) => true, Self::Integer(_) => true,
Self::Rational(n) if is_racional_intiger(n) => true, Self::Rational(n) if is_rational_integer(n) => true,
_ => false, _ => 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)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum PreferredType { pub enum PreferredType {
String, String,

10
boa_engine/src/value/tests.rs

@ -612,7 +612,7 @@ fn to_primitive() {
} }
/// Test cyclic conversions that previously caused stack overflows /// 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` /// `JsObject::to_json`
mod cyclic_conversions { mod cyclic_conversions {
use super::*; use super::*;
@ -843,7 +843,7 @@ mod abstract_relational_comparison {
} }
#[test] #[test]
fn negative_infnity_less_than_bigint() { fn negative_infinity_less_than_bigint() {
let mut context = Context::default(); let mut context = Context::default();
check_comparison!(context, "-Infinity < -10000000000n" => true); check_comparison!(context, "-Infinity < -10000000000n" => true);
check_comparison!(context, "-Infinity < (-1n << 100n)" => true); check_comparison!(context, "-Infinity < (-1n << 100n)" => true);
@ -989,7 +989,7 @@ mod abstract_relational_comparison {
} }
#[test] #[test]
fn negative_infnity_less_than_or_equal_bigint() { fn negative_infinity_less_than_or_equal_bigint() {
let mut context = Context::default(); let mut context = Context::default();
check_comparison!(context, "-Infinity <= -10000000000n" => true); check_comparison!(context, "-Infinity <= -10000000000n" => true);
check_comparison!(context, "-Infinity <= (-1n << 100n)" => true); check_comparison!(context, "-Infinity <= (-1n << 100n)" => true);
@ -1138,7 +1138,7 @@ mod abstract_relational_comparison {
} }
#[test] #[test]
fn negative_infnity_greater_than_bigint() { fn negative_infinity_greater_than_bigint() {
let mut context = Context::default(); let mut context = Context::default();
check_comparison!(context, "-Infinity > -10000000000n" => false); check_comparison!(context, "-Infinity > -10000000000n" => false);
check_comparison!(context, "-Infinity > (-1n << 100n)" => false); check_comparison!(context, "-Infinity > (-1n << 100n)" => false);
@ -1287,7 +1287,7 @@ mod abstract_relational_comparison {
} }
#[test] #[test]
fn negative_infnity_greater_or_equal_than_bigint() { fn negative_infinity_greater_or_equal_than_bigint() {
let mut context = Context::default(); let mut context = Context::default();
check_comparison!(context, "-Infinity >= -10000000000n" => false); check_comparison!(context, "-Infinity >= -10000000000n" => false);
check_comparison!(context, "-Infinity >= (-1n << 100n)" => false); check_comparison!(context, "-Infinity >= (-1n << 100n)" => false);

2
boa_examples/src/bin/classes.rs

@ -18,7 +18,7 @@ use boa_gc::{Finalize, Trace};
struct Person { struct Person {
/// The name of the person. /// The name of the person.
name: String, name: String,
/// The age of the preson. /// The age of the person.
age: u32, age: u32,
} }

2
boa_examples/src/bin/closures.rs

@ -80,7 +80,7 @@ fn main() -> Result<(), JsValue> {
println!("{message}"); println!("{message}");
println!(); 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()) Ok(message.into())
}, },
// Here is where we move `clone_variable` into the closure. // Here is where we move `clone_variable` into the closure.

53
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(())
}
Loading…
Cancel
Save