From b13bc320274e773c9b0ef479e86194c4c6f1f6f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Mon, 30 Oct 2023 14:08:27 +0900 Subject: [PATCH] fix(es/compat): Make `block-scoping` pass rename exports correctly (#8175) **Related issue:** - Closes #8148 --- .../fixture/issues-8xxx/8148/input/.swcrc | 19 ++++ .../fixture/issues-8xxx/8148/input/input.js | 10 ++ .../fixture/issues-8xxx/8148/output/input.js | 13 +++ .../src/block_scoping/mod.rs | 1 - .../src/block_scoping/operator.rs | 94 ------------------- .../src/block_scoping/vars.rs | 8 +- .../src/rename/mod.rs | 8 ++ .../src/rename/ops.rs | 56 ++++++++--- .../tests/block-scoping/issue-8148/1/input.js | 11 +++ crates/swc_ecma_utils/src/ident.rs | 16 +++- 10 files changed, 120 insertions(+), 116 deletions(-) create mode 100644 crates/swc/tests/fixture/issues-8xxx/8148/input/.swcrc create mode 100644 crates/swc/tests/fixture/issues-8xxx/8148/input/input.js create mode 100644 crates/swc/tests/fixture/issues-8xxx/8148/output/input.js delete mode 100644 crates/swc_ecma_compat_es2015/src/block_scoping/operator.rs create mode 100644 crates/swc_ecma_transforms_compat/tests/block-scoping/issue-8148/1/input.js diff --git a/crates/swc/tests/fixture/issues-8xxx/8148/input/.swcrc b/crates/swc/tests/fixture/issues-8xxx/8148/input/.swcrc new file mode 100644 index 000000000000..daa38ba90c7a --- /dev/null +++ b/crates/swc/tests/fixture/issues-8xxx/8148/input/.swcrc @@ -0,0 +1,19 @@ +{ + "jsc": { + "parser": { + "syntax": "ecmascript", + "jsx": false + }, + "target": "es5", + "loose": false, + "minify": { + "compress": false, + "mangle": false + } + }, + "module": { + "type": "es6" + }, + "minify": false, + "isModule": true +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-8xxx/8148/input/input.js b/crates/swc/tests/fixture/issues-8xxx/8148/input/input.js new file mode 100644 index 000000000000..6f7eddd979d1 --- /dev/null +++ b/crates/swc/tests/fixture/issues-8xxx/8148/input/input.js @@ -0,0 +1,10 @@ +function _toBeMocked() { + return "I am the original function"; +} + +export let toBeMocked = _toBeMocked + +export const mock = { + get toBeMocked() { return toBeMocked; }, + set toBeMocked(mock) { toBeMocked = mock; } +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-8xxx/8148/output/input.js b/crates/swc/tests/fixture/issues-8xxx/8148/output/input.js new file mode 100644 index 000000000000..56490d86fbe9 --- /dev/null +++ b/crates/swc/tests/fixture/issues-8xxx/8148/output/input.js @@ -0,0 +1,13 @@ +function _toBeMocked() { + return "I am the original function"; +} +export var toBeMocked = _toBeMocked; +var _$mock = { + get toBeMocked () { + return toBeMocked; + }, + set toBeMocked (mock){ + toBeMocked = mock; + } +}; +export { _$mock as mock }; diff --git a/crates/swc_ecma_compat_es2015/src/block_scoping/mod.rs b/crates/swc_ecma_compat_es2015/src/block_scoping/mod.rs index 008b1e65fa99..1c916586b526 100644 --- a/crates/swc_ecma_compat_es2015/src/block_scoping/mod.rs +++ b/crates/swc_ecma_compat_es2015/src/block_scoping/mod.rs @@ -21,7 +21,6 @@ use swc_ecma_visit::{ }; use swc_trace_macro::swc_trace; -mod operator; mod vars; /// diff --git a/crates/swc_ecma_compat_es2015/src/block_scoping/operator.rs b/crates/swc_ecma_compat_es2015/src/block_scoping/operator.rs deleted file mode 100644 index 5e5b1fbe6559..000000000000 --- a/crates/swc_ecma_compat_es2015/src/block_scoping/operator.rs +++ /dev/null @@ -1,94 +0,0 @@ -use swc_common::{collections::AHashMap, SyntaxContext, DUMMY_SP}; -use swc_ecma_ast::*; -use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith}; -use swc_trace_macro::swc_trace; - -pub(super) fn rename(map: AHashMap) -> Rename { - Rename { map } -} - -pub(super) struct Rename { - map: AHashMap, -} - -#[swc_trace] -impl VisitMut for Rename { - noop_visit_mut_type!(); - - fn visit_mut_ident(&mut self, i: &mut Ident) { - // fast path. - if i.span.ctxt == SyntaxContext::empty() { - return; - } - - if let Some(id) = self.map.get(&i.to_id()) { - *i = Ident::new(id.0.clone(), i.span.with_ctxt(id.1)); - } - } - - fn visit_mut_member_prop(&mut self, n: &mut MemberProp) { - if let MemberProp::Computed(n) = n { - n.visit_mut_with(self); - } - } - - fn visit_mut_object_pat_prop(&mut self, i: &mut ObjectPatProp) { - match i { - ObjectPatProp::Assign(p) => { - p.value.visit_mut_with(self); - - let orig = p.key.clone(); - p.key.visit_mut_with(self); - - if orig.to_id() == p.key.to_id() { - return; - } - - match p.value.take() { - Some(default) => { - *i = ObjectPatProp::KeyValue(KeyValuePatProp { - key: PropName::Ident(orig), - value: Box::new(Pat::Assign(AssignPat { - span: DUMMY_SP, - left: Box::new(Pat::Ident(p.key.clone().into())), - right: default, - })), - }); - } - None => { - *i = ObjectPatProp::KeyValue(KeyValuePatProp { - key: PropName::Ident(orig), - value: Box::new(Pat::Ident(p.key.clone().into())), - }); - } - } - } - - _ => { - i.visit_mut_children_with(self); - } - } - } - - fn visit_mut_prop(&mut self, n: &mut Prop) { - if let Prop::Shorthand(ident) = n { - if let Some(id) = self.map.get(&ident.to_id()) { - *n = KeyValueProp { - key: PropName::Ident(ident.clone()), - value: Box::new(Ident::new(id.0.clone(), ident.span.with_ctxt(id.1)).into()), - } - .into(); - } - - return; - } - - n.visit_mut_children_with(self); - } - - fn visit_mut_prop_name(&mut self, n: &mut PropName) { - if let PropName::Computed(n) = n { - n.visit_mut_with(self); - } - } -} diff --git a/crates/swc_ecma_compat_es2015/src/block_scoping/vars.rs b/crates/swc_ecma_compat_es2015/src/block_scoping/vars.rs index c9d26639a151..f00c79b9af9d 100644 --- a/crates/swc_ecma_compat_es2015/src/block_scoping/vars.rs +++ b/crates/swc_ecma_compat_es2015/src/block_scoping/vars.rs @@ -5,12 +5,10 @@ use swc_common::{ Mark, SyntaxContext, }; use swc_ecma_ast::*; -use swc_ecma_transforms_base::scope::ScopeKind; +use swc_ecma_transforms_base::{rename::remap, scope::ScopeKind}; use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith}; use swc_trace_macro::swc_trace; -use super::operator::{rename, Rename}; - pub(super) fn block_scoped_vars() -> impl VisitMut { BlockScopedVars::default() } @@ -62,7 +60,7 @@ impl BlockScopedVars { /// - For third, we rename all declarations which may conflict. fn handle_program(&mut self, n: &mut N) where - N: VisitMutWith + VisitMutWith, + N: VisitMutWith + for<'aa> VisitMutWith, { n.visit_mut_children_with(self); @@ -81,7 +79,7 @@ impl BlockScopedVars { // dbg!(&rename_map); - n.visit_mut_with(&mut rename(rename_map)) + n.visit_mut_with(&mut remap(&rename_map, Default::default()) as &mut dyn VisitMut); } fn with_scope(&mut self, kind: ScopeKind, op: impl FnOnce(&mut Self)) { diff --git a/crates/swc_ecma_transforms_base/src/rename/mod.rs b/crates/swc_ecma_transforms_base/src/rename/mod.rs index 09d1e5003030..2f00233b56d7 100644 --- a/crates/swc_ecma_transforms_base/src/rename/mod.rs +++ b/crates/swc_ecma_transforms_base/src/rename/mod.rs @@ -54,6 +54,14 @@ pub fn rename_with_config(map: &AHashMap, config: Config) -> impl '_ }) } +pub fn remap(map: &AHashMap, config: Config) -> impl '_ + Fold + VisitMut { + as_folder(Operator { + rename: map, + config, + extra: Default::default(), + }) +} + pub fn renamer(config: Config, renamer: R) -> impl Fold + VisitMut where R: Renamer, diff --git a/crates/swc_ecma_transforms_base/src/rename/ops.rs b/crates/swc_ecma_transforms_base/src/rename/ops.rs index c2e35bf962dd..078f1bae8bf5 100644 --- a/crates/swc_ecma_transforms_base/src/rename/ops.rs +++ b/crates/swc_ecma_transforms_base/src/rename/ops.rs @@ -1,10 +1,10 @@ -use swc_atoms::JsWord; use swc_common::{ collections::AHashMap, util::{move_map::MoveMap, take::Take}, Spanned, SyntaxContext, DUMMY_SP, }; use swc_ecma_ast::*; +use swc_ecma_utils::ident::IdentLike; use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith}; use crate::{ @@ -12,14 +12,20 @@ use crate::{ perf::{cpu_count, ParExplode, Parallel, ParallelExt}, }; -pub(super) struct Operator<'a> { - pub rename: &'a AHashMap, +pub(super) struct Operator<'a, I> +where + I: IdentLike, +{ + pub rename: &'a AHashMap, pub config: Config, pub extra: Vec, } -impl Operator<'_> { +impl Operator<'_, I> +where + I: IdentLike, +{ fn keep_class_name(&mut self, ident: &mut Ident, class: &mut Class) -> Option { if !self.config.keep_class_names { return None; @@ -55,7 +61,10 @@ impl Operator<'_> { } } -impl Parallel for Operator<'_> { +impl Parallel for Operator<'_, I> +where + I: IdentLike, +{ fn create(&self) -> Self { Self { rename: self.rename, @@ -73,7 +82,10 @@ impl Parallel for Operator<'_> { } } -impl ParExplode for Operator<'_> { +impl ParExplode for Operator<'_, I> +where + I: IdentLike, +{ fn after_one_stmt(&mut self, _: &mut Vec) {} fn after_one_module_item(&mut self, stmts: &mut Vec) { @@ -81,7 +93,10 @@ impl ParExplode for Operator<'_> { } } -impl<'a> VisitMut for Operator<'a> { +impl<'a, I> VisitMut for Operator<'a, I> +where + I: IdentLike, +{ noop_visit_mut_type!(); /// Preserve key of properties. @@ -576,12 +591,18 @@ impl<'a> VisitMut for Operator<'a> { } } -struct VarFolder<'a, 'b> { - orig: &'a mut Operator<'b>, +struct VarFolder<'a, 'b, I> +where + I: IdentLike, +{ + orig: &'a mut Operator<'b, I>, renamed: &'a mut Vec, } -impl VisitMut for VarFolder<'_, '_> { +impl VisitMut for VarFolder<'_, '_, I> +where + I: IdentLike, +{ noop_visit_mut_type!(); #[inline] @@ -601,16 +622,21 @@ impl VisitMut for VarFolder<'_, '_> { } } -impl<'a> Operator<'a> { +impl<'a, I> Operator<'a, I> +where + I: IdentLike, +{ /// Returns `Ok(renamed_ident)` if ident should be renamed. fn rename_ident(&mut self, ident: &mut Ident) -> Result<(), ()> { - if let Some(sym) = self.rename.get(&ident.to_id()) { - if *sym == ident.sym { + if let Some(new_id) = self.rename.get(&ident.to_id()) { + let (new_sym, new_ctxt) = new_id.to_id(); + + if new_sym == ident.sym { return Err(()); } - ident.span = ident.span.with_ctxt(SyntaxContext::empty()); - ident.sym = sym.clone(); + ident.span = ident.span.with_ctxt(new_ctxt); + ident.sym = new_sym; return Ok(()); } diff --git a/crates/swc_ecma_transforms_compat/tests/block-scoping/issue-8148/1/input.js b/crates/swc_ecma_transforms_compat/tests/block-scoping/issue-8148/1/input.js new file mode 100644 index 000000000000..1ce6ae8eafde --- /dev/null +++ b/crates/swc_ecma_transforms_compat/tests/block-scoping/issue-8148/1/input.js @@ -0,0 +1,11 @@ + +function _toBeMocked() { + return "I am the original function"; +} + +export let toBeMocked = _toBeMocked + +export const mock = { + get toBeMocked() { return toBeMocked; }, + set toBeMocked(mock) { toBeMocked = mock; } +} \ No newline at end of file diff --git a/crates/swc_ecma_utils/src/ident.rs b/crates/swc_ecma_utils/src/ident.rs index 43758daa8fdd..ff621e4faf78 100644 --- a/crates/swc_ecma_utils/src/ident.rs +++ b/crates/swc_ecma_utils/src/ident.rs @@ -2,12 +2,26 @@ use swc_atoms::JsWord; use swc_common::{Span, SyntaxContext}; use swc_ecma_ast::{BindingIdent, Id, Ident}; -pub trait IdentLike: Sized { +pub trait IdentLike: Sized + Send + Sync + 'static { fn from_ident(i: &Ident) -> Self; fn to_id(&self) -> Id; fn into_id(self) -> Id; } +impl IdentLike for JsWord { + fn from_ident(i: &Ident) -> Self { + i.sym.clone() + } + + fn to_id(&self) -> Id { + (self.clone(), Default::default()) + } + + fn into_id(self) -> Id { + (self, Default::default()) + } +} + impl IdentLike for BindingIdent { fn from_ident(i: &Ident) -> Self { i.clone().into()