Skip to content

Commit 5d1658f

Browse files
committed
restore nullish
1 parent 2215d4f commit 5d1658f

File tree

6 files changed

+269
-310
lines changed

6 files changed

+269
-310
lines changed

crates/swc_ecma_compat_es2020/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ pub fn es2020(config: Config, unresolved_mark: Mark) -> impl Pass {
1818
assumptions.no_document_all = config.nullish_coalescing.no_document_all;
1919

2020
(
21+
nullish_coalescing(config.nullish_coalescing),
2122
optional_chaining(config.optional_chaining, unresolved_mark),
2223
Compiler::new(swc_ecma_compiler::Config {
23-
includes: Features::NULLISH_COALESCING | Features::EXPORT_NAMESPACE_FROM,
24+
includes: Features::EXPORT_NAMESPACE_FROM,
2425
assumptions,
2526
..Default::default()
2627
}),
Lines changed: 251 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,264 @@
1+
use std::mem::take;
2+
13
use serde::Deserialize;
2-
use swc_ecma_ast::Pass;
3-
use swc_ecma_compiler::{Compiler, Features};
4-
use swc_ecma_transforms_base::assumptions::Assumptions;
4+
use swc_common::{util::take::Take, Span, DUMMY_SP};
5+
use swc_ecma_ast::*;
6+
use swc_ecma_utils::{alias_ident_for_simple_assign_tatget, alias_if_required, StmtLike};
7+
use swc_ecma_visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith};
58

69
pub fn nullish_coalescing(c: Config) -> impl Pass + 'static {
7-
let mut assumptions = Assumptions::default();
8-
assumptions.no_document_all = c.no_document_all;
9-
10-
Compiler::new(swc_ecma_compiler::Config {
11-
includes: Features::NULLISH_COALESCING,
12-
assumptions,
10+
visit_mut_pass(NullishCoalescing {
11+
c,
1312
..Default::default()
1413
})
1514
}
1615

16+
#[derive(Debug, Default)]
17+
struct NullishCoalescing {
18+
vars: Vec<VarDeclarator>,
19+
c: Config,
20+
}
21+
1722
#[derive(Debug, Clone, Copy, Default, Deserialize)]
1823
#[serde(rename_all = "camelCase")]
1924
pub struct Config {
2025
#[serde(default)]
2126
pub no_document_all: bool,
2227
}
28+
29+
impl NullishCoalescing {
30+
fn visit_mut_stmt_like<T>(&mut self, stmts: &mut Vec<T>)
31+
where
32+
T: VisitMutWith<Self> + StmtLike,
33+
{
34+
let mut buf = Vec::with_capacity(stmts.len() + 2);
35+
36+
for mut stmt in stmts.take() {
37+
stmt.visit_mut_with(self);
38+
39+
if !self.vars.is_empty() {
40+
buf.push(T::from(
41+
VarDecl {
42+
span: DUMMY_SP,
43+
kind: VarDeclKind::Var,
44+
decls: take(&mut self.vars),
45+
declare: false,
46+
..Default::default()
47+
}
48+
.into(),
49+
));
50+
}
51+
52+
buf.push(stmt);
53+
}
54+
55+
*stmts = buf
56+
}
57+
}
58+
59+
impl VisitMut for NullishCoalescing {
60+
noop_visit_mut_type!(fail);
61+
62+
/// Prevents #1123
63+
fn visit_mut_block_stmt(&mut self, s: &mut BlockStmt) {
64+
let old_vars = self.vars.take();
65+
s.visit_mut_children_with(self);
66+
self.vars = old_vars;
67+
}
68+
69+
/// Prevents #1123
70+
fn visit_mut_switch_case(&mut self, s: &mut SwitchCase) {
71+
// Prevents #6328
72+
s.test.visit_mut_with(self);
73+
let old_vars = self.vars.take();
74+
s.cons.visit_mut_with(self);
75+
self.vars = old_vars;
76+
}
77+
78+
fn visit_mut_module_items(&mut self, n: &mut Vec<ModuleItem>) {
79+
self.visit_mut_stmt_like(n)
80+
}
81+
82+
fn visit_mut_stmts(&mut self, n: &mut Vec<Stmt>) {
83+
self.visit_mut_stmt_like(n)
84+
}
85+
86+
fn visit_mut_expr(&mut self, e: &mut Expr) {
87+
e.visit_mut_children_with(self);
88+
89+
match e {
90+
Expr::Bin(BinExpr {
91+
span,
92+
left,
93+
op: op!("??"),
94+
right,
95+
}) => {
96+
//
97+
let (l, aliased) = alias_if_required(left, "ref");
98+
99+
if aliased {
100+
self.vars.push(VarDeclarator {
101+
span: DUMMY_SP,
102+
name: l.clone().into(),
103+
init: None,
104+
definite: false,
105+
});
106+
}
107+
108+
let var_expr = if aliased {
109+
AssignExpr {
110+
span: DUMMY_SP,
111+
op: op!("="),
112+
left: l.clone().into(),
113+
right: left.take(),
114+
}
115+
.into()
116+
} else {
117+
l.clone().into()
118+
};
119+
120+
*e = make_cond(self.c, *span, &l, var_expr, right.take());
121+
}
122+
123+
Expr::Assign(ref mut assign @ AssignExpr { op: op!("??="), .. }) => {
124+
match &mut assign.left {
125+
AssignTarget::Simple(SimpleAssignTarget::Ident(i)) => {
126+
*e = AssignExpr {
127+
span: assign.span,
128+
op: op!("="),
129+
left: i.clone().into(),
130+
right: Box::new(make_cond(
131+
self.c,
132+
assign.span,
133+
&Ident::from(&*i),
134+
Expr::Ident(Ident::from(&*i)),
135+
assign.right.take(),
136+
)),
137+
}
138+
.into();
139+
}
140+
141+
AssignTarget::Simple(left) => {
142+
let alias = alias_ident_for_simple_assign_tatget(left, "refs");
143+
self.vars.push(VarDeclarator {
144+
span: DUMMY_SP,
145+
name: alias.clone().into(),
146+
init: None,
147+
definite: false,
148+
});
149+
150+
// TODO: Check for computed.
151+
let right_expr = AssignExpr {
152+
span: assign.span,
153+
left: left.clone().into(),
154+
op: op!("="),
155+
right: assign.right.take(),
156+
}
157+
.into();
158+
159+
let var_expr = AssignExpr {
160+
span: DUMMY_SP,
161+
op: op!("="),
162+
left: alias.clone().into(),
163+
right: left.take().into(),
164+
}
165+
.into();
166+
167+
*e = AssignExpr {
168+
span: assign.span,
169+
op: op!("="),
170+
left: alias.clone().into(),
171+
right: Box::new(make_cond(
172+
self.c,
173+
assign.span,
174+
&alias,
175+
var_expr,
176+
right_expr,
177+
)),
178+
}
179+
.into();
180+
}
181+
182+
_ => {}
183+
}
184+
}
185+
186+
_ => {}
187+
}
188+
}
189+
190+
fn visit_mut_block_stmt_or_expr(&mut self, n: &mut BlockStmtOrExpr) {
191+
let vars = self.vars.take();
192+
n.visit_mut_children_with(self);
193+
194+
if !self.vars.is_empty() {
195+
if let BlockStmtOrExpr::Expr(expr) = n {
196+
// expr
197+
// { var decl = init; return expr; }
198+
let stmts = vec![
199+
VarDecl {
200+
span: DUMMY_SP,
201+
kind: VarDeclKind::Var,
202+
decls: self.vars.take(),
203+
declare: false,
204+
..Default::default()
205+
}
206+
.into(),
207+
Stmt::Return(ReturnStmt {
208+
span: DUMMY_SP,
209+
arg: Some(expr.take()),
210+
}),
211+
];
212+
*n = BlockStmtOrExpr::BlockStmt(BlockStmt {
213+
span: DUMMY_SP,
214+
stmts,
215+
..Default::default()
216+
});
217+
}
218+
}
219+
220+
self.vars = vars;
221+
}
222+
}
223+
224+
#[tracing::instrument(level = "debug", skip_all)]
225+
fn make_cond(c: Config, span: Span, alias: &Ident, var_expr: Expr, init: Box<Expr>) -> Expr {
226+
if c.no_document_all {
227+
CondExpr {
228+
span,
229+
test: BinExpr {
230+
span: DUMMY_SP,
231+
left: Box::new(var_expr),
232+
op: op!("!="),
233+
right: Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))),
234+
}
235+
.into(),
236+
cons: alias.clone().into(),
237+
alt: init,
238+
}
239+
} else {
240+
CondExpr {
241+
span,
242+
test: BinExpr {
243+
span: DUMMY_SP,
244+
left: Box::new(Expr::Bin(BinExpr {
245+
span: DUMMY_SP,
246+
left: Box::new(var_expr),
247+
op: op!("!=="),
248+
right: Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))),
249+
})),
250+
op: op!("&&"),
251+
right: Box::new(Expr::Bin(BinExpr {
252+
span: DUMMY_SP,
253+
left: Box::new(Expr::Ident(alias.clone())),
254+
op: op!("!=="),
255+
right: Expr::undefined(DUMMY_SP),
256+
})),
257+
}
258+
.into(),
259+
cons: alias.clone().into(),
260+
alt: init,
261+
}
262+
}
263+
.into()
264+
}
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
pub(crate) mod export_namespace_from;
2-
pub(crate) mod nullish_coalescing;

0 commit comments

Comments
 (0)