mirror of https://github.com/boa-dev/boa.git
Browse Source
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
32 changed files with 403 additions and 46 deletions
@ -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 {} |
@ -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 {} |
@ -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…
Reference in new issue