Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

array: implement array.slice #180

Merged
merged 2 commits into from
Oct 28, 2019
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
array: implement array.slice
  • Loading branch information
muskuloes committed Oct 25, 2019
commit 5c43c7e8d52f82e12dcbb3bb762b727fec661293
71 changes: 68 additions & 3 deletions src/lib/builtins/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
exec::Interpreter,
};
use gc::Gc;
use std::cmp;
use std::cmp::{max, min};

/// Utility function for creating array objects: `array_obj` can be any array with
/// prototype already set (it will be wiped and recreated from `array_contents`)
Expand Down Expand Up @@ -298,7 +298,7 @@ pub fn every(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Res
if !result {
return Ok(to_value(false));
}
len = std::cmp::min(max_len, from_value(this.get_field_slice("length")).unwrap());
len = min(max_len, from_value(this.get_field_slice("length")).unwrap());
i += 1;
}
Ok(to_value(true))
Expand Down Expand Up @@ -382,7 +382,7 @@ pub fn last_index_of(this: &Value, args: &[Value], _: &mut Interpreter) -> Resul
.expect("Error parsing \"Array.prototype.indexOf - fromIndex\" argument");

if from_idx >= 0 {
cmp::min(from_idx, len - 1)
min(from_idx, len - 1)
} else {
len + from_idx
}
Expand Down Expand Up @@ -490,6 +490,46 @@ pub fn includes_value(this: &Value, args: &[Value], _: &mut Interpreter) -> Resu
Ok(to_value(false))
}

/// Array.prototype.slice ( [begin[, end]] )
///
/// The slice method takes two arguments, start and end, and returns an array containing the
/// elements of the array from element start up to, but not including, element end (or through the
/// end of the array if end is undefined). If start is negative, it is treated as length + start
/// where length is the length of the array. If end is negative, it is treated as length + end where
/// length is the length of the array.
/// <https://tc39.es/ecma262/#sec-array.prototype.slice>
pub fn slice(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
let new_array = make_array(&to_value(Object::default()), &[], interpreter)?;
let len: i32 =
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");

let start = match args.get(0) {
Some(v) => from_value(v.clone()).expect("failed to parse argument for Array method"),
None => return Ok(this.clone()),
};
let end = match args.get(1) {
Some(v) => from_value(v.clone()).expect("failed to parse argument for Array method"),
None => len,
};

let from: i32 = if start < 0 {
max(len.wrapping_add(start), 0)
} else {
min(start, len)
};
let to: i32 = if end < 0 {
max(len.wrapping_add(end), 0)
} else {
min(end, len)
};

let span = max(to.wrapping_sub(from), 0);
for i in from..from.wrapping_add(span) {
add_to_array_object(&new_array, &[this.get_field(&i.to_string())])?;
}
Ok(new_array)
}

/// Create a new `Array` object
pub fn create_constructor(global: &Value) -> Value {
// Create Constructor
Expand Down Expand Up @@ -529,6 +569,7 @@ pub fn create_constructor(global: &Value) -> Value {
array_prototype.set_field_slice("includes", includes_func);
array_prototype.set_field_slice("indexOf", index_of_func);
array_prototype.set_field_slice("lastIndexOf", last_index_of_func);
array_prototype.set_field_slice("slice", to_value(slice as NativeFunctionData));

let array = to_value(array_constructor);
array.set_field_slice(PROTOTYPE, to_value(array_prototype.clone()));
Expand Down Expand Up @@ -929,4 +970,28 @@ mod tests {
let second_in_many = forward(&mut engine, "duplicates.includes('d')");
assert_eq!(second_in_many, String::from("false"));
}

#[test]
fn slice() {
let realm = Realm::create();
let mut engine = Executor::new(realm);
let init = r#"
let empty = [ ].slice();
let one = ["a"].slice();
let many1 = ["a", "b", "c", "d"].slice(1);
let many2 = ["a", "b", "c", "d"].slice(2, 3);
let many3 = ["a", "b", "c", "d"].slice(7);
"#;
forward(&mut engine, init);

assert_eq!(forward(&mut engine, "empty.length"), "0");
assert_eq!(forward(&mut engine, "one[0]"), "a");
assert_eq!(forward(&mut engine, "many1[0]"), "b");
assert_eq!(forward(&mut engine, "many1[1]"), "c");
assert_eq!(forward(&mut engine, "many1[2]"), "d");
assert_eq!(forward(&mut engine, "many1.length"), "3");
assert_eq!(forward(&mut engine, "many2[0]"), "c");
assert_eq!(forward(&mut engine, "many2.length"), "1");
assert_eq!(forward(&mut engine, "many3.length"), "0");
}
}