fix(es/compat): Make block-scoping pass rename exports correctly (#8175)

**Related issue:**

 - Closes #8148
This commit is contained in:
Donny/강동윤 2023-10-30 14:08:27 +09:00 committed by GitHub
parent b77d99d4b8
commit b13bc32027
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 120 additions and 116 deletions

View File

@ -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
}

View File

@ -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; }
}

View File

@ -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 };

View File

@ -21,7 +21,6 @@ use swc_ecma_visit::{
}; };
use swc_trace_macro::swc_trace; use swc_trace_macro::swc_trace;
mod operator;
mod vars; mod vars;
/// ///

View File

@ -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<Id, Id>) -> Rename {
Rename { map }
}
pub(super) struct Rename {
map: AHashMap<Id, Id>,
}
#[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);
}
}
}

View File

@ -5,12 +5,10 @@ use swc_common::{
Mark, SyntaxContext, Mark, SyntaxContext,
}; };
use swc_ecma_ast::*; 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_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
use swc_trace_macro::swc_trace; use swc_trace_macro::swc_trace;
use super::operator::{rename, Rename};
pub(super) fn block_scoped_vars() -> impl VisitMut { pub(super) fn block_scoped_vars() -> impl VisitMut {
BlockScopedVars::default() BlockScopedVars::default()
} }
@ -62,7 +60,7 @@ impl BlockScopedVars {
/// - For third, we rename all declarations which may conflict. /// - For third, we rename all declarations which may conflict.
fn handle_program<N>(&mut self, n: &mut N) fn handle_program<N>(&mut self, n: &mut N)
where where
N: VisitMutWith<Self> + VisitMutWith<Rename>, N: VisitMutWith<Self> + for<'aa> VisitMutWith<dyn 'aa + VisitMut>,
{ {
n.visit_mut_children_with(self); n.visit_mut_children_with(self);
@ -81,7 +79,7 @@ impl BlockScopedVars {
// dbg!(&rename_map); // 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)) { fn with_scope(&mut self, kind: ScopeKind, op: impl FnOnce(&mut Self)) {

View File

@ -54,6 +54,14 @@ pub fn rename_with_config(map: &AHashMap<Id, JsWord>, config: Config) -> impl '_
}) })
} }
pub fn remap(map: &AHashMap<Id, Id>, config: Config) -> impl '_ + Fold + VisitMut {
as_folder(Operator {
rename: map,
config,
extra: Default::default(),
})
}
pub fn renamer<R>(config: Config, renamer: R) -> impl Fold + VisitMut pub fn renamer<R>(config: Config, renamer: R) -> impl Fold + VisitMut
where where
R: Renamer, R: Renamer,

View File

@ -1,10 +1,10 @@
use swc_atoms::JsWord;
use swc_common::{ use swc_common::{
collections::AHashMap, collections::AHashMap,
util::{move_map::MoveMap, take::Take}, util::{move_map::MoveMap, take::Take},
Spanned, SyntaxContext, DUMMY_SP, Spanned, SyntaxContext, DUMMY_SP,
}; };
use swc_ecma_ast::*; use swc_ecma_ast::*;
use swc_ecma_utils::ident::IdentLike;
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith}; use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
use crate::{ use crate::{
@ -12,14 +12,20 @@ use crate::{
perf::{cpu_count, ParExplode, Parallel, ParallelExt}, perf::{cpu_count, ParExplode, Parallel, ParallelExt},
}; };
pub(super) struct Operator<'a> { pub(super) struct Operator<'a, I>
pub rename: &'a AHashMap<Id, JsWord>, where
I: IdentLike,
{
pub rename: &'a AHashMap<Id, I>,
pub config: Config, pub config: Config,
pub extra: Vec<ModuleItem>, pub extra: Vec<ModuleItem>,
} }
impl Operator<'_> { impl<I> Operator<'_, I>
where
I: IdentLike,
{
fn keep_class_name(&mut self, ident: &mut Ident, class: &mut Class) -> Option<ClassExpr> { fn keep_class_name(&mut self, ident: &mut Ident, class: &mut Class) -> Option<ClassExpr> {
if !self.config.keep_class_names { if !self.config.keep_class_names {
return None; return None;
@ -55,7 +61,10 @@ impl Operator<'_> {
} }
} }
impl Parallel for Operator<'_> { impl<I> Parallel for Operator<'_, I>
where
I: IdentLike,
{
fn create(&self) -> Self { fn create(&self) -> Self {
Self { Self {
rename: self.rename, rename: self.rename,
@ -73,7 +82,10 @@ impl Parallel for Operator<'_> {
} }
} }
impl ParExplode for Operator<'_> { impl<I> ParExplode for Operator<'_, I>
where
I: IdentLike,
{
fn after_one_stmt(&mut self, _: &mut Vec<Stmt>) {} fn after_one_stmt(&mut self, _: &mut Vec<Stmt>) {}
fn after_one_module_item(&mut self, stmts: &mut Vec<ModuleItem>) { fn after_one_module_item(&mut self, stmts: &mut Vec<ModuleItem>) {
@ -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!(); noop_visit_mut_type!();
/// Preserve key of properties. /// Preserve key of properties.
@ -576,12 +591,18 @@ impl<'a> VisitMut for Operator<'a> {
} }
} }
struct VarFolder<'a, 'b> { struct VarFolder<'a, 'b, I>
orig: &'a mut Operator<'b>, where
I: IdentLike,
{
orig: &'a mut Operator<'b, I>,
renamed: &'a mut Vec<ExportSpecifier>, renamed: &'a mut Vec<ExportSpecifier>,
} }
impl VisitMut for VarFolder<'_, '_> { impl<I> VisitMut for VarFolder<'_, '_, I>
where
I: IdentLike,
{
noop_visit_mut_type!(); noop_visit_mut_type!();
#[inline] #[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. /// Returns `Ok(renamed_ident)` if ident should be renamed.
fn rename_ident(&mut self, ident: &mut Ident) -> Result<(), ()> { fn rename_ident(&mut self, ident: &mut Ident) -> Result<(), ()> {
if let Some(sym) = self.rename.get(&ident.to_id()) { if let Some(new_id) = self.rename.get(&ident.to_id()) {
if *sym == ident.sym { let (new_sym, new_ctxt) = new_id.to_id();
if new_sym == ident.sym {
return Err(()); return Err(());
} }
ident.span = ident.span.with_ctxt(SyntaxContext::empty()); ident.span = ident.span.with_ctxt(new_ctxt);
ident.sym = sym.clone(); ident.sym = new_sym;
return Ok(()); return Ok(());
} }

View File

@ -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; }
}

View File

@ -2,12 +2,26 @@ use swc_atoms::JsWord;
use swc_common::{Span, SyntaxContext}; use swc_common::{Span, SyntaxContext};
use swc_ecma_ast::{BindingIdent, Id, Ident}; 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 from_ident(i: &Ident) -> Self;
fn to_id(&self) -> Id; fn to_id(&self) -> Id;
fn into_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 { impl IdentLike for BindingIdent {
fn from_ident(i: &Ident) -> Self { fn from_ident(i: &Ident) -> Self {
i.clone().into() i.clone().into()