diff --git a/crates/oxc_minifier/src/peephole/minimize_statements.rs b/crates/oxc_minifier/src/peephole/minimize_statements.rs index 60d9ffc29cacf..bab4e9fdfa451 100644 --- a/crates/oxc_minifier/src/peephole/minimize_statements.rs +++ b/crates/oxc_minifier/src/peephole/minimize_statements.rs @@ -983,6 +983,49 @@ impl<'a> PeepholeOptimizations { return Some(true); } } + Expression::AwaitExpression(await_expr) => { + if let Some(changed) = Self::substitute_single_use_symbol_in_expression( + &mut await_expr.argument, + search_for, + replacement, + replacement_has_side_effect, + ctx, + ) { + return Some(changed); + } + } + Expression::YieldExpression(yield_expr) => { + if let Some(argument) = &mut yield_expr.argument + && let Some(changed) = Self::substitute_single_use_symbol_in_expression( + argument, + search_for, + replacement, + replacement_has_side_effect, + ctx, + ) + { + return Some(changed); + } + } + Expression::ImportExpression(import_expr) => { + if let Some(changed) = Self::substitute_single_use_symbol_in_expression( + &mut import_expr.source, + search_for, + replacement, + replacement_has_side_effect, + ctx, + ) { + return Some(changed); + } + + // The "import()" expression has side effects but the side effects are + // always asynchronous so there is no way for the side effects to modify + // the replacement value. So it's ok to reorder the replacement value + // past the "import()" expression assuming everything else checks out. + if !replacement_has_side_effect && !import_expr.source.may_have_side_effects(ctx) { + return None; + } + } Expression::UnaryExpression(unary_expr) => { if unary_expr.operator != UnaryOperator::Delete && let Some(changed) = Self::substitute_single_use_symbol_in_expression( @@ -996,6 +1039,324 @@ impl<'a> PeepholeOptimizations { return Some(changed); } } + Expression::StaticMemberExpression(member_expr) => { + if let Some(changed) = Self::substitute_single_use_symbol_in_expression( + &mut member_expr.object, + search_for, + replacement, + replacement_has_side_effect, + ctx, + ) { + return Some(changed); + } + } + Expression::BinaryExpression(binary_expr) => { + if let Some(changed) = Self::substitute_single_use_symbol_in_expression( + &mut binary_expr.left, + search_for, + replacement, + replacement_has_side_effect, + ctx, + ) { + return Some(changed); + } + if let Some(changed) = Self::substitute_single_use_symbol_in_expression( + &mut binary_expr.right, + search_for, + replacement, + replacement_has_side_effect, + ctx, + ) { + return Some(changed); + } + } + Expression::AssignmentExpression(_assign_expr) => { + // TODO: requires implementing `MayHaveSideEffects` to `AssignmentTarget` + } + Expression::LogicalExpression(logical_expr) => { + if let Some(changed) = Self::substitute_single_use_symbol_in_expression( + &mut logical_expr.left, + search_for, + replacement, + replacement_has_side_effect, + ctx, + ) { + return Some(changed); + } + if let Some(changed) = Self::substitute_single_use_symbol_in_expression( + &mut logical_expr.right, + search_for, + replacement, + replacement_has_side_effect, + ctx, + ) { + return Some(changed); + } + } + Expression::PrivateInExpression(_private_in_expr) => { + // TODO: implement + } + Expression::ConditionalExpression(cond_expr) => { + if let Some(changed) = Self::substitute_single_use_symbol_in_expression( + &mut cond_expr.test, + search_for, + replacement, + replacement_has_side_effect, + ctx, + ) { + return Some(changed); + } + // Do not substitute our unconditionally-executed value into a branch + // unless the value itself has no side effects + if !replacement_has_side_effect { + // Unlike other branches in this function such as "a && b" or "a?.[b]", + // the "a ? b : c" form has potential code evaluation along both control + // flow paths. Handle this by allowing substitution into either branch. + // Side effects in one branch should not prevent the substitution into + // the other branch. + + let consequent_changed = Self::substitute_single_use_symbol_in_expression( + &mut cond_expr.consequent, + search_for, + replacement, + replacement_has_side_effect, + ctx, + ); + if consequent_changed == Some(true) { + return consequent_changed; + } + let alternate_changed = Self::substitute_single_use_symbol_in_expression( + &mut cond_expr.alternate, + search_for, + replacement, + replacement_has_side_effect, + ctx, + ); + if alternate_changed == Some(true) { + return alternate_changed; + } + // Side effects in either branch should stop us from continuing to try to + // substitute the replacement after the control flow branches merge again. + if consequent_changed == Some(false) || alternate_changed == Some(false) { + return Some(false); + } + } + } + Expression::ComputedMemberExpression(member_expr) => { + if let Some(changed) = Self::substitute_single_use_symbol_in_expression( + &mut member_expr.object, + search_for, + replacement, + replacement_has_side_effect, + ctx, + ) { + return Some(changed); + } + // Do not substitute our unconditionally-executed value into a branch + // unless the value itself has no side effects + if (!replacement_has_side_effect || !member_expr.optional) + && let Some(changed) = Self::substitute_single_use_symbol_in_expression( + &mut member_expr.expression, + search_for, + replacement, + replacement_has_side_effect, + ctx, + ) + { + return Some(changed); + } + } + Expression::PrivateFieldExpression(_private_field_expr) => { + // TODO: implement + } + Expression::CallExpression(call_expr) => { + // Don't substitute something into a call target that could change "this" + if !((replacement.is_member_expression() + || matches!(replacement, Expression::ChainExpression(_))) + && call_expr.callee.is_identifier_reference()) + { + if let Some(changed) = Self::substitute_single_use_symbol_in_expression( + &mut call_expr.callee, + search_for, + replacement, + replacement_has_side_effect, + ctx, + ) { + return Some(changed); + } + + // Do not substitute our unconditionally-executed value into a branch + // unless the value itself has no side effects + if !replacement_has_side_effect || !call_expr.optional { + for arg in &mut call_expr.arguments { + match arg { + Argument::SpreadElement(spread_elem) => { + if let Some(changed) = + Self::substitute_single_use_symbol_in_expression( + &mut spread_elem.argument, + search_for, + replacement, + replacement_has_side_effect, + ctx, + ) + { + return Some(changed); + } + // spread element may have sideeffects + return Some(false); + } + match_expression!(Argument) => { + if let Some(changed) = + Self::substitute_single_use_symbol_in_expression( + arg.to_expression_mut(), + search_for, + replacement, + replacement_has_side_effect, + ctx, + ) + { + return Some(changed); + } + } + } + } + } + } + } + Expression::NewExpression(_new_expr) => { + // TODO: implement + } + Expression::ArrayExpression(array_expr) => { + for elem in &mut array_expr.elements { + match elem { + ArrayExpressionElement::SpreadElement(spread_elem) => { + if let Some(changed) = Self::substitute_single_use_symbol_in_expression( + &mut spread_elem.argument, + search_for, + replacement, + replacement_has_side_effect, + ctx, + ) { + return Some(changed); + } + // spread element may have sideeffects + return Some(false); + } + ArrayExpressionElement::Elision(_) => {} + match_expression!(ArrayExpressionElement) => { + if let Some(changed) = Self::substitute_single_use_symbol_in_expression( + elem.to_expression_mut(), + search_for, + replacement, + replacement_has_side_effect, + ctx, + ) { + return Some(changed); + } + } + } + } + } + Expression::ObjectExpression(obj_expr) => { + for prop in &mut obj_expr.properties { + match prop { + ObjectPropertyKind::ObjectProperty(prop) => match prop.key { + PropertyKey::StaticIdentifier(_) + | PropertyKey::PrivateIdentifier(_) => { + if let Some(changed) = + Self::substitute_single_use_symbol_in_expression( + &mut prop.value, + search_for, + replacement, + replacement_has_side_effect, + ctx, + ) + { + return Some(changed); + } + } + match_expression!(PropertyKey) => { + if let Some(changed) = + Self::substitute_single_use_symbol_in_expression( + prop.key.to_expression_mut(), + search_for, + replacement, + replacement_has_side_effect, + ctx, + ) + { + return Some(changed); + } + // Stop now because computed keys have side effects + return Some(false); + } + }, + ObjectPropertyKind::SpreadProperty(prop) => { + if let Some(changed) = Self::substitute_single_use_symbol_in_expression( + &mut prop.argument, + search_for, + replacement, + replacement_has_side_effect, + ctx, + ) { + return Some(changed); + } + // Stop now because spread properties have side effects + return Some(false); + } + } + } + } + Expression::TaggedTemplateExpression(tagged_expr) => { + if let Some(changed) = Self::substitute_single_use_symbol_in_expression( + &mut tagged_expr.tag, + search_for, + replacement, + replacement_has_side_effect, + ctx, + ) { + return Some(changed); + } + for elem in &mut tagged_expr.quasi.expressions { + if let Some(changed) = Self::substitute_single_use_symbol_in_expression( + elem, + search_for, + replacement, + replacement_has_side_effect, + ctx, + ) { + return Some(changed); + } + } + } + Expression::TemplateLiteral(template_literal) => { + for elem in &mut template_literal.expressions { + if let Some(changed) = Self::substitute_single_use_symbol_in_expression( + elem, + search_for, + replacement, + replacement_has_side_effect, + ctx, + ) { + return Some(changed); + } + } + } + Expression::ChainExpression(chain_expr) => { + let mut expr = Expression::from(chain_expr.expression.take_in(ctx.ast)); + let changed = Self::substitute_single_use_symbol_in_expression( + &mut expr, + search_for, + replacement, + replacement_has_side_effect, + ctx, + ); + chain_expr.expression = expr.into_chain_element().expect("should be chain element"); + return changed; + } + Expression::SequenceExpression(_sequence_expr) => { + // TODO: implement + } _ => {} } @@ -1019,6 +1380,12 @@ impl<'a> PeepholeOptimizations { return None; } + // We can always reorder past primitive values + // TODO(sapphi-red): we may use is_literal_value after I checked if it handles edge cases properly + if replacement.is_literal() || target_expr.is_literal() { + return None; + } + // Otherwise we should stop trying to substitute past this point Some(false) } diff --git a/crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs b/crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs index ff8b0ad574800..614940c756371 100644 --- a/crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs +++ b/crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs @@ -536,15 +536,19 @@ impl<'a> PeepholeOptimizations { // Parse statement: `r[a - offset] = arguments[a];` let body_assign_expr = { - let assign = match &for_stmt.body { + let assign = match &mut for_stmt.body { Statement::ExpressionStatement(expr_stmt) => expr_stmt, - Statement::BlockStatement(block) if block.body.len() == 1 => match &block.body[0] { - Statement::ExpressionStatement(expr_stmt) => expr_stmt, - _ => return, - }, + Statement::BlockStatement(block) if block.body.len() == 1 => { + match &mut block.body[0] { + Statement::ExpressionStatement(expr_stmt) => expr_stmt, + _ => return, + } + } _ => return, }; - let Expression::AssignmentExpression(assign_expr) = &assign.expression else { return }; + let Expression::AssignmentExpression(assign_expr) = &mut assign.expression else { + return; + }; if !assign_expr.operator.is_assign() { return; } @@ -578,12 +582,13 @@ impl<'a> PeepholeOptimizations { (lhs_member_expr_obj.name, base_name, offset) }; - { - let Expression::ComputedMemberExpression(rhs_member_expr) = &body_assign_expr.right + let arguments_id = { + let Expression::ComputedMemberExpression(rhs_member_expr) = &mut body_assign_expr.right else { return; }; - let Expression::Identifier(rhs_member_expr_obj) = &rhs_member_expr.object else { + let ComputedMemberExpression { object, expression, .. } = rhs_member_expr.as_mut(); + let Expression::Identifier(rhs_member_expr_obj) = object else { return; }; if rhs_member_expr_obj.name != "arguments" @@ -591,13 +596,13 @@ impl<'a> PeepholeOptimizations { { return; } - let Expression::Identifier(rhs_member_expr_expr_id) = &rhs_member_expr.expression - else { + let Expression::Identifier(rhs_member_expr_expr_id) = expression else { return; }; if rhs_member_expr_expr_id.name != a_id_name { return; } + rhs_member_expr_obj }; // Parse update: `a++` @@ -737,7 +742,7 @@ impl<'a> PeepholeOptimizations { SPAN, ctx.ast.vec1(ctx.ast.array_expression_element_spread_element( SPAN, - ctx.ast.expression_identifier(SPAN, "arguments"), + Expression::Identifier(arguments_id.take_in_box(ctx.ast)), )), ); // wrap with `.slice(offset)` diff --git a/crates/oxc_minifier/tests/peephole/esbuild.rs b/crates/oxc_minifier/tests/peephole/esbuild.rs index c3fbbafd91ee1..37188be04e37f 100644 --- a/crates/oxc_minifier/tests/peephole/esbuild.rs +++ b/crates/oxc_minifier/tests/peephole/esbuild.rs @@ -832,51 +832,51 @@ fn js_parser_test() { ); test( "function _() { let a = foo(); return a != null ? a.b : undefined }", - "function _() { let a = foo(); return a?.b; }", + "function _() { return foo()?.b; }", ); test( "function _() { let a = foo(); return a != null ? a[b] : undefined }", - "function _() { let a = foo(); return a?.[b]; }", + "function _() { return foo()?.[b]; }", ); test( "function _() { let a = foo(); return a != null ? a(b) : undefined }", - "function _() { let a = foo(); return a?.(b); }", + "function _() { return foo()?.(b); }", ); test( "function _() { let a = foo(); return a == null ? undefined : a.b }", - "function _() { let a = foo(); return a?.b; }", + "function _() { return foo()?.b; }", ); test( "function _() { let a = foo(); return a == null ? undefined : a[b] }", - "function _() { let a = foo(); return a?.[b]; }", + "function _() { return foo()?.[b]; }", ); test( "function _() { let a = foo(); return a == null ? undefined : a(b) }", - "function _() { let a = foo(); return a?.(b); }", + "function _() { return foo()?.(b); }", ); test( "function _() { let a = foo(); return null != a ? a.b : undefined }", - "function _() { let a = foo(); return a?.b; }", + "function _() { return foo()?.b; }", ); test( "function _() { let a = foo(); return null != a ? a[b] : undefined }", - "function _() { let a = foo(); return a?.[b]; }", + "function _() { return foo()?.[b]; }", ); test( "function _() { let a = foo(); return null != a ? a(b) : undefined }", - "function _() { let a = foo(); return a?.(b); }", + "function _() { return foo()?.(b); }", ); test( "function _() { let a = foo(); return null == a ? undefined : a.b }", - "function _() { let a = foo(); return a?.b; }", + "function _() { return foo()?.b; }", ); test( "function _() { let a = foo(); return null == a ? undefined : a[b] }", - "function _() { let a = foo(); return a?.[b]; }", + "function _() { return foo()?.[b]; }", ); test( "function _() { let a = foo(); return null == a ? undefined : a(b) }", - "function _() { let a = foo(); return a?.(b); }", + "function _() { return foo()?.(b); }", ); test( "function _() { return a != null ? a.b : undefined }", @@ -888,7 +888,7 @@ fn js_parser_test() { ); test( "function _() { let a = foo(); return a != null ? b.a : undefined }", - "function _() { let a = foo(); return a == null ? void 0 : b.a; }", + "function _() { return foo() == null ? void 0 : b.a; }", ); test( "function _() { let a = foo(); return a != 0 ? a.b : undefined }", @@ -900,27 +900,27 @@ fn js_parser_test() { ); test( "function _() { let a = foo(); return a != undefined ? a.b : undefined }", - "function _() { let a = foo(); return a?.b; }", + "function _() { return foo()?.b; }", ); test( "function _() { let a = foo(); return a != null ? a?.b : undefined }", - "function _() { let a = foo(); return a?.b; }", + "function _() { return foo()?.b; }", ); test( "function _() { let a = foo(); return a != null ? a.b.c[d](e) : undefined }", - "function _() { let a = foo(); return a?.b.c[d](e); }", + "function _() { return foo()?.b.c[d](e); }", ); test( "function _() { let a = foo(); return a != null ? a?.b.c[d](e) : undefined }", - "function _() { let a = foo(); return a?.b.c[d](e); }", + "function _() { return foo()?.b.c[d](e); }", ); test( "function _() { let a = foo(); return a != null ? a.b.c?.[d](e) : undefined }", - "function _() { let a = foo(); return a?.b.c?.[d](e); }", + "function _() { return foo()?.b.c?.[d](e); }", ); test( "function _() { let a = foo(); return a != null ? a?.b.c?.[d](e) : undefined }", - "function _() { let a = foo(); return a?.b.c?.[d](e); }", + "function _() { return foo()?.b.c?.[d](e); }", ); } @@ -1693,11 +1693,6 @@ fn test_inline_single_use_variable() { "var foo; function wrapper(arg0, arg1) { let x = foo; return typeof x}", "var foo; function wrapper(arg0, arg1) { return typeof foo;}", ); -} - -#[test] -#[ignore] -fn test_inline_single_use_variable_ignored() { test( "var foo; function wrapper(arg0, arg1) { let x = foo; return `<${x}>`}", "var foo; function wrapper(arg0, arg1) { return `<${foo}>`;}", @@ -1724,7 +1719,7 @@ fn test_inline_single_use_variable_ignored() { ); test( "var foo; function wrapper(arg0, arg1) { let x = foo; return fn() + x}", - "var foo; function wrapper(arg0, arg1) { return fn() + foo;}", + "var foo; function wrapper(arg0, arg1) { let x = foo; return fn() + x;}", ); test( "var foo; function wrapper(arg0, arg1) { let x = foo; return x + undef}", @@ -1732,7 +1727,7 @@ fn test_inline_single_use_variable_ignored() { ); test( "var foo; function wrapper(arg0, arg1) { let x = foo; return undef + x}", - "var foo; function wrapper(arg0, arg1) { return undef + foo;}", + "var foo; function wrapper(arg0, arg1) { let x = foo; return undef + x;}", ); test( "function wrapper(arg0, arg1) { let x = fn(); return x + 2}", @@ -1798,54 +1793,54 @@ fn test_inline_single_use_variable_ignored() { "var foo; function wrapper(arg0, arg1) { let x = foo; x ||= 2}", "var foo; function wrapper(arg0, arg1) { let x = foo; x ||= 2;}", ); - test( - "var foo; function wrapper(arg0, arg1) { let x = foo; arg0 = x}", - "var foo; function wrapper(arg0, arg1) { arg0 = foo;}", - ); - test( - "var foo; function wrapper(arg0, arg1) { let x = foo; arg0 += x}", - "var foo; function wrapper(arg0, arg1) { arg0 += foo;}", - ); - test( - "var foo; function wrapper(arg0, arg1) { let x = foo; arg0 ||= x}", - "var foo; function wrapper(arg0, arg1) { arg0 ||= foo;}", - ); - test( - "function wrapper(arg0, arg1) { let x = fn(); arg0 = x}", - "function wrapper(arg0, arg1) { arg0 = fn();}", - ); - test( - "function wrapper(arg0, arg1) { let x = fn(); arg0 += x}", - "function wrapper(arg0, arg1) { let x = fn(); arg0 += x;}", - ); - test( - "function wrapper(arg0, arg1) { let x = fn(); arg0 ||= x}", - "function wrapper(arg0, arg1) { let x = fn(); arg0 ||= x;}", - ); - test( - "var foo; function wrapper(arg0, arg1) { let x = foo; y.z = x}", - "var foo; function wrapper(arg0, arg1) { let x = foo; y.z = x;}", - ); - test( - "var foo; function wrapper(arg0, arg1) { let x = foo; y.z += x}", - "var foo; function wrapper(arg0, arg1) { let x = foo; y.z += x;}", - ); - test( - "var foo; function wrapper(arg0, arg1) { let x = foo; y.z ||= x}", - "var foo; function wrapper(arg0, arg1) { let x = foo; y.z ||= x;}", - ); - test( - "function wrapper(arg0, arg1) { let x = fn(); y.z = x}", - "function wrapper(arg0, arg1) { let x = fn(); y.z = x;}", - ); - test( - "function wrapper(arg0, arg1) { let x = fn(); y.z += x}", - "function wrapper(arg0, arg1) { let x = fn(); y.z += x;}", - ); - test( - "function wrapper(arg0, arg1) { let x = fn(); y.z ||= x}", - "function wrapper(arg0, arg1) { let x = fn(); y.z ||= x;}", - ); + // test( + // "var foo; function wrapper(arg0, arg1) { let x = foo; arg0 = x}", + // "var foo; function wrapper(arg0, arg1) { arg0 = foo;}", + // ); + // test( + // "var foo; function wrapper(arg0, arg1) { let x = foo; arg0 += x}", + // "var foo; function wrapper(arg0, arg1) { arg0 += foo;}", + // ); + // test( + // "var foo; function wrapper(arg0, arg1) { let x = foo; arg0 ||= x}", + // "var foo; function wrapper(arg0, arg1) { arg0 ||= foo;}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = fn(); arg0 = x}", + // "function wrapper(arg0, arg1) { arg0 = fn();}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = fn(); arg0 += x}", + // "function wrapper(arg0, arg1) { let x = fn(); arg0 += x;}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = fn(); arg0 ||= x}", + // "function wrapper(arg0, arg1) { let x = fn(); arg0 ||= x;}", + // ); + // test( + // "var foo; function wrapper(arg0, arg1) { let x = foo; y.z = x}", + // "var foo; function wrapper(arg0, arg1) { let x = foo; y.z = x;}", + // ); + // test( + // "var foo; function wrapper(arg0, arg1) { let x = foo; y.z += x}", + // "var foo; function wrapper(arg0, arg1) { let x = foo; y.z += x;}", + // ); + // test( + // "var foo; function wrapper(arg0, arg1) { let x = foo; y.z ||= x}", + // "var foo; function wrapper(arg0, arg1) { let x = foo; y.z ||= x;}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = fn(); y.z = x}", + // "function wrapper(arg0, arg1) { let x = fn(); y.z = x;}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = fn(); y.z += x}", + // "function wrapper(arg0, arg1) { let x = fn(); y.z += x;}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = fn(); y.z ||= x}", + // "function wrapper(arg0, arg1) { let x = fn(); y.z ||= x;}", + // ); test( "function wrapper(arg0, arg1) { let x = arg0; return x ? y : z;}", "function wrapper(arg0, arg1) { return arg0 ? y : z;}", @@ -1962,13 +1957,21 @@ fn test_inline_single_use_variable_ignored() { "function wrapper(arg0, arg1) { let x = fn(); return arg0 ?? x;}", "function wrapper(arg0, arg1) { let x = fn(); return arg0 ?? x;}", ); + // test( + // "function wrapper(arg0, arg1) { let x = fn(); let y = x[prop]; let z = y.val; throw z}", + // "function wrapper(arg0, arg1) { throw fn()[prop].val;}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = fn(), y = x[prop], z = y.val; throw z}", + // "function wrapper(arg0, arg1) { throw fn()[prop].val;}", + // ); test( - "function wrapper(arg0, arg1) { let x = fn(); let y = x[prop]; let z = y.val; throw z}", - "function wrapper(arg0, arg1) { throw fn()[prop].val;}", + "function wrapper(arg0, arg1) { let x = fn(); let y = x[prop]; let z = y.val; return z}", + "function wrapper(arg0, arg1) { return fn()[prop].val;}", ); test( - "function wrapper(arg0, arg1) { let x = fn(), y = x[prop], z = y.val; throw z}", - "function wrapper(arg0, arg1) { throw fn()[prop].val;}", + "function wrapper(arg0, arg1) { let x = fn(), y = x[prop], z = y.val; return z}", + "function wrapper(arg0, arg1) { return fn()[prop].val;}", ); test( "var foo; function wrapper(arg0, arg1) { let x = foo; let y = ++x; return y}", @@ -1982,9 +1985,10 @@ fn test_inline_single_use_variable_ignored() { "var foo; function wrapper(arg0, arg1) { let x = foo; let y = ++x; return [x, y]}", "var foo; function wrapper(arg0, arg1) { let x = foo, y = ++x; return [x, y];}", ); + // NOTE: differently from esbuild, we assume `valueOf` does not have a sideeffect test( "var foo; function wrapper(arg0, arg1) { let x = foo; let y = {valueOf() { x = 1 }}; let z = x; return [y == 1, z]}", - "var foo; function wrapper(arg0, arg1) { let x = foo, y = { valueOf() { x = 1; } }, z = x; return [y == 1, z];}", + "var foo; function wrapper(arg0, arg1) { let x = foo; return [{ valueOf() { x = 1; } } == 1, x];}", ); test( "function wrapper(arg0, arg1) { let x = arg0; return [...x];}", @@ -2010,42 +2014,42 @@ fn test_inline_single_use_variable_ignored() { "function wrapper(arg0, arg1) { let x = arg0; return arg1(...arg1, x);}", "function wrapper(arg0, arg1) { let x = arg0; return arg1(...arg1, x);}", ); - test( - "function wrapper(arg0, arg1) { let x = arg0; arg1(x);}", - "function wrapper(arg0, arg1) { arg1(arg0);}", - ); - test( - "function wrapper(arg0, arg1) { let x = arg0; throw x;}", - "function wrapper(arg0, arg1) { throw arg0;}", - ); + // test( + // "function wrapper(arg0, arg1) { let x = arg0; arg1(x);}", + // "function wrapper(arg0, arg1) { arg1(arg0);}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = arg0; throw x;}", + // "function wrapper(arg0, arg1) { throw arg0;}", + // ); test( "function wrapper(arg0, arg1) { let x = arg0; return x;}", "function wrapper(arg0, arg1) { return arg0;}", ); - test( - "function wrapper(arg0, arg1) { let x = arg0; if (x) return 1;}", - "function wrapper(arg0, arg1) { if (arg0) return 1;}", - ); - test( - "function wrapper(arg0, arg1) { let x = arg0; switch (x) { case 0: return 1; }}", - "function wrapper(arg0, arg1) { switch (arg0) { case 0: return 1; }}", - ); - test( - "function wrapper(arg0, arg1) { let x = arg0; let y = x; return y + y;}", - "function wrapper(arg0, arg1) { let y = arg0; return y + y;}", - ); - test( - "function wrapper(arg0, arg1) { let x = arg0; do {} while (x);}", - "function wrapper(arg0, arg1) { let x = arg0; do ; while (x);}", - ); - test( - "function wrapper(arg0, arg1) { let x = arg0; while (x) return 1;}", - "function wrapper(arg0, arg1) { let x = arg0; for (; x; ) return 1;}", - ); - test( - "function wrapper(arg0, arg1) { let x = arg0; for (; x; ) return 1;}", - "function wrapper(arg0, arg1) { let x = arg0; for (; x; ) return 1;}", - ); + // test( + // "function wrapper(arg0, arg1) { let x = arg0; if (x) return 1;}", + // "function wrapper(arg0, arg1) { if (arg0) return 1;}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = arg0; switch (x) { case 0: return 1; }}", + // "function wrapper(arg0, arg1) { switch (arg0) { case 0: return 1; }}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = arg0; let y = x; return y + y;}", + // "function wrapper(arg0, arg1) { let y = arg0; return y + y;}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = arg0; do {} while (x);}", + // "function wrapper(arg0, arg1) { let x = arg0; do ; while (x);}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = arg0; while (x) return 1;}", + // "function wrapper(arg0, arg1) { let x = arg0; for (; x; ) return 1;}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = arg0; for (; x; ) return 1;}", + // "function wrapper(arg0, arg1) { let x = arg0; for (; x; ) return 1;}", + // ); test( "function wrapper(arg0, arg1) { let x = arg0; return arg1?.[x];}", "function wrapper(arg0, arg1) { return arg1?.[arg0];}", @@ -2170,10 +2174,6 @@ fn test_inline_single_use_variable_ignored() { "function wrapper(arg0, arg1) { let x = arg0; return arg1`a${x}b`;}", "function wrapper(arg0, arg1) { return arg1`a${arg0}b`;}", ); - test( - "function wrapper(arg0, arg1) { let x = 'x'; return `a${x}b`;}", - "function wrapper(arg0, arg1) { return `axb`;}", - ); test( "function wrapper(arg0, arg1) { let x = arg0; return import(x);}", "function wrapper(arg0, arg1) { return import(arg0);}", @@ -2186,10 +2186,10 @@ fn test_inline_single_use_variable_ignored() { "function wrapper(arg0, arg1) { let x = arg0; return [import(arg1), x];}", "function wrapper(arg0, arg1) { return [import(arg1), arg0];}", ); - test( - "function wrapper(arg0, arg1) {return async () => { let x = arg0; await x; };}", - "function wrapper(arg0, arg1) { return async () => { await arg0; };}", - ); + // test( + // "function wrapper(arg0, arg1) {return async () => { let x = arg0; await x; };}", + // "function wrapper(arg0, arg1) { return async () => { await arg0; };}", + // ); test( "function wrapper(arg0, arg1) {return async () => { let x = arg0; await y; return x; };}", "function wrapper(arg0, arg1) { return async () => { let x = arg0; return await y, x; };}", @@ -2198,10 +2198,10 @@ fn test_inline_single_use_variable_ignored() { "function wrapper(arg0, arg1) {return async () => { let x = arg0; await arg1; return x; };}", "function wrapper(arg0, arg1) { return async () => { let x = arg0; return await arg1, x; };}", ); - test( - "function wrapper(arg0, arg1) {return function* () { let x = arg0; yield x; };}", - "function wrapper(arg0, arg1) { return function* () { yield arg0; };}", - ); + // test( + // "function wrapper(arg0, arg1) {return function* () { let x = arg0; yield x; };}", + // "function wrapper(arg0, arg1) { return function* () { yield arg0; };}", + // ); test( "function wrapper(arg0, arg1) {return function* () { let x = arg0; yield; return x; };}", "function wrapper(arg0, arg1) { return function* () { let x = arg0; return yield, x; };}", @@ -2214,70 +2214,70 @@ fn test_inline_single_use_variable_ignored() { "function wrapper(arg0, arg1) {return function* () { let x = arg0; yield arg1; return x; };}", "function wrapper(arg0, arg1) { return function* () { let x = arg0; return yield arg1, x; };}", ); - test( - "function wrapper(arg0, arg1) { let x = arg0; x()}", - "function wrapper(arg0, arg1) { arg0();}", - ); - test( - "function wrapper(arg0, arg1) { let x = arg0; (0, x)()}", - "function wrapper(arg0, arg1) { arg0();}", - ); - test( - "function wrapper(arg0, arg1) { let x = arg0.foo; x.bar()}", - "function wrapper(arg0, arg1) { arg0.foo.bar();}", - ); - test( - "function wrapper(arg0, arg1) { let x = arg0.foo; x[bar]()}", - "function wrapper(arg0, arg1) { arg0.foo[bar]();}", - ); - test( - "function wrapper(arg0, arg1) { let x = arg0.foo; x()}", - "function wrapper(arg0, arg1) { let x = arg0.foo; x();}", - ); - test( - "function wrapper(arg0, arg1) { let x = arg0[foo]; x()}", - "function wrapper(arg0, arg1) { let x = arg0[foo]; x();}", - ); - test( - "function wrapper(arg0, arg1) { let x = arg0?.foo; x()}", - "function wrapper(arg0, arg1) { let x = arg0?.foo; x();}", - ); - test( - "function wrapper(arg0, arg1) { let x = arg0?.[foo]; x()}", - "function wrapper(arg0, arg1) { let x = arg0?.[foo]; x();}", - ); - test( - "function wrapper(arg0, arg1) { let x = arg0.foo; (0, x)()}", - "function wrapper(arg0, arg1) { let x = arg0.foo; x();}", - ); - test( - "function wrapper(arg0, arg1) { let x = arg0[foo]; (0, x)()}", - "function wrapper(arg0, arg1) { let x = arg0[foo]; x();}", - ); - test( - "function wrapper(arg0, arg1) { let x = arg0?.foo; (0, x)()}", - "function wrapper(arg0, arg1) { let x = arg0?.foo; x();}", - ); - test( - "function wrapper(arg0, arg1) { let x = arg0?.[foo]; (0, x)()}", - "function wrapper(arg0, arg1) { let x = arg0?.[foo]; x();}", - ); - test( - "function wrapper(arg0, arg1) { let x = arg0(); arg1() + x}", - "function wrapper(arg0, arg1) { let x = arg0(); arg1() + x;}", - ); - test( - "function wrapper(arg0, arg1) { let x = arg0(); /* @__PURE__ */ arg1() + x}", - "function wrapper(arg0, arg1) { let x = arg0(); /* @__PURE__ */ arg1() + x;}", - ); - test( - "function wrapper(arg0, arg1) { let x = /* @__PURE__ */ arg0(); arg1() + x}", - "function wrapper(arg0, arg1) { let x = /* @__PURE__ */ arg0(); arg1() + x;}", - ); - test( - "function wrapper(arg0, arg1) { let x = /* @__PURE__ */ arg0(); /* @__PURE__ */ arg1() + x}", - "function wrapper(arg0, arg1) { /* @__PURE__ */ arg1() + /* @__PURE__ */ arg0();}", - ); + // test( + // "function wrapper(arg0, arg1) { let x = arg0; x()}", + // "function wrapper(arg0, arg1) { arg0();}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = arg0; (0, x)()}", + // "function wrapper(arg0, arg1) { arg0();}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = arg0.foo; x.bar()}", + // "function wrapper(arg0, arg1) { arg0.foo.bar();}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = arg0.foo; x[bar]()}", + // "function wrapper(arg0, arg1) { arg0.foo[bar]();}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = arg0.foo; x()}", + // "function wrapper(arg0, arg1) { let x = arg0.foo; x();}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = arg0[foo]; x()}", + // "function wrapper(arg0, arg1) { let x = arg0[foo]; x();}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = arg0?.foo; x()}", + // "function wrapper(arg0, arg1) { let x = arg0?.foo; x();}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = arg0?.[foo]; x()}", + // "function wrapper(arg0, arg1) { let x = arg0?.[foo]; x();}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = arg0.foo; (0, x)()}", + // "function wrapper(arg0, arg1) { let x = arg0.foo; x();}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = arg0[foo]; (0, x)()}", + // "function wrapper(arg0, arg1) { let x = arg0[foo]; x();}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = arg0?.foo; (0, x)()}", + // "function wrapper(arg0, arg1) { let x = arg0?.foo; x();}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = arg0?.[foo]; (0, x)()}", + // "function wrapper(arg0, arg1) { let x = arg0?.[foo]; x();}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = arg0(); arg1() + x}", + // "function wrapper(arg0, arg1) { let x = arg0(); arg1() + x;}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = arg0(); /* @__PURE__ */ arg1() + x}", + // "function wrapper(arg0, arg1) { let x = arg0(); /* @__PURE__ */ arg1() + x;}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = /* @__PURE__ */ arg0(); arg1() + x}", + // "function wrapper(arg0, arg1) { let x = /* @__PURE__ */ arg0(); arg1() + x;}", + // ); + // test( + // "function wrapper(arg0, arg1) { let x = /* @__PURE__ */ arg0(); /* @__PURE__ */ arg1() + x}", + // "function wrapper(arg0, arg1) { /* @__PURE__ */ arg1() + /* @__PURE__ */ arg0();}", + // ); } #[test] diff --git a/crates/oxc_minifier/tests/peephole/inline_single_use_variable.rs b/crates/oxc_minifier/tests/peephole/inline_single_use_variable.rs index 64785b29b7efd..ef251292ac4da 100644 --- a/crates/oxc_minifier/tests/peephole/inline_single_use_variable.rs +++ b/crates/oxc_minifier/tests/peephole/inline_single_use_variable.rs @@ -1,7 +1,34 @@ -use super::test_same; +use super::{test, test_same}; #[test] fn test_inline_single_use_variable() { test_same("function wrapper(arg0, arg1) {using x = foo; return x}"); test_same("async function wrapper(arg0, arg1) { await using x = foo; return x}"); } + +#[test] +fn integration() { + test( + " + export function foo() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + return bar(args); + } + + function bar(args) { + return args.concat(0) + } + ", + " + export function foo() { + return bar([...arguments]); + } + function bar(args) { + return args.concat(0); + } + ", + ); +} diff --git a/tasks/minsize/minsize.snap b/tasks/minsize/minsize.snap index 5581236921ffa..da4e2eff0a16f 100644 --- a/tasks/minsize/minsize.snap +++ b/tasks/minsize/minsize.snap @@ -1,27 +1,27 @@ | Oxc | ESBuild | Oxc | ESBuild | Original | minified | minified | gzip | gzip | Iterations | File ------------------------------------------------------------------------------------- -72.14 kB | 23.33 kB | 23.70 kB | 8.43 kB | 8.54 kB | 2 | react.development.js +72.14 kB | 23.23 kB | 23.70 kB | 8.40 kB | 8.54 kB | 2 | react.development.js -173.90 kB | 59.48 kB | 59.82 kB | 19.18 kB | 19.33 kB | 2 | moment.js +173.90 kB | 59.47 kB | 59.82 kB | 19.18 kB | 19.33 kB | 2 | moment.js -287.63 kB | 89.34 kB | 90.07 kB | 30.94 kB | 31.95 kB | 2 | jquery.js +287.63 kB | 89.31 kB | 90.07 kB | 30.95 kB | 31.95 kB | 2 | jquery.js -342.15 kB | 117.21 kB | 118.14 kB | 43.27 kB | 44.37 kB | 2 | vue.js +342.15 kB | 117.17 kB | 118.14 kB | 43.27 kB | 44.37 kB | 2 | vue.js -544.10 kB | 71.38 kB | 72.48 kB | 25.85 kB | 26.20 kB | 1 | lodash.js +544.10 kB | 71.20 kB | 72.48 kB | 25.85 kB | 26.20 kB | 3 | lodash.js -555.77 kB | 270.86 kB | 270.13 kB | 88.23 kB | 90.80 kB | 2 | d3.js +555.77 kB | 270.85 kB | 270.13 kB | 88.23 kB | 90.80 kB | 2 | d3.js -1.01 MB | 440.01 kB | 458.89 kB | 122.27 kB | 126.71 kB | 2 | bundle.min.js +1.01 MB | 439.88 kB | 458.89 kB | 122.24 kB | 126.71 kB | 3 | bundle.min.js -1.25 MB | 646.91 kB | 646.76 kB | 160.25 kB | 163.73 kB | 2 | three.js +1.25 MB | 646.86 kB | 646.76 kB | 160.24 kB | 163.73 kB | 2 | three.js -2.14 MB | 715.35 kB | 724.14 kB | 161.65 kB | 181.07 kB | 2 | victory.js +2.14 MB | 714.65 kB | 724.14 kB | 161.41 kB | 181.07 kB | 2 | victory.js -3.20 MB | 1.01 MB | 1.01 MB | 323.94 kB | 331.56 kB | 2 | echarts.js +3.20 MB | 1.01 MB | 1.01 MB | 323.86 kB | 331.56 kB | 3 | echarts.js -6.69 MB | 2.23 MB | 2.31 MB | 460.92 kB | 488.28 kB | 4 | antd.js +6.69 MB | 2.23 MB | 2.31 MB | 460.69 kB | 488.28 kB | 4 | antd.js -10.95 MB | 3.34 MB | 3.49 MB | 856.93 kB | 915.50 kB | 3 | typescript.js +10.95 MB | 3.34 MB | 3.49 MB | 856.46 kB | 915.50 kB | 4 | typescript.js