From a77c8792983d67e72760c1460523a74448571c54 Mon Sep 17 00:00:00 2001 From: tofpie <75836434+tofpie@users.noreply.github.com> Date: Fri, 1 Jan 2021 19:22:43 +0100 Subject: [PATCH] Fix spread in new and call expressions (#1021) * Fix spread in new and call expressions * Fix formatting * Add unit tests * Fix unit test Co-authored-by: tofpie --- boa/src/syntax/ast/node/call/mod.rs | 15 +++++++++--- boa/src/syntax/ast/node/new/mod.rs | 17 +++++++++++++- boa/src/syntax/ast/node/spread/mod.rs | 3 +++ boa/src/syntax/ast/node/spread/tests.rs | 31 +++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 boa/src/syntax/ast/node/spread/tests.rs diff --git a/boa/src/syntax/ast/node/call/mod.rs b/boa/src/syntax/ast/node/call/mod.rs index 3640422058..f742042339 100644 --- a/boa/src/syntax/ast/node/call/mod.rs +++ b/boa/src/syntax/ast/node/call/mod.rs @@ -1,4 +1,5 @@ use crate::{ + builtins::iterable, exec::Executable, exec::InterpreterState, gc::{Finalize, Trace}, @@ -84,11 +85,19 @@ impl Executable for Call { for arg in self.args() { if let Node::Spread(ref x) = arg { let val = x.run(context)?; - let mut vals = context.extract_array_properties(&val)?.unwrap(); - v_args.append(&mut vals); + let iterator_record = iterable::get_iterator(context, val)?; + loop { + let next = iterator_record.next(context)?; + if next.is_done() { + break; + } + let next_value = next.value(); + v_args.push(next_value.clone()); + } break; // after spread we don't accept any new arguments + } else { + v_args.push(arg.run(context)?); } - v_args.push(arg.run(context)?); } // execute the function call itself diff --git a/boa/src/syntax/ast/node/new/mod.rs b/boa/src/syntax/ast/node/new/mod.rs index 9cda100122..e67e0a7c56 100644 --- a/boa/src/syntax/ast/node/new/mod.rs +++ b/boa/src/syntax/ast/node/new/mod.rs @@ -1,4 +1,5 @@ use crate::{ + builtins::iterable, exec::Executable, gc::{Finalize, Trace}, syntax::ast::node::{Call, Node}, @@ -50,7 +51,21 @@ impl Executable for New { let func_object = self.expr().run(context)?; let mut v_args = Vec::with_capacity(self.args().len()); for arg in self.args() { - v_args.push(arg.run(context)?); + if let Node::Spread(ref x) = arg { + let val = x.run(context)?; + let iterator_record = iterable::get_iterator(context, val)?; + loop { + let next = iterator_record.next(context)?; + if next.is_done() { + break; + } + let next_value = next.value(); + v_args.push(next_value.clone()); + } + break; // after spread we don't accept any new arguments + } else { + v_args.push(arg.run(context)?); + } } match func_object { diff --git a/boa/src/syntax/ast/node/spread/mod.rs b/boa/src/syntax/ast/node/spread/mod.rs index e0802a4a91..b79292ebab 100644 --- a/boa/src/syntax/ast/node/spread/mod.rs +++ b/boa/src/syntax/ast/node/spread/mod.rs @@ -9,6 +9,9 @@ use std::fmt; #[cfg(feature = "deser")] use serde::{Deserialize, Serialize}; +#[cfg(test)] +mod tests; + /// The `spread` operator allows an iterable such as an array expression or string to be /// expanded. /// diff --git a/boa/src/syntax/ast/node/spread/tests.rs b/boa/src/syntax/ast/node/spread/tests.rs new file mode 100644 index 0000000000..4da380c622 --- /dev/null +++ b/boa/src/syntax/ast/node/spread/tests.rs @@ -0,0 +1,31 @@ +use crate::exec; + +#[test] +fn spread_with_new() { + let scenario = r#" + function F(m) { + this.m = m; + } + function f(...args) { + return new F(...args); + } + let a = f('message'); + a.m; + "#; + assert_eq!(&exec(scenario), r#""message""#); +} + +#[test] +fn spread_with_call() { + let scenario = r#" + function f(m) { + return m; + } + function g(...args) { + return f(...args); + } + let a = g('message'); + a; + "#; + assert_eq!(&exec(scenario), r#""message""#); +}