From 4110fcb9612ceeba0c52f539896075a571c9a619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 15 Apr 2022 20:01:29 +0900 Subject: [PATCH] fix(es/minifier): Inline before cloning (#4338) --- .../src/compress/optimize/hoist_props.rs | 14 +- .../src/compress/optimize/inline.rs | 4 + .../src/compress/pure/dead_code.rs | 2 +- .../36127/2/1/analysis-snapshot.rust-debug | 194 +++++++++++++++ .../compress/fixture/next/36127/2/1/input.js | 19 ++ .../compress/fixture/next/36127/2/1/output.js | 5 + .../36127/2/2/analysis-snapshot.rust-debug | 232 ++++++++++++++++++ .../compress/fixture/next/36127/2/2/input.js | 31 +++ .../compress/fixture/next/36127/2/2/output.js | 6 + 9 files changed, 501 insertions(+), 6 deletions(-) create mode 100644 crates/swc_ecma_minifier/tests/compress/fixture/next/36127/2/1/analysis-snapshot.rust-debug create mode 100644 crates/swc_ecma_minifier/tests/compress/fixture/next/36127/2/1/input.js create mode 100644 crates/swc_ecma_minifier/tests/compress/fixture/next/36127/2/1/output.js create mode 100644 crates/swc_ecma_minifier/tests/compress/fixture/next/36127/2/2/analysis-snapshot.rust-debug create mode 100644 crates/swc_ecma_minifier/tests/compress/fixture/next/36127/2/2/input.js create mode 100644 crates/swc_ecma_minifier/tests/compress/fixture/next/36127/2/2/output.js diff --git a/crates/swc_ecma_minifier/src/compress/optimize/hoist_props.rs b/crates/swc_ecma_minifier/src/compress/optimize/hoist_props.rs index 82bf005b7f3..64ae5feaee3 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/hoist_props.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/hoist_props.rs @@ -89,14 +89,20 @@ where return; } - if let Some(Expr::Object(init)) = n.init.as_deref() { - for prop in &init.props { + if let Some(init) = n.init.as_deref() { + self.mode.store(name.to_id(), init); + } + + if let Some(Expr::Object(init)) = n.init.as_deref_mut() { + for prop in &mut init.props { let prop = match prop { PropOrSpread::Spread(_) => continue, PropOrSpread::Prop(prop) => prop, }; - if let Prop::KeyValue(p) = &**prop { + if let Prop::KeyValue(p) = &mut **prop { + self.vars.inline_with_multi_replacer(&mut p.value); + let value = match &*p.value { Expr::Lit(..) => p.value.clone(), Expr::Fn(..) | Expr::Arrow(..) => { @@ -117,7 +123,6 @@ where ); self.simple_props .insert((name.to_id(), s.value.clone()), value); - self.mode.store(name.to_id(), n.init.as_deref().unwrap()); } PropName::Ident(i) => { trace_op!( @@ -126,7 +131,6 @@ where ); self.simple_props .insert((name.to_id(), i.sym.clone()), value); - self.mode.store(name.to_id(), n.init.as_deref().unwrap()); } _ => {} } diff --git a/crates/swc_ecma_minifier/src/compress/optimize/inline.rs b/crates/swc_ecma_minifier/src/compress/optimize/inline.rs index bb9be83b21a..718339d4249 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/inline.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/inline.rs @@ -40,6 +40,8 @@ where return; } + self.vars.inline_with_multi_replacer(init); + // TODO: Check for side effect between original decl position and inlined // position @@ -435,6 +437,8 @@ where // Inline very simple functions. match decl { Decl::Fn(f) if self.options.inline >= 2 && f.ident.sym != *"arguments" => { + self.vars.inline_with_multi_replacer(&mut f.function.body); + match &f.function.body { Some(body) => { if !UsageFinder::find(&i, body) diff --git a/crates/swc_ecma_minifier/src/compress/pure/dead_code.rs b/crates/swc_ecma_minifier/src/compress/pure/dead_code.rs index 0b5e480bab0..2d0a5ed83fb 100644 --- a/crates/swc_ecma_minifier/src/compress/pure/dead_code.rs +++ b/crates/swc_ecma_minifier/src/compress/pure/dead_code.rs @@ -144,7 +144,7 @@ impl Pure<'_> { } } - /// Returns [Some] if the whole statement sohuld be replaced + /// Returns [Some] if the whole statement should be replaced fn drop_useless_continue_inner( &mut self, label: Option, diff --git a/crates/swc_ecma_minifier/tests/compress/fixture/next/36127/2/1/analysis-snapshot.rust-debug b/crates/swc_ecma_minifier/tests/compress/fixture/next/36127/2/1/analysis-snapshot.rust-debug new file mode 100644 index 00000000000..8e4bcfe5adf --- /dev/null +++ b/crates/swc_ecma_minifier/tests/compress/fixture/next/36127/2/1/analysis-snapshot.rust-debug @@ -0,0 +1,194 @@ +TestSnapshot { + vars: [ + ( + ( + Atom('String' type=static), + #1, + ), + VarUsageInfo { + inline_prevented: false, + ref_count: 1, + cond_init: false, + declared: false, + declared_count: 0, + declared_as_fn_param: false, + declared_as_fn_expr: false, + assign_count: 0, + mutation_by_call_count: 0, + usage_count: 1, + reassigned_with_assignment: false, + reassigned_with_var_decl: false, + mutated: false, + has_property_access: true, + has_property_mutation: false, + accessed_props: {}, + exported: false, + used_above_decl: true, + is_fn_local: true, + used_by_nested_fn: true, + executed_multiple_time: false, + used_in_cond: false, + var_kind: None, + var_initialized: false, + declared_as_catch_param: false, + no_side_effect_for_member_access: false, + used_as_callee: false, + used_as_arg: false, + pure_fn: false, + infects: [], + }, + ), + ( + ( + Atom('check' type=inline), + #2, + ), + VarUsageInfo { + inline_prevented: false, + ref_count: 1, + cond_init: false, + declared: true, + declared_count: 1, + declared_as_fn_param: false, + declared_as_fn_expr: false, + assign_count: 0, + mutation_by_call_count: 0, + usage_count: 1, + reassigned_with_assignment: false, + reassigned_with_var_decl: false, + mutated: false, + has_property_access: false, + has_property_mutation: false, + accessed_props: {}, + exported: false, + used_above_decl: true, + is_fn_local: true, + used_by_nested_fn: true, + executed_multiple_time: false, + used_in_cond: false, + var_kind: None, + var_initialized: false, + declared_as_catch_param: false, + no_side_effect_for_member_access: false, + used_as_callee: false, + used_as_arg: false, + pure_fn: false, + infects: [], + }, + ), + ( + ( + Atom('code' type=inline), + #3, + ), + VarUsageInfo { + inline_prevented: false, + ref_count: 2, + cond_init: false, + declared: true, + declared_count: 1, + declared_as_fn_param: true, + declared_as_fn_expr: false, + assign_count: 0, + mutation_by_call_count: 1, + usage_count: 2, + reassigned_with_assignment: false, + reassigned_with_var_decl: false, + mutated: true, + has_property_access: false, + has_property_mutation: false, + accessed_props: {}, + exported: false, + used_above_decl: false, + is_fn_local: true, + used_by_nested_fn: true, + executed_multiple_time: false, + used_in_cond: false, + var_kind: None, + var_initialized: false, + declared_as_catch_param: false, + no_side_effect_for_member_access: false, + used_as_callee: false, + used_as_arg: true, + pure_fn: false, + infects: [], + }, + ), + ( + ( + Atom('regex' type=inline), + #2, + ), + VarUsageInfo { + inline_prevented: false, + ref_count: 1, + cond_init: false, + declared: true, + declared_count: 1, + declared_as_fn_param: true, + declared_as_fn_expr: false, + assign_count: 0, + mutation_by_call_count: 0, + usage_count: 1, + reassigned_with_assignment: false, + reassigned_with_var_decl: false, + mutated: false, + has_property_access: true, + has_property_mutation: false, + accessed_props: {}, + exported: false, + used_above_decl: false, + is_fn_local: false, + used_by_nested_fn: true, + executed_multiple_time: false, + used_in_cond: false, + var_kind: None, + var_initialized: false, + declared_as_catch_param: false, + no_side_effect_for_member_access: false, + used_as_callee: false, + used_as_arg: false, + pure_fn: false, + infects: [], + }, + ), + ( + ( + Atom('regexCheck' type=dynamic), + #1, + ), + VarUsageInfo { + inline_prevented: true, + ref_count: 0, + cond_init: false, + declared: true, + declared_count: 1, + declared_as_fn_param: false, + declared_as_fn_expr: false, + assign_count: 0, + mutation_by_call_count: 0, + usage_count: 0, + reassigned_with_assignment: false, + reassigned_with_var_decl: false, + mutated: false, + has_property_access: false, + has_property_mutation: false, + accessed_props: {}, + exported: false, + used_above_decl: false, + is_fn_local: true, + used_by_nested_fn: false, + executed_multiple_time: false, + used_in_cond: false, + var_kind: None, + var_initialized: true, + declared_as_catch_param: false, + no_side_effect_for_member_access: false, + used_as_callee: false, + used_as_arg: false, + pure_fn: false, + infects: [], + }, + ), + ], +} diff --git a/crates/swc_ecma_minifier/tests/compress/fixture/next/36127/2/1/input.js b/crates/swc_ecma_minifier/tests/compress/fixture/next/36127/2/1/input.js new file mode 100644 index 00000000000..b07fba99482 --- /dev/null +++ b/crates/swc_ecma_minifier/tests/compress/fixture/next/36127/2/1/input.js @@ -0,0 +1,19 @@ +/** + * Create a code check from a regex. + * + * @param {RegExp} regex + * @returns {(code: Code) => code is number} + */ +export function regexCheck(regex) { + return check + + /** + * Check whether a code matches the bound regex. + * + * @param {Code} code Character code + * @returns {code is number} Whether the character code matches the bound regex + */ + function check(code) { + return code !== null && regex.test(String.fromCharCode(code)) + } +} \ No newline at end of file diff --git a/crates/swc_ecma_minifier/tests/compress/fixture/next/36127/2/1/output.js b/crates/swc_ecma_minifier/tests/compress/fixture/next/36127/2/1/output.js new file mode 100644 index 00000000000..e5fcad59a52 --- /dev/null +++ b/crates/swc_ecma_minifier/tests/compress/fixture/next/36127/2/1/output.js @@ -0,0 +1,5 @@ +export function regexCheck(regex) { + return function(code) { + return null !== code && regex.test(String.fromCharCode(code)); + }; +} diff --git a/crates/swc_ecma_minifier/tests/compress/fixture/next/36127/2/2/analysis-snapshot.rust-debug b/crates/swc_ecma_minifier/tests/compress/fixture/next/36127/2/2/analysis-snapshot.rust-debug new file mode 100644 index 00000000000..7f8a50f2416 --- /dev/null +++ b/crates/swc_ecma_minifier/tests/compress/fixture/next/36127/2/2/analysis-snapshot.rust-debug @@ -0,0 +1,232 @@ +TestSnapshot { + vars: [ + ( + ( + Atom('String' type=static), + #1, + ), + VarUsageInfo { + inline_prevented: false, + ref_count: 1, + cond_init: false, + declared: false, + declared_count: 0, + declared_as_fn_param: false, + declared_as_fn_expr: false, + assign_count: 0, + mutation_by_call_count: 0, + usage_count: 1, + reassigned_with_assignment: false, + reassigned_with_var_decl: false, + mutated: false, + has_property_access: true, + has_property_mutation: false, + accessed_props: {}, + exported: false, + used_above_decl: true, + is_fn_local: true, + used_by_nested_fn: true, + executed_multiple_time: false, + used_in_cond: false, + var_kind: None, + var_initialized: false, + declared_as_catch_param: false, + no_side_effect_for_member_access: false, + used_as_callee: false, + used_as_arg: false, + pure_fn: false, + infects: [], + }, + ), + ( + ( + Atom('check' type=inline), + #2, + ), + VarUsageInfo { + inline_prevented: false, + ref_count: 1, + cond_init: false, + declared: true, + declared_count: 1, + declared_as_fn_param: false, + declared_as_fn_expr: false, + assign_count: 0, + mutation_by_call_count: 0, + usage_count: 1, + reassigned_with_assignment: false, + reassigned_with_var_decl: false, + mutated: false, + has_property_access: false, + has_property_mutation: false, + accessed_props: {}, + exported: false, + used_above_decl: true, + is_fn_local: true, + used_by_nested_fn: true, + executed_multiple_time: false, + used_in_cond: false, + var_kind: None, + var_initialized: false, + declared_as_catch_param: false, + no_side_effect_for_member_access: false, + used_as_callee: false, + used_as_arg: false, + pure_fn: false, + infects: [], + }, + ), + ( + ( + Atom('code' type=inline), + #3, + ), + VarUsageInfo { + inline_prevented: false, + ref_count: 2, + cond_init: false, + declared: true, + declared_count: 1, + declared_as_fn_param: true, + declared_as_fn_expr: false, + assign_count: 0, + mutation_by_call_count: 1, + usage_count: 2, + reassigned_with_assignment: false, + reassigned_with_var_decl: false, + mutated: true, + has_property_access: false, + has_property_mutation: false, + accessed_props: {}, + exported: false, + used_above_decl: false, + is_fn_local: true, + used_by_nested_fn: true, + executed_multiple_time: false, + used_in_cond: false, + var_kind: None, + var_initialized: false, + declared_as_catch_param: false, + no_side_effect_for_member_access: false, + used_as_callee: false, + used_as_arg: true, + pure_fn: false, + infects: [], + }, + ), + ( + ( + Atom('console' type=inline), + #1, + ), + VarUsageInfo { + inline_prevented: false, + ref_count: 10, + cond_init: false, + declared: false, + declared_count: 0, + declared_as_fn_param: false, + declared_as_fn_expr: false, + assign_count: 0, + mutation_by_call_count: 0, + usage_count: 10, + reassigned_with_assignment: false, + reassigned_with_var_decl: false, + mutated: false, + has_property_access: true, + has_property_mutation: false, + accessed_props: {}, + exported: false, + used_above_decl: true, + is_fn_local: true, + used_by_nested_fn: false, + executed_multiple_time: false, + used_in_cond: false, + var_kind: None, + var_initialized: false, + declared_as_catch_param: false, + no_side_effect_for_member_access: false, + used_as_callee: false, + used_as_arg: false, + pure_fn: false, + infects: [], + }, + ), + ( + ( + Atom('regex' type=inline), + #2, + ), + VarUsageInfo { + inline_prevented: false, + ref_count: 1, + cond_init: false, + declared: true, + declared_count: 1, + declared_as_fn_param: true, + declared_as_fn_expr: false, + assign_count: 0, + mutation_by_call_count: 0, + usage_count: 1, + reassigned_with_assignment: false, + reassigned_with_var_decl: false, + mutated: false, + has_property_access: true, + has_property_mutation: false, + accessed_props: {}, + exported: false, + used_above_decl: false, + is_fn_local: false, + used_by_nested_fn: true, + executed_multiple_time: false, + used_in_cond: false, + var_kind: None, + var_initialized: false, + declared_as_catch_param: false, + no_side_effect_for_member_access: false, + used_as_callee: false, + used_as_arg: false, + pure_fn: false, + infects: [], + }, + ), + ( + ( + Atom('regexCheck' type=dynamic), + #1, + ), + VarUsageInfo { + inline_prevented: false, + ref_count: 10, + cond_init: false, + declared: true, + declared_count: 1, + declared_as_fn_param: false, + declared_as_fn_expr: false, + assign_count: 0, + mutation_by_call_count: 10, + usage_count: 10, + reassigned_with_assignment: false, + reassigned_with_var_decl: false, + mutated: true, + has_property_access: false, + has_property_mutation: false, + accessed_props: {}, + exported: false, + used_above_decl: false, + is_fn_local: true, + used_by_nested_fn: false, + executed_multiple_time: false, + used_in_cond: false, + var_kind: None, + var_initialized: true, + declared_as_catch_param: false, + no_side_effect_for_member_access: false, + used_as_callee: true, + used_as_arg: false, + pure_fn: false, + infects: [], + }, + ), + ], +} diff --git a/crates/swc_ecma_minifier/tests/compress/fixture/next/36127/2/2/input.js b/crates/swc_ecma_minifier/tests/compress/fixture/next/36127/2/2/input.js new file mode 100644 index 00000000000..030f84fb4c2 --- /dev/null +++ b/crates/swc_ecma_minifier/tests/compress/fixture/next/36127/2/2/input.js @@ -0,0 +1,31 @@ +/** + * Create a code check from a regex. + * + * @param {RegExp} regex + * @returns {(code: Code) => code is number} + */ +function regexCheck(regex) { + return check + + /** + * Check whether a code matches the bound regex. + * + * @param {Code} code Character code + * @returns {code is number} Whether the character code matches the bound regex + */ + function check(code) { + return code !== null && regex.test(String.fromCharCode(code)) + } +} + + +console.log(regexCheck('Foo')); +console.log(regexCheck('Foo')); +console.log(regexCheck('Foo')); +console.log(regexCheck('Foo')); +console.log(regexCheck('Foo')); +console.log(regexCheck('Foo')); +console.log(regexCheck('Foo')); +console.log(regexCheck('Foo')); +console.log(regexCheck('Foo')); +console.log(regexCheck('Foo')); \ No newline at end of file diff --git a/crates/swc_ecma_minifier/tests/compress/fixture/next/36127/2/2/output.js b/crates/swc_ecma_minifier/tests/compress/fixture/next/36127/2/2/output.js new file mode 100644 index 00000000000..390d44a190c --- /dev/null +++ b/crates/swc_ecma_minifier/tests/compress/fixture/next/36127/2/2/output.js @@ -0,0 +1,6 @@ +function regexCheck(regex) { + return function(code) { + return null !== code && regex.test(String.fromCharCode(code)); + }; +} +console.log(regexCheck('Foo')), console.log(regexCheck('Foo')), console.log(regexCheck('Foo')), console.log(regexCheck('Foo')), console.log(regexCheck('Foo')), console.log(regexCheck('Foo')), console.log(regexCheck('Foo')), console.log(regexCheck('Foo')), console.log(regexCheck('Foo')), console.log(regexCheck('Foo'));