From 9766409c521af12f93ec53b68169fdafd5bd5b21 Mon Sep 17 00:00:00 2001 From: Jason Williams <936006+jasonwilliams@users.noreply.github.com> Date: Sun, 8 Mar 2020 21:45:24 +0000 Subject: [PATCH] Addition of forEach() (#268) * Addition of forEach() * fixing LLDB launcher for windows (it needs .exe to work for windows) --- .vscode/launch.json | 5 ++++ .vscode/tasks.json | 6 +++++ boa/src/builtins/array/mod.rs | 29 +++++++++++++++++++++- boa/src/builtins/array/tests.rs | 44 +++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index ef1ff5b79f..ca9e95998e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,13 +8,18 @@ "type": "lldb", "request": "launch", "name": "Launch", + "windows": { + "program": "${workspaceFolder}/target/debug/boa_cli.exe" + }, "program": "${workspaceFolder}/target/debug/boa_cli", + "args": ["${workspaceFolder}/tests/js/test.js"], "sourceLanguages": ["rust"] }, { "name": "(Windows) Launch", "type": "cppvsdbg", "request": "launch", + "args": ["${workspaceFolder}/tests/js/test.js"], "program": "${workspaceFolder}/target/debug/boa_cli.exe", "cwd": "${workspaceFolder}", "sourceFileMap": { diff --git a/.vscode/tasks.json b/.vscode/tasks.json index f40039aa0b..24985f5fed 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -12,6 +12,9 @@ "group": { "kind": "build", "isDefault": true + }, + "presentation": { + "clear": true } }, { @@ -23,6 +26,9 @@ "group": { "kind": "test", "isDefault": true + }, + "presentation": { + "clear": true } } ] diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index 1dcd95761a..3ae5751ba9 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -206,6 +206,33 @@ pub fn pop(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { Ok(pop_value) } +/// Array.prototype.forEach ( callbackFn [ , thisArg ] ) +/// +/// This method executes the provided callback function for each element in the array. +/// +pub fn for_each(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue { + if args.is_empty() { + return Err(to_value( + "Missing argument for Array.prototype.forEach".to_string(), + )); + } + + let callback_arg = args.get(0).expect("Could not get `callbackFn` argument."); + let this_arg = args.get(1).cloned().unwrap_or_else(undefined); + + let length: i32 = + from_value(this.get_field_slice("length")).expect("Could not get `length` property."); + + for i in 0..length { + let element = this.get_field_slice(&i.to_string()); + let arguments = vec![element.clone(), to_value(i), this.clone()]; + + interpreter.call(callback_arg, &this_arg, arguments)?; + } + + Ok(Gc::new(ValueData::Undefined)) +} + /// Array.prototype.join ( separator ) /// /// The elements of the array are converted to Strings, and these Strings are @@ -732,7 +759,7 @@ pub fn create_constructor(global: &Value) -> Value { make_builtin_fn!(includes_value, named "includes", with length 1, of array_prototype); make_builtin_fn!(map, named "map", with length 1, of array_prototype); make_builtin_fn!(fill, named "fill", with length 1, of array_prototype); - + make_builtin_fn!(for_each, named "forEach", with length 1, of array_prototype); make_builtin_fn!(pop, named "pop", of array_prototype); make_builtin_fn!(join, named "join", with length 1, of array_prototype); make_builtin_fn!(to_string, named "toString", of array_prototype); diff --git a/boa/src/builtins/array/tests.rs b/boa/src/builtins/array/tests.rs index 5e88ff5f7f..ed25220360 100644 --- a/boa/src/builtins/array/tests.rs +++ b/boa/src/builtins/array/tests.rs @@ -631,3 +631,47 @@ fn slice() { assert_eq!(forward(&mut engine, "many2.length"), "1"); assert_eq!(forward(&mut engine, "many3.length"), "0"); } + +#[test] +fn for_each() { + let realm = Realm::create(); + let mut engine = Executor::new(realm); + let init = r#" + var a = [2, 3, 4, 5]; + var sum = 0; + var indexSum = 0; + var listLengthSum = 0; + function callingCallback(item, index, list) { + sum += item; + indexSum += index; + listLengthSum += list.length; + } + a.forEach(callingCallback); + "#; + forward(&mut engine, init); + + assert_eq!(forward(&mut engine, "sum"), "14"); + assert_eq!(forward(&mut engine, "indexSum"), "6"); + assert_eq!(forward(&mut engine, "listLengthSum"), "16"); +} + +#[test] +fn for_each_push_value() { + let realm = Realm::create(); + let mut engine = Executor::new(realm); + let init = r#" + var a = [1, 2, 3, 4]; + function callingCallback(item, index, list) { + list.push(item * 2); + } + a.forEach(callingCallback); + "#; + forward(&mut engine, init); + + // [ 1, 2, 3, 4, 2, 4, 6, 8 ] + assert_eq!(forward(&mut engine, "a.length"), "8"); + assert_eq!(forward(&mut engine, "a[4]"), "2"); + assert_eq!(forward(&mut engine, "a[5]"), "4"); + assert_eq!(forward(&mut engine, "a[6]"), "6"); + assert_eq!(forward(&mut engine, "a[7]"), "8"); +}