From 092aeafdc0ee147ee7fda88adffac26a873c5434 Mon Sep 17 00:00:00 2001 From: camc314 <18101008+camc314@users.noreply.github.com> Date: Mon, 6 Jan 2025 02:21:27 +0000 Subject: [PATCH] feat(minifier): flatten spread args in call expressions (#8266) --- .../peephole_substitute_alternate_syntax.rs | 79 ++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/crates/oxc_minifier/src/ast_passes/peephole_substitute_alternate_syntax.rs b/crates/oxc_minifier/src/ast_passes/peephole_substitute_alternate_syntax.rs index 7c48e1ec21e43..328fb0609bcf2 100644 --- a/crates/oxc_minifier/src/ast_passes/peephole_substitute_alternate_syntax.rs +++ b/crates/oxc_minifier/src/ast_passes/peephole_substitute_alternate_syntax.rs @@ -114,8 +114,10 @@ impl<'a> Traverse<'a> for PeepholeSubstituteAlternateSyntax { } } - fn exit_call_expression(&mut self, _expr: &mut CallExpression<'a>, _ctx: &mut TraverseCtx<'a>) { + fn exit_call_expression(&mut self, expr: &mut CallExpression<'a>, ctx: &mut TraverseCtx<'a>) { self.in_define_export = false; + + self.try_compress_call_expression_arguments(expr, ctx); } fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { @@ -768,6 +770,68 @@ impl<'a, 'b> PeepholeSubstituteAlternateSyntax { } } } + + // `foo(...[1,2,3])` -> `foo(1,2,3)` + fn try_compress_call_expression_arguments( + &mut self, + node: &mut CallExpression<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + let (new_size, should_fold) = + node.arguments.iter().fold((0, false), |(mut new_size, mut should_fold), arg| { + new_size += if let Argument::SpreadElement(spread_el) = arg { + if let Expression::ArrayExpression(array_expr) = &spread_el.argument { + should_fold = true; + array_expr.elements.len() + } else { + 1 + } + } else { + 1 + }; + + (new_size, should_fold) + }); + + if should_fold { + let old_args = + std::mem::replace(&mut node.arguments, ctx.ast.vec_with_capacity(new_size)); + let new_args = &mut node.arguments; + + for arg in old_args { + if let Argument::SpreadElement(mut spread_el) = arg { + if let Expression::ArrayExpression(array_expr) = &mut spread_el.argument { + for el in array_expr.elements.iter_mut() { + match el { + ArrayExpressionElement::SpreadElement(spread_el) => { + new_args.push(ctx.ast.argument_spread_element( + spread_el.span, + ctx.ast.move_expression(&mut spread_el.argument), + )); + } + ArrayExpressionElement::Elision(elision) => { + new_args.push(ctx.ast.void_0(elision.span).into()); + } + match_expression!(ArrayExpressionElement) => { + new_args.push( + ctx.ast.move_expression(el.to_expression_mut()).into(), + ); + } + } + } + } else { + new_args.push(ctx.ast.argument_spread_element( + spread_el.span, + ctx.ast.move_expression(&mut spread_el.argument), + )); + } + } else { + new_args.push(arg); + } + } + self.changed = true; + } + } } /// Port from @@ -1296,4 +1360,17 @@ mod test { "class F { accessor 0 = _; accessor a = _; accessor 1 = _; accessor b = _; accessor 'c.c' = _; accessor '1.1' = _; accessor '😊' = _; accessor 'd.d' = _ }" ); } + + #[test] + fn fold_function_spread_args() { + test_same("f(...a)"); + test_same("f(...a, ...b)"); + test_same("f(...a, b, ...c)"); + + test("f(...[])", "f()"); + test("f(...[1])", "f(1)"); + test("f(...[1, 2])", "f(1, 2)"); + test("f(...[1,,,3])", "f(1, void 0, void 0, 3)"); + test("f(a, ...[])", "f(a)"); + } }