diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index ecaf544d6c..426e418f94 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -778,6 +778,44 @@ pub fn filter(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> Re construct_array(&new, &values) } +/// Array.prototype.some ( callbackfn [ , thisArg ] ) +/// +/// The some method tests whether at least one element in the array passes +/// the test implemented by the provided callback function. It returns a Boolean value, +/// true if the callback function returns a truthy value for at least one element +/// in the array. Otherwise, false. +/// +/// Caution: Calling this method on an empty array returns false for any condition! +/// +pub fn some(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue { + if args.is_empty() { + return Err(to_value( + "missing callback when calling function Array.prototype.some".to_string(), + )); + } + let callback = &args[0]; + let this_arg = if args.len() > 1 { + args[1].clone() + } else { + Gc::new(ValueData::Undefined) + }; + let mut i = 0; + let max_len: i32 = from_value(this.get_field_slice("length")).unwrap(); + let mut len = max_len; + while i < len { + let element = this.get_field_slice(&i.to_string()); + let arguments = vec![element.clone(), to_value(i), this.clone()]; + let result = interpreter.call(callback, &this_arg, arguments)?.is_true(); + if result { + return Ok(to_value(true)); + } + // the length of the array must be updated because the callback can mutate it. + len = min(max_len, from_value(this.get_field_slice("length")).unwrap()); + i += 1; + } + Ok(to_value(false)) +} + /// Create a new `Array` object pub fn create_constructor(global: &Value) -> Value { // Create Constructor @@ -812,6 +850,7 @@ pub fn create_constructor(global: &Value) -> Value { make_builtin_fn!(find, named "find", with length 1, of array_prototype); make_builtin_fn!(find_index, named "findIndex", with length 1, of array_prototype); make_builtin_fn!(slice, named "slice", with length 2, of array_prototype); + make_builtin_fn!(some, named "some", with length 2, of array_prototype); let array = to_value(array_constructor); make_builtin_fn!(is_array, named "isArray", with length 1, of array); diff --git a/boa/src/builtins/array/tests.rs b/boa/src/builtins/array/tests.rs index 71b509f0b7..a114fef293 100644 --- a/boa/src/builtins/array/tests.rs +++ b/boa/src/builtins/array/tests.rs @@ -742,3 +742,52 @@ fn filter() { String::from("0") ); } + +#[test] +fn some() { + let realm = Realm::create(); + let mut engine = Executor::new(realm); + let init = r#" + var empty = []; + + var array = [11, 23, 45]; + function lessThan10(element) { + return element > 10; + } + function greaterThan10(element) { + return element < 10; + } + + // Cases where callback mutates the array. + var appendArray = [1,2,3,4]; + function appendingCallback(elem,index,arr) { + arr.push('new'); + return elem !== "new"; + } + + var delArray = [1,2,3,4]; + function deletingCallback(elem,index,arr) { + arr.pop() + return elem < 3; + } + "#; + forward(&mut engine, init); + let result = forward(&mut engine, "array.some(lessThan10);"); + assert_eq!(result, "true"); + + let result = forward(&mut engine, "empty.some(lessThan10);"); + assert_eq!(result, "false"); + + let result = forward(&mut engine, "array.some(greaterThan10);"); + assert_eq!(result, "false"); + + let result = forward(&mut engine, "appendArray.some(appendingCallback);"); + let append_array_length = forward(&mut engine, "appendArray.length"); + assert_eq!(append_array_length, "5"); + assert_eq!(result, "true"); + + let result = forward(&mut engine, "delArray.some(deletingCallback);"); + let del_array_length = forward(&mut engine, "delArray.length"); + assert_eq!(del_array_length, "3"); + assert_eq!(result, "true"); +}