From c18a959e3a53c52c05672297a39f6eb628a076a0 Mon Sep 17 00:00:00 2001 From: Austaras Date: Sat, 18 Nov 2023 08:01:27 +0800 Subject: [PATCH] fix(es/minifier): Apply new `SyntaxContext` to inlined `Arrow` (#8301) **Related issue:** - Closes #8288 --- .../src/compress/optimize/iife.rs | 47 +++++++++---------- .../src/compress/optimize/inline.rs | 26 ++++++++-- .../tests/full/issues/8288/input.js | 30 ++++++++++++ .../tests/full/issues/8288/output.js | 1 + 4 files changed, 76 insertions(+), 28 deletions(-) create mode 100644 crates/swc_ecma_minifier/tests/full/issues/8288/input.js create mode 100644 crates/swc_ecma_minifier/tests/full/issues/8288/output.js diff --git a/crates/swc_ecma_minifier/src/compress/optimize/iife.rs b/crates/swc_ecma_minifier/src/compress/optimize/iife.rs index 841545f5e5b..df8738e1f38 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/iife.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/iife.rs @@ -509,12 +509,11 @@ impl Optimizer<'_> { // // For arrow expressions this is required because we copy simple arrow // expressions. - let mut remap = HashMap::default(); - let new_ctxt = SyntaxContext::empty().apply_mark(Mark::fresh(Mark::root())); - - for p in param_ids.iter() { - remap.insert(p.to_id(), new_ctxt); - } + let new_ctxt = SyntaxContext::empty().apply_mark(Mark::new()); + let remap = param_ids + .iter() + .map(|p| (p.to_id(), new_ctxt)) + .collect::>(); { let vars = param_ids @@ -843,7 +842,7 @@ impl Optimizer<'_> { // We remap variables. let mut remap = HashMap::default(); - let new_ctxt = SyntaxContext::empty().apply_mark(Mark::fresh(Mark::root())); + let new_ctxt = SyntaxContext::empty().apply_mark(Mark::new()); let params = orig_params .iter() @@ -857,27 +856,25 @@ impl Optimizer<'_> { }) .collect::>(); - { - for stmt in &body.stmts { - if let Stmt::Decl(Decl::Var(var)) = stmt { - for decl in &var.decls { - let ids: Vec = find_pat_ids(&decl.name); + for stmt in &body.stmts { + if let Stmt::Decl(Decl::Var(var)) = stmt { + for decl in &var.decls { + let ids: Vec = find_pat_ids(&decl.name); - for id in ids { - let ctx = remap - .entry(id.clone()) - .or_insert_with(|| SyntaxContext::empty().apply_mark(Mark::new())); + for id in ids { + let ctx = remap + .entry(id.clone()) + .or_insert_with(|| SyntaxContext::empty().apply_mark(Mark::new())); - // [is_skippable_for_seq] would check fn scope - if let Some(usage) = self.data.vars.get(&id) { - let mut usage = usage.clone(); - // as we turn var declaration into assignment - // we need to maintain correct var usage - if decl.init.is_some() { - usage.ref_count += 1; - } - self.data.vars.insert((id.0, *ctx), usage); + // [is_skippable_for_seq] would check fn scope + if let Some(usage) = self.data.vars.get(&id) { + let mut usage = usage.clone(); + // as we turn var declaration into assignment + // we need to maintain correct var usage + if decl.init.is_some() { + usage.ref_count += 1; } + self.data.vars.insert((id.0, *ctx), usage); } } } diff --git a/crates/swc_ecma_minifier/src/compress/optimize/inline.rs b/crates/swc_ecma_minifier/src/compress/optimize/inline.rs index 0a4d10058ab..b118d43e1aa 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/inline.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/inline.rs @@ -1,8 +1,9 @@ -use swc_common::{util::take::Take, EqIgnoreSpan, Spanned}; +use rustc_hash::FxHashMap; +use swc_common::{collections::AHashSet, util::take::Take, EqIgnoreSpan, Mark, Spanned}; use swc_ecma_ast::*; use swc_ecma_transforms_optimization::simplify::expr_simplifier; use swc_ecma_usage_analyzer::alias::{collect_infects_from, AliasConfig}; -use swc_ecma_utils::{class_has_side_effect, find_pat_ids, ExprExt}; +use swc_ecma_utils::{class_has_side_effect, collect_decls, find_pat_ids, ExprExt, Remapper}; use swc_ecma_visit::VisitMutWith; use super::Optimizer; @@ -808,7 +809,7 @@ impl Optimizer<'_> { } Expr::Ident(i) => { let id = i.to_id(); - if let Some(value) = self + if let Some(mut value) = self .vars .lits .get(&id) @@ -827,6 +828,25 @@ impl Optimizer<'_> { return; } + // currently renamer relies on the fact no distinct var has same ctxt, we need + // to remap all new bindings. + let bindings: AHashSet = collect_decls(&*value); + let new_mark = Mark::new(); + let mut cache = FxHashMap::default(); + let remap: FxHashMap<_, _> = bindings + .into_iter() + .map(|id| { + let value = cache + .entry(id.1) + .or_insert_with(|| id.1.apply_mark(new_mark)); + + (id, *value) + }) + .collect(); + + let mut remapper = Remapper::new(&remap); + value.visit_mut_with(&mut remapper); + self.changed = true; report_change!("inline: Replacing a variable `{}` with cheap expression", i); diff --git a/crates/swc_ecma_minifier/tests/full/issues/8288/input.js b/crates/swc_ecma_minifier/tests/full/issues/8288/input.js new file mode 100644 index 00000000000..4f5dfa28d14 --- /dev/null +++ b/crates/swc_ecma_minifier/tests/full/issues/8288/input.js @@ -0,0 +1,30 @@ +function memoizeWithArgs(fnWithArgs, options) { + const fn = proxyMemoize((args) => fnWithArgs(...args), options); + return (...args) => fn(args); +} + +function makeSelector(selector, selectorTransformer) { + return (state) => selectorTransformer(selector(state)); +} + +export function createSelectorHook(selector) { + const useSelectorHook = (selectorTransformer, deps) => { + const memoSelector = useMemo( + () => + selectorTransformer && deps + ? makeSelector( + selector, + memoizeWithArgs(selectorTransformer) + ) + : undefined, + deps + ); + const finalSelector = memoSelector + ? memoSelector + : selectorTransformer + ? makeSelector(selector, selectorTransformer) + : selector; + return useSelector(finalSelector); + }; + return useSelectorHook; +} diff --git a/crates/swc_ecma_minifier/tests/full/issues/8288/output.js b/crates/swc_ecma_minifier/tests/full/issues/8288/output.js new file mode 100644 index 00000000000..b23b1c067b1 --- /dev/null +++ b/crates/swc_ecma_minifier/tests/full/issues/8288/output.js @@ -0,0 +1 @@ +export function createSelectorHook(e){return(o,r)=>{const t=useMemo(()=>{var t;return o&&r?(t=function(e,o){const r=proxyMemoize(o=>e(...o),void 0);return(...e)=>r(e)}(o),o=>t(e(o))):void 0},r)||(o?r=>o(e(r)):e);return useSelector(t)}}