swc_ecma_minifier/compress/optimize/
arguments.rs1use std::iter::repeat_with;
2
3use swc_common::{util::take::Take, DUMMY_SP};
4use swc_ecma_ast::*;
5use swc_ecma_utils::{find_pat_ids, is_valid_prop_ident, private_ident};
6use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
7
8use super::Optimizer;
9use crate::compress::optimize::is_left_access_to_arguments;
10
11impl Optimizer<'_> {
13 pub(super) fn optimize_str_access_to_arguments(&mut self, e: &mut Expr) {
16 if !self.options.arguments {
17 return;
18 }
19
20 match e {
21 Expr::Member(MemberExpr { prop, .. }) => {
22 if let MemberProp::Computed(c) = prop {
23 if let Expr::Lit(Lit::Str(s)) = &mut *c.expr {
24 let Some(value) = s.value.as_str() else {
25 return;
26 };
27
28 if !value.starts_with(|c: char| c.is_ascii_alphabetic()) {
29 return;
30 }
31
32 if !is_valid_prop_ident(value) {
33 return;
34 }
35
36 self.changed = true;
37 report_change!("arguments: Optimizing computed access to arguments");
38
39 let name = s.take().value;
40 *prop = MemberProp::Ident(IdentName {
41 span: s.span,
42 sym: name.try_into_atom().unwrap(),
44 })
45 }
46 }
47 }
48
49 Expr::SuperProp(SuperPropExpr { prop, .. }) => {
50 if let SuperProp::Computed(c) = prop {
51 if let Expr::Lit(Lit::Str(s)) = &mut *c.expr {
52 let Some(value) = s.value.as_str() else {
53 return;
54 };
55 if !value.starts_with(|c: char| c.is_ascii_alphabetic()) {
56 return;
57 }
58
59 if !is_valid_prop_ident(value) {
60 return;
61 }
62
63 self.changed = true;
64 report_change!("arguments: Optimizing computed access to arguments");
65
66 let name = s.take().value;
67 *prop = SuperProp::Ident(IdentName {
68 span: s.span,
69 sym: name.try_into_atom().unwrap(),
71 })
72 }
73 }
74 }
75
76 _ => (),
77 };
78 }
79
80 pub(super) fn optimize_usage_of_arguments(&mut self, f: &mut Function) {
81 if !self.options.arguments {
82 return;
83 }
84
85 if f.params.iter().any(|param| match ¶m.pat {
86 Pat::Ident(BindingIdent {
87 id: Ident { sym, .. },
88 ..
89 }) if &**sym == "arguments" => true,
90 Pat::Ident(i) => self
91 .data
92 .vars
93 .get(&i.id.to_id())
94 .map(|v| v.declared_count >= 2)
95 .unwrap_or(false),
96 _ => true,
97 }) {
98 return;
99 }
100
101 {
102 let data: Vec<Id> = find_pat_ids(&f.body);
104 if data.iter().any(|id| {
105 if id.0 == "arguments" {
106 return true;
107 }
108 false
109 }) {
110 return;
111 }
112 }
113
114 let mut v = ArgReplacer {
115 params: &mut f.params,
116 changed: false,
117 keep_fargs: self.options.keep_fargs,
118 prevent: false,
119 };
120
121 f.body.visit_mut_children_with(&mut v);
123 f.body.visit_mut_children_with(&mut v);
124
125 self.changed |= v.changed;
126 }
127}
128
129struct ArgReplacer<'a> {
130 params: &'a mut Vec<Param>,
131 changed: bool,
132 keep_fargs: bool,
133 prevent: bool,
134}
135
136impl ArgReplacer<'_> {
137 fn inject_params_if_required(&mut self, idx: usize) {
138 if idx < self.params.len() || self.keep_fargs {
139 return;
140 }
141 let new_args = idx + 1 - self.params.len();
142
143 self.changed = true;
144 report_change!("arguments: Injecting {} parameters", new_args);
145 let mut start = self.params.len();
146 self.params.extend(
147 repeat_with(|| {
148 let p = Param {
149 span: DUMMY_SP,
150 decorators: Default::default(),
151 pat: private_ident!(format!("argument_{}", start)).into(),
152 };
153 start += 1;
154 p
155 })
156 .take(new_args),
157 )
158 }
159}
160
161impl VisitMut for ArgReplacer<'_> {
162 noop_visit_mut_type!(fail);
163
164 fn visit_mut_arrow_expr(&mut self, _: &mut ArrowExpr) {}
166
167 fn visit_mut_assign_expr(&mut self, n: &mut AssignExpr) {
168 n.visit_mut_children_with(self);
169
170 if is_left_access_to_arguments(&n.left) {
171 self.prevent = true;
172 }
173 }
174
175 fn visit_mut_expr(&mut self, n: &mut Expr) {
176 if self.prevent {
177 return;
178 }
179
180 n.visit_mut_children_with(self);
181
182 if let Expr::Member(MemberExpr {
183 obj,
184 prop: MemberProp::Computed(c),
185 ..
186 }) = n
187 {
188 match &**obj {
189 Expr::Ident(Ident { sym, .. }) if &**sym == "arguments" => {
190 match &*c.expr {
191 Expr::Lit(Lit::Str(Str { value, .. })) => {
192 let Some(value) = value.as_str() else {
193 return;
194 };
195 let idx = value.parse::<usize>();
196 let idx = match idx {
197 Ok(v) => v,
198 _ => return,
199 };
200
201 self.inject_params_if_required(idx);
202
203 if let Some(param) = self.params.get(idx) {
204 if let Pat::Ident(i) = ¶m.pat {
205 self.changed = true;
206 report_change!(
207 "arguments: Replacing access to arguments to normal \
208 reference"
209 );
210 *n = i.id.clone().into();
211 }
212 }
213 }
214 Expr::Lit(Lit::Num(Number { value, .. })) => {
215 if value.fract() != 0.0 {
216 return;
218 }
219
220 let idx = value.round() as i64 as usize;
221
222 self.inject_params_if_required(idx);
223
224 if let Some(param) = self.params.get(idx) {
226 if let Pat::Ident(i) = ¶m.pat {
227 report_change!(
228 "arguments: Replacing access to arguments to normal \
229 reference"
230 );
231 self.changed = true;
232 *n = i.id.clone().into();
233 }
234 }
235 }
236 _ => {}
237 }
238 }
239 _ => (),
240 }
241 }
242 }
243
244 fn visit_mut_function(&mut self, _: &mut Function) {}
246
247 fn visit_mut_member_expr(&mut self, n: &mut MemberExpr) {
248 if self.prevent {
249 return;
250 }
251
252 n.obj.visit_mut_with(self);
253
254 if let MemberProp::Computed(c) = &mut n.prop {
255 c.visit_mut_with(self);
256 }
257 }
258
259 fn visit_mut_super_prop_expr(&mut self, n: &mut SuperPropExpr) {
260 if self.prevent {
261 return;
262 }
263
264 if let SuperProp::Computed(c) = &mut n.prop {
265 c.visit_mut_with(self);
266 }
267 }
268}