From 08775f479d1413b602e4b26b263c5848981f4c2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bojan=20=C4=90ur=C4=91evi=C4=87?= Date: Sun, 3 Nov 2019 14:14:25 -0500 Subject: [PATCH] Array.prototype.fill method implementation (#181) * Array.prototype.fill method implementation * Added types * Fmt changes * Fill tests fixed - let to var change * Added support for string arguments * Formatting * Replaced set_field_slice with set_field * Parse args to int change * More tests and bug fixes * Removed print * Removed unnecessary else block * Calling function to get ValueData::Undefined in GC * Fmt update * Function length prop, variable name change, and one more test * Removed duplicated test case --- src/lib/builtins/array.rs | 136 +++++++++++++++++++++++++++++++++++++- 1 file changed, 135 insertions(+), 1 deletion(-) diff --git a/src/lib/builtins/array.rs b/src/lib/builtins/array.rs index a95ce96b50..cced2418d4 100644 --- a/src/lib/builtins/array.rs +++ b/src/lib/builtins/array.rs @@ -3,7 +3,7 @@ use crate::{ function::NativeFunctionData, object::{Object, ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE}, property::Property, - value::{from_value, to_value, ResultValue, Value, ValueData}, + value::{from_value, to_value, undefined, ResultValue, Value, ValueData}, }, exec::Interpreter, }; @@ -493,6 +493,40 @@ pub fn find_index(this: &Value, args: &[Value], interpreter: &mut Interpreter) - Ok(Gc::new(ValueData::Number(f64::from(-1)))) } +/// Array.prototype.fill ( value[, start[, end]] ) +/// +/// The method fills (modifies) all the elements of an array from start index (default 0) +/// to an end index (default array length) with a static value. It returns the modified array +/// +pub fn fill(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + let len: i32 = from_value(this.get_field_slice("length")).expect("Could not get argument"); + let default_value = undefined(); + let value = args.get(0).unwrap_or(&default_value); + let relative_start = args.get(1).unwrap_or(&default_value).to_num() as i32; + let relative_end_val = args.get(2).unwrap_or(&default_value); + let relative_end = if relative_end_val.is_undefined() { + len + } else { + relative_end_val.to_num() as i32 + }; + let start = if relative_start < 0 { + max(len + relative_start, 0) + } else { + min(relative_start, len) + }; + let fin = if relative_end < 0 { + max(len + relative_end, 0) + } else { + min(relative_end, len) + }; + + for i in start..fin { + this.set_field(i.to_string(), value.clone()); + } + + Ok(this.clone()) +} + pub fn includes_value(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let search_element = args .get(0) @@ -582,6 +616,8 @@ pub fn create_constructor(global: &Value) -> Value { last_index_of_func.set_field_slice("length", to_value(1_i32)); let includes_func = to_value(includes_value as NativeFunctionData); includes_func.set_field_slice("length", to_value(1_i32)); + let fill_func = to_value(fill as NativeFunctionData); + fill_func.set_field_slice("length", to_value(1_i32)); array_prototype.set_field_slice("push", push_func); array_prototype.set_field_slice("pop", to_value(pop as NativeFunctionData)); @@ -595,6 +631,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("fill", fill_func); array_prototype.set_field_slice("slice", to_value(slice as NativeFunctionData)); let array = to_value(array_constructor); @@ -958,6 +995,103 @@ mod tests { assert_eq!(second_in_many, String::from("1")); } + #[test] + fn fill() { + let realm = Realm::create(); + let mut engine = Executor::new(realm); + + forward(&mut engine, "var a = [1, 2, 3];"); + assert_eq!( + forward(&mut engine, "a.fill(4).join()"), + String::from("4,4,4") + ); + // make sure the array is modified + assert_eq!(forward(&mut engine, "a.join()"), String::from("4,4,4")); + + forward(&mut engine, "a = [1, 2, 3];"); + assert_eq!( + forward(&mut engine, "a.fill(4, '1').join()"), + String::from("1,4,4") + ); + + forward(&mut engine, "a = [1, 2, 3];"); + assert_eq!( + forward(&mut engine, "a.fill(4, 1, 2).join()"), + String::from("1,4,3") + ); + + forward(&mut engine, "a = [1, 2, 3];"); + assert_eq!( + forward(&mut engine, "a.fill(4, 1, 1).join()"), + String::from("1,2,3") + ); + + forward(&mut engine, "a = [1, 2, 3];"); + assert_eq!( + forward(&mut engine, "a.fill(4, 3, 3).join()"), + String::from("1,2,3") + ); + + forward(&mut engine, "a = [1, 2, 3];"); + assert_eq!( + forward(&mut engine, "a.fill(4, -3, -2).join()"), + String::from("4,2,3") + ); + + // TODO: uncomment when NaN support is added + // forward(&mut engine, "a = [1, 2, 3];"); + // assert_eq!( + // forward(&mut engine, "a.fill(4, NaN, NaN).join()"), + // String::from("1,2,3") + // ); + + forward(&mut engine, "a = [1, 2, 3];"); + assert_eq!( + forward(&mut engine, "a.fill(4, 3, 5).join()"), + String::from("1,2,3") + ); + + forward(&mut engine, "a = [1, 2, 3];"); + assert_eq!( + forward(&mut engine, "a.fill(4, '1.2', '2.5').join()"), + String::from("1,4,3") + ); + + forward(&mut engine, "a = [1, 2, 3];"); + assert_eq!( + forward(&mut engine, "a.fill(4, 'str').join()"), + String::from("4,4,4") + ); + + forward(&mut engine, "a = [1, 2, 3];"); + assert_eq!( + forward(&mut engine, "a.fill(4, 'str', 'str').join()"), + String::from("1,2,3") + ); + + forward(&mut engine, "a = [1, 2, 3];"); + assert_eq!( + forward(&mut engine, "a.fill(4, undefined, null).join()"), + String::from("1,2,3") + ); + + forward(&mut engine, "a = [1, 2, 3];"); + assert_eq!( + forward(&mut engine, "a.fill(4, undefined, undefined).join()"), + String::from("4,4,4") + ); + + assert_eq!( + forward(&mut engine, "a.fill().join()"), + String::from("undefined,undefined,undefined") + ); + + // test object reference + forward(&mut engine, "a = (new Array(3)).fill({});"); + forward(&mut engine, "a[0].hi = 'hi';"); + assert_eq!(forward(&mut engine, "a[1].hi"), String::from("hi")); + } + #[test] fn inclues_value() { let realm = Realm::create();