Skip to content

Commit

Permalink
Add more Array.prototype methods
Browse files Browse the repository at this point in the history
  • Loading branch information
HalidOdat committed Feb 16, 2022
1 parent 86b653b commit af59c00
Show file tree
Hide file tree
Showing 2 changed files with 242 additions and 11 deletions.
67 changes: 56 additions & 11 deletions boa/examples/jsarray.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// This example goes into the details on how to pass closures as functions
// inside Rust and call them from Javascript.

use boa::{object::JsArray, Context, JsValue};
use boa::{
object::{FunctionBuilder, JsArray},
Context, JsValue,
};

fn main() -> Result<(), JsValue> {
// We create a new `Context` to create a new Javascript executor.
Expand All @@ -26,29 +26,74 @@ fn main() -> Result<(), JsValue> {
array.push_items(
&[
JsValue::new(10),
JsValue::new(11),
JsValue::new(12),
JsValue::new(13),
JsValue::new(14),
],
context,
)?; // [ 10, 12, 13, 14 ]
)?; // [ 10, 11, 12, 13, 14 ]

array.reverse(context)?; // [ 14, 13, 12, 10 ]
array.reverse(context)?; // [ 14, 13, 12, 11, 10 ]

assert_eq!(array.index_of(12, None, context)?, Some(2));

// We can also use JsObject method `.get()` through the Deref trait.
let element = array.get(0, context)?; // 0
assert_eq!(element, JsValue::new(14));
let element = array.get(2, context)?; // array[ 0 ]
assert_eq!(element, JsValue::new(12));
// Or we can use the `.at(index)` method.
assert_eq!(array.at(0, context)?, JsValue::new(14)); // first element
assert_eq!(array.at(-1, context)?, JsValue::new(10)); // last element

// Join the array with an optional separator (default ",").
let joined_array = array.join(None, context)?;
assert_eq!(joined_array, "14,13,12,10");
assert_eq!(joined_array, "14,13,12,11,10");

array.fill(false, Some(1), Some(3), context)?;
array.fill(false, Some(1), Some(4), context)?;

let joined_array = array.join(Some("::".into()), context)?;
assert_eq!(joined_array, "14::false::false::10");
assert_eq!(joined_array, "14::false::false::false::10");

let filter_callback = FunctionBuilder::native(context, |_this, args, _context| {
Ok(args.get(0).cloned().unwrap_or_default().is_number().into())
})
.build();

let map_callback = FunctionBuilder::native(context, |_this, args, context| {
args.get(0)
.cloned()
.unwrap_or_default()
.pow(&JsValue::new(2), context)
})
.build();

let mut data = Vec::new();
for i in 1..=5 {
data.push(JsValue::new(i));
}
let another_array = JsArray::new(&data, context); // [ 1, 2, 3, 4, 5]

let chained_array = array // [ 14, false, false, false, 10 ]
.filter(filter_callback, None, context)? // [ 14, 10 ]
.map(map_callback, None, context)? // [ 196, 100 ]
.sort(None, context)? // [ 100, 196 ]
.concat(&[another_array.into()], context)? // [ 100, 196, 1, 2, 3, 4, 5 ]
.slice(Some(1), Some(5), context)?; // [ 196, 1, 2, 3 ]

assert_eq!(chained_array.join(None, context)?, "196,1,2,3");

let reduce_callback = FunctionBuilder::native(context, |_this, args, context| {
let accumulator = args.get(0).cloned().unwrap_or_default();
let value = args.get(1).cloned().unwrap_or_default();

accumulator.add(&value, context)
})
.build();

assert_eq!(
chained_array.reduce(reduce_callback, Some(JsValue::new(0)), context)?,
JsValue::new(202)
);

context
.global_object()
Expand Down
186 changes: 186 additions & 0 deletions boa/src/object/jsarray.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ impl JsArray {
Array::pop(&self.inner.clone().into(), &[], context)
}

#[inline]
pub fn at<T>(&self, index: T, context: &mut Context) -> JsResult<JsValue>
where
T: Into<i64>,
{
Array::at(&self.inner.clone().into(), &[index.into().into()], context)
}

#[inline]
pub fn shift(&self, context: &mut Context) -> JsResult<JsValue> {
Array::shift(&self.inner.clone().into(), &[], context)
Expand All @@ -85,6 +93,16 @@ impl JsArray {
Ok(self.clone())
}

#[inline]
pub fn concat(&self, items: &[JsValue], context: &mut Context) -> JsResult<Self> {
let object = Array::concat(&self.inner.clone().into(), items, context)?
.as_object()
.cloned()
.expect("Array.prototype.filter should always return object");

Self::from(object, context)
}

#[inline]
pub fn join(&self, separator: Option<JsString>, context: &mut Context) -> JsResult<JsString> {
Array::join(&self.inner.clone().into(), &[separator.into()], context).map(|x| {
Expand Down Expand Up @@ -138,6 +156,174 @@ impl JsArray {
Ok(Some(index as u32))
}
}

#[inline]
pub fn last_index_of<T>(
&self,
search_element: T,
from_index: Option<u32>,
context: &mut Context,
) -> JsResult<Option<u32>>
where
T: Into<JsValue>,
{
let index = Array::last_index_of(
&self.inner.clone().into(),
&[search_element.into(), from_index.into()],
context,
)?
.as_number()
.expect("Array.prototype.lastIndexOf should always return number");

#[allow(clippy::float_cmp)]
if index == -1.0 {
Ok(None)
} else {
Ok(Some(index as u32))
}
}

#[inline]
pub fn find(
&self,
predicate: JsObject,
this_arg: Option<JsValue>,
context: &mut Context,
) -> JsResult<JsValue> {
Array::find(
&self.inner.clone().into(),
&[predicate.into(), this_arg.into()],
context,
)
}

#[inline]
pub fn filter(
&self,
callback: JsObject,
this_arg: Option<JsValue>,
context: &mut Context,
) -> JsResult<Self> {
let object = Array::filter(
&self.inner.clone().into(),
&[callback.into(), this_arg.into()],
context,
)?
.as_object()
.cloned()
.expect("Array.prototype.filter should always return object");

Self::from(object, context)
}

#[inline]
pub fn map(
&self,
callback: JsObject,
this_arg: Option<JsValue>,
context: &mut Context,
) -> JsResult<Self> {
let object = Array::map(
&self.inner.clone().into(),
&[callback.into(), this_arg.into()],
context,
)?
.as_object()
.cloned()
.expect("Array.prototype.map should always return object");

Self::from(object, context)
}

#[inline]
pub fn every(
&self,
callback: JsObject,
this_arg: Option<JsValue>,
context: &mut Context,
) -> JsResult<bool> {
let result = Array::every(
&self.inner.clone().into(),
&[callback.into(), this_arg.into()],
context,
)?
.as_boolean()
.expect("Array.prototype.every should always return boolean");

Ok(result)
}

#[inline]
pub fn some(
&self,
callback: JsObject,
this_arg: Option<JsValue>,
context: &mut Context,
) -> JsResult<bool> {
let result = Array::some(
&self.inner.clone().into(),
&[callback.into(), this_arg.into()],
context,
)?
.as_boolean()
.expect("Array.prototype.some should always return boolean");

Ok(result)
}

#[inline]
pub fn sort(&self, compare_fn: Option<JsObject>, context: &mut Context) -> JsResult<Self> {
Array::sort(&self.inner.clone().into(), &[compare_fn.into()], context)?;

Ok(self.clone())
}

#[inline]
pub fn slice(
&self,
start: Option<u32>,
end: Option<u32>,
context: &mut Context,
) -> JsResult<Self> {
let object = Array::slice(
&self.inner.clone().into(),
&[start.into(), end.into()],
context,
)?
.as_object()
.cloned()
.expect("Array.prototype.slice should always return object");

Self::from(object, context)
}

#[inline]
pub fn reduce(
&self,
callback: JsObject,
initial_value: Option<JsValue>,
context: &mut Context,
) -> JsResult<JsValue> {
Array::reduce(
&self.inner.clone().into(),
&[callback.into(), initial_value.into()],
context,
)
}

#[inline]
pub fn reduce_right(
&self,
callback: JsObject,
initial_value: Option<JsValue>,
context: &mut Context,
) -> JsResult<JsValue> {
Array::reduce_right(
&self.inner.clone().into(),
&[callback.into(), initial_value.into()],
context,
)
}
}

impl From<JsArray> for JsObject {
Expand Down

0 comments on commit af59c00

Please sign in to comment.