Skip to content

Commit

Permalink
More Array.prototype functions
Browse files Browse the repository at this point in the history
  • Loading branch information
HalidOdat committed Dec 15, 2021
1 parent c154284 commit ab0a708
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 14 deletions.
38 changes: 26 additions & 12 deletions boa/examples/jsarray.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,42 @@ fn main() -> Result<(), JsValue> {
let context = &mut Context::new();

// Create a new array with 2 elements.
//
// [ "Hello, world", true ]
let array = JsArrayObject::new(&[JsValue::new("Hello, world"), JsValue::new(true)], context);

assert!(!array.is_empty(context)?);

assert_eq!(array.pop(context)?, JsValue::new(true));
assert_eq!(array.pop(context)?, JsValue::new("Hello, world"));
assert_eq!(array.pop(context)?, JsValue::undefined());
assert_eq!(array.pop(context)?, JsValue::new(true)); // [ "Hello, world" ]
assert_eq!(array.pop(context)?, JsValue::new("Hello, world")); // [ ]
assert_eq!(array.pop(context)?, JsValue::undefined()); // [ ]

assert!(array.is_empty(context)?);
array.push(1, context)?; // [ 1 ]

array.push(1, context)?;
assert_eq!(array.pop(context)?, JsValue::new(1)); // [ ]
assert_eq!(array.pop(context)?, JsValue::undefined()); // [ ]

assert!(!array.is_empty(context)?);
array.push(10, context)?; // [ 10 ]
array.push(12, context)?; // [ 10, 12 ]
array.push(13, context)?; // [ 10, 12, 13 ]
array.push(14, context)?; // [ 10, 12, 13, 14 ]

array.reverse(context)?; // [ 14, 13, 12, 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));

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

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

// We can also use JsObject method `.get()` through the Deref trait
let element = array.get(0, context)?;
assert_eq!(element, JsValue::new(10));
let joined_array = array.join(Some("::".into()), context)?;
assert_eq!(joined_array, "14::false::false::10");

context
.global_object()
Expand Down
84 changes: 83 additions & 1 deletion boa/src/object/jsarray.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::ops::Deref;

use crate::{builtins::Array, object::JsObject, Context, JsResult, JsValue};
use crate::{builtins::Array, object::JsObject, Context, JsResult, JsString, JsValue};

// TODO: put in a better place.
pub trait JsObjectType: Into<JsValue> + Into<JsObject> + Deref<Target = JsObject> {}
Expand All @@ -18,6 +18,20 @@ impl JsArrayObject {
Self { inner }
}

#[inline]
pub fn from(object: JsObject, context: &mut Context) -> JsResult<Self> {
if object.borrow().is_array() {
Ok(Self { inner: object })
} else {
context.throw_type_error("object is not an Array")
}
}

#[inline]
pub fn length(&self, context: &mut Context) -> JsResult<usize> {
self.inner.length_of_array_like(context)
}

#[inline]
pub fn is_empty(&self, context: &mut Context) -> JsResult<bool> {
self.inner.length_of_array_like(context).map(|len| len == 0)
Expand All @@ -36,6 +50,74 @@ impl JsArrayObject {
Array::pop(&self.inner.clone().into(), &[], context)
}

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

#[inline]
pub fn unshift(&self, items: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
Array::shift(&self.inner.clone().into(), items, context)
}

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

#[inline]
pub fn join(&self, separator: Option<JsString>, context: &mut Context) -> JsResult<JsString> {
Array::join(&self.inner.clone().into(), &[separator.into()], context).map(|x| {
x.as_string()
.cloned()
.expect("Array.prototype.join always returns string")
})
}

#[inline]
pub fn fill<T>(
&self,
value: T,
start: Option<u32>,
end: Option<u32>,
context: &mut Context,
) -> JsResult<Self>
where
T: Into<JsValue>,
{
Array::fill(
&self.inner.clone().into(),
&[value.into(), start.into(), end.into()],
context,
)?;
Ok(self.clone())
}

#[inline]
pub fn index_of<T>(
&self,
search_element: T,
from_index: Option<u32>,
context: &mut Context,
) -> JsResult<Option<u32>>
where
T: Into<JsValue>,
{
let index = Array::index_of(
&self.inner.clone().into(),
&[search_element.into(), from_index.into()],
context,
)?
.as_number()
.expect("Array.prototype.indexOf should always return number");
if index == -1.0 {
Ok(None)
} else {
Ok(Some(index as u32))
}
}

// TODO: Other Array methods
}

Expand Down
2 changes: 1 addition & 1 deletion boa/src/value/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ where
fn from(value: Option<T>) -> Self {
match value {
Some(value) => value.into(),
None => JsValue::null(),
None => JsValue::undefined(),
}
}
}

0 comments on commit ab0a708

Please sign in to comment.