swc_ecma_compat_es2015/
typeof_symbol.rs1use swc_common::{util::take::Take, DUMMY_SP};
2use swc_ecma_ast::*;
3use swc_ecma_transforms_base::{helper, perf::Parallel};
4use swc_ecma_utils::{quote_str, ExprFactory};
5use swc_ecma_visit::{noop_visit_mut_type, visit_mut_pass, VisitMut, VisitMutWith};
6use swc_trace_macro::swc_trace;
7
8pub fn typeof_symbol() -> impl Pass {
9 visit_mut_pass(TypeOfSymbol)
10}
11
12#[derive(Clone, Copy)]
13struct TypeOfSymbol;
14
15#[swc_trace]
16impl Parallel for TypeOfSymbol {
17 fn merge(&mut self, _: Self) {}
18
19 fn create(&self) -> Self {
20 TypeOfSymbol
21 }
22}
23
24#[swc_trace]
25impl VisitMut for TypeOfSymbol {
26 noop_visit_mut_type!(fail);
27
28 fn visit_mut_bin_expr(&mut self, expr: &mut BinExpr) {
29 match expr.op {
30 op!("==") | op!("!=") | op!("===") | op!("!==") => {}
31 _ => {
32 expr.visit_mut_children_with(self);
33 return;
34 }
35 }
36
37 if let Expr::Unary(UnaryExpr {
38 op: op!("typeof"), ..
39 }) = *expr.left
40 {
41 if is_non_symbol_literal(&expr.right) {
42 return;
43 }
44 }
45 if let Expr::Unary(UnaryExpr {
46 op: op!("typeof"), ..
47 }) = *expr.right
48 {
49 if is_non_symbol_literal(&expr.left) {
50 return;
51 }
52 }
53
54 expr.visit_mut_children_with(self)
55 }
56
57 fn visit_mut_expr(&mut self, expr: &mut Expr) {
58 expr.visit_mut_children_with(self);
59
60 if let Expr::Unary(UnaryExpr {
61 span,
62 op: op!("typeof"),
63 arg,
64 }) = expr
65 {
66 match &**arg {
67 Expr::Ident(..) => {
68 let undefined_str: Box<Expr> = quote_str!("undefined").into();
69
70 let test = BinExpr {
71 span: DUMMY_SP,
72 op: op!("==="),
73 left: Box::new(
74 UnaryExpr {
75 span: DUMMY_SP,
76 op: op!("typeof"),
77 arg: arg.clone(),
78 }
79 .into(),
80 ),
81 right: undefined_str.clone(),
82 }
83 .into();
84
85 let call = CallExpr {
86 span: *span,
87 callee: helper!(*span, type_of),
88 args: vec![arg.take().as_arg()],
89 ..Default::default()
90 }
91 .into();
92
93 *expr = CondExpr {
94 span: *span,
95 test,
96 cons: undefined_str,
97 alt: Box::new(call),
98 }
99 .into();
100 }
101 _ => {
102 let call = CallExpr {
103 span: *span,
104 callee: helper!(*span, type_of),
105 args: vec![arg.take().as_arg()],
106
107 ..Default::default()
108 }
109 .into();
110
111 *expr = call;
112 }
113 }
114 }
115 }
116
117 fn visit_mut_fn_decl(&mut self, f: &mut FnDecl) {
118 if &f.ident.sym == "_type_of" {
119 return;
120 }
121
122 f.visit_mut_children_with(self);
123 }
124
125 fn visit_mut_function(&mut self, f: &mut Function) {
126 if let Some(body) = &f.body {
127 if let Some(Stmt::Expr(first)) = body.stmts.first() {
128 if let Expr::Lit(Lit::Str(s)) = &*first.expr {
129 match &*s.value {
130 "@swc/helpers - typeof" | "@babel/helpers - typeof" => return,
131 _ => {}
132 }
133 }
134 }
135 }
136
137 f.visit_mut_children_with(self);
138 }
139}
140
141#[tracing::instrument(level = "debug", skip_all)]
142fn is_non_symbol_literal(e: &Expr) -> bool {
143 match e {
144 Expr::Lit(Lit::Str(Str { value, .. })) => matches!(
145 &**value,
146 "undefined" | "boolean" | "number" | "string" | "function"
147 ),
148 _ => false,
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use swc_ecma_parser::Syntax;
155 use swc_ecma_transforms_testing::test;
156
157 use super::*;
158
159 test!(
160 Syntax::default(),
161 |_| typeof_symbol(),
162 dont_touch_non_symbol_comparison,
163 "typeof window !== 'undefined'"
164 );
165
166 test!(
167 Syntax::default(),
168 |_| typeof_symbol(),
169 dont_touch_non_symbol_comparison_02,
170 "'undefined' !== typeof window"
171 );
172
173 test!(
174 Syntax::default(),
175 |_| typeof_symbol(),
176 issue_1843_1,
177 "
178 function isUndef(type) {
179 return type === 'undefined';
180 }
181
182 var isWeb = !isUndef(typeof window) && 'onload' in window;
183 exports.isWeb = isWeb;
184 var isNode = !isUndef(typeof process) && !!(process.versions && process.versions.node);
185 exports.isNode = isNode;
186 var isWeex = !isUndef(typeof WXEnvironment) && WXEnvironment.platform !== 'Web';
187 exports.isWeex = isWeex;
188 "
189 );
190}