UNPKG

563 kBJavaScriptView Raw
1/***********************************************************************
2
3 A JavaScript tokenizer / parser / beautifier / compressor.
4 https://github.com/mishoo/UglifyJS
5
6 -------------------------------- (C) ---------------------------------
7
8 Author: Mihai Bazon
9 <mihai.bazon@gmail.com>
10 http://mihai.bazon.net/blog
11
12 Distributed under the BSD license:
13
14 Copyright 2012 (c) Mihai Bazon <mihai.bazon@gmail.com>
15
16 Redistribution and use in source and binary forms, with or without
17 modification, are permitted provided that the following conditions
18 are met:
19
20 * Redistributions of source code must retain the above
21 copyright notice, this list of conditions and the following
22 disclaimer.
23
24 * Redistributions in binary form must reproduce the above
25 copyright notice, this list of conditions and the following
26 disclaimer in the documentation and/or other materials
27 provided with the distribution.
28
29 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
30 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
33 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
34 OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
35 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
36 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
37 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
38 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
39 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 SUCH DAMAGE.
41
42 ***********************************************************************/
43
44"use strict";
45
46function Compressor(options, false_by_default) {
47 if (!(this instanceof Compressor))
48 return new Compressor(options, false_by_default);
49 TreeTransformer.call(this, this.before, this.after);
50 this.options = defaults(options, {
51 annotations : !false_by_default,
52 arguments : !false_by_default,
53 arrows : !false_by_default,
54 assignments : !false_by_default,
55 awaits : !false_by_default,
56 booleans : !false_by_default,
57 collapse_vars : !false_by_default,
58 comparisons : !false_by_default,
59 conditionals : !false_by_default,
60 dead_code : !false_by_default,
61 default_values : !false_by_default,
62 directives : !false_by_default,
63 drop_console : false,
64 drop_debugger : !false_by_default,
65 evaluate : !false_by_default,
66 expression : false,
67 functions : !false_by_default,
68 global_defs : false,
69 hoist_exports : !false_by_default,
70 hoist_funs : false,
71 hoist_props : !false_by_default,
72 hoist_vars : false,
73 ie : false,
74 if_return : !false_by_default,
75 imports : !false_by_default,
76 inline : !false_by_default,
77 join_vars : !false_by_default,
78 keep_fargs : false_by_default,
79 keep_fnames : false,
80 keep_infinity : false,
81 loops : !false_by_default,
82 merge_vars : !false_by_default,
83 negate_iife : !false_by_default,
84 objects : !false_by_default,
85 optional_chains : !false_by_default,
86 passes : 1,
87 properties : !false_by_default,
88 pure_funcs : null,
89 pure_getters : !false_by_default && "strict",
90 reduce_funcs : !false_by_default,
91 reduce_vars : !false_by_default,
92 rests : !false_by_default,
93 sequences : !false_by_default,
94 side_effects : !false_by_default,
95 spreads : !false_by_default,
96 strings : !false_by_default,
97 switches : !false_by_default,
98 templates : !false_by_default,
99 top_retain : null,
100 toplevel : !!(options && options["top_retain"]),
101 typeofs : !false_by_default,
102 unsafe : false,
103 unsafe_comps : false,
104 unsafe_Function : false,
105 unsafe_math : false,
106 unsafe_proto : false,
107 unsafe_regexp : false,
108 unsafe_undefined: false,
109 unused : !false_by_default,
110 varify : !false_by_default,
111 webkit : false,
112 yields : !false_by_default,
113 }, true);
114 var evaluate = this.options["evaluate"];
115 this.eval_threshold = /eager/.test(evaluate) ? 1 / 0 : +evaluate;
116 var global_defs = this.options["global_defs"];
117 if (typeof global_defs == "object") for (var key in global_defs) {
118 if (/^@/.test(key) && HOP(global_defs, key)) {
119 global_defs[key.slice(1)] = parse(global_defs[key], {
120 expression: true
121 });
122 }
123 }
124 if (this.options["inline"] === true) this.options["inline"] = 3;
125 this.drop_fargs = this.options["keep_fargs"] ? return_false : function(lambda, parent) {
126 if (lambda.length_read) return false;
127 var name = lambda.name;
128 if (!name) return parent && parent.TYPE == "Call" && parent.expression === lambda;
129 if (name.fixed_value() !== lambda) return false;
130 var def = name.definition();
131 if (def.direct_access) return false;
132 var escaped = def.escaped;
133 return escaped && escaped.depth != 1;
134 };
135 var pure_funcs = this.options["pure_funcs"];
136 if (typeof pure_funcs == "function") {
137 this.pure_funcs = pure_funcs;
138 } else if (typeof pure_funcs == "string") {
139 this.pure_funcs = function(node) {
140 var expr;
141 if (node instanceof AST_Call) {
142 expr = node.expression;
143 } else if (node instanceof AST_Template) {
144 expr = node.tag;
145 }
146 return !(expr && pure_funcs === expr.print_to_string());
147 };
148 } else if (Array.isArray(pure_funcs)) {
149 this.pure_funcs = function(node) {
150 var expr;
151 if (node instanceof AST_Call) {
152 expr = node.expression;
153 } else if (node instanceof AST_Template) {
154 expr = node.tag;
155 }
156 return !(expr && member(expr.print_to_string(), pure_funcs));
157 };
158 } else {
159 this.pure_funcs = return_true;
160 }
161 var sequences = this.options["sequences"];
162 this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
163 var top_retain = this.options["top_retain"];
164 if (top_retain instanceof RegExp) {
165 this.top_retain = function(def) {
166 return top_retain.test(def.name);
167 };
168 } else if (typeof top_retain == "function") {
169 this.top_retain = top_retain;
170 } else if (top_retain) {
171 if (typeof top_retain == "string") {
172 top_retain = top_retain.split(/,/);
173 }
174 this.top_retain = function(def) {
175 return member(def.name, top_retain);
176 };
177 }
178 var toplevel = this.options["toplevel"];
179 this.toplevel = typeof toplevel == "string" ? {
180 funcs: /funcs/.test(toplevel),
181 vars: /vars/.test(toplevel)
182 } : {
183 funcs: toplevel,
184 vars: toplevel
185 };
186}
187
188Compressor.prototype = new TreeTransformer(function(node, descend, in_list) {
189 if (node._squeezed) return node;
190 var is_scope = node instanceof AST_Scope;
191 if (is_scope) {
192 node.hoist_properties(this);
193 node.hoist_declarations(this);
194 node.process_boolean_returns(this);
195 }
196 // Before https://github.com/mishoo/UglifyJS/pull/1602 AST_Node.optimize()
197 // would call AST_Node.transform() if a different instance of AST_Node is
198 // produced after OPT().
199 // This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
200 // Migrate and defer all children's AST_Node.transform() to below, which
201 // will now happen after this parent AST_Node has been properly substituted
202 // thus gives a consistent AST snapshot.
203 descend(node, this);
204 // Existing code relies on how AST_Node.optimize() worked, and omitting the
205 // following replacement call would result in degraded efficiency of both
206 // output and performance.
207 descend(node, this);
208 var opt = node.optimize(this);
209 if (is_scope && opt === node && !this.has_directive("use asm") && !opt.pinned()) {
210 opt.drop_unused(this);
211 if (opt.merge_variables(this)) opt.drop_unused(this);
212 descend(opt, this);
213 }
214 if (opt === node) opt._squeezed = true;
215 return opt;
216});
217Compressor.prototype.option = function(key) {
218 return this.options[key];
219};
220Compressor.prototype.exposed = function(def) {
221 if (def.exported) return true;
222 if (def.undeclared) return true;
223 if (!(def.global || def.scope.resolve() instanceof AST_Toplevel)) return false;
224 var toplevel = this.toplevel;
225 return !all(def.orig, function(sym) {
226 return toplevel[sym instanceof AST_SymbolDefun ? "funcs" : "vars"];
227 });
228};
229Compressor.prototype.compress = function(node) {
230 node = node.resolve_defines(this);
231 node.hoist_exports(this);
232 if (this.option("expression")) {
233 node.process_expression(true);
234 }
235 var merge_vars = this.options.merge_vars;
236 var passes = +this.options.passes || 1;
237 var min_count = 1 / 0;
238 var stopping = false;
239 var mangle = { ie: this.option("ie") };
240 for (var pass = 0; pass < passes; pass++) {
241 node.figure_out_scope(mangle);
242 if (pass > 0 || this.option("reduce_vars"))
243 node.reset_opt_flags(this);
244 this.options.merge_vars = merge_vars && (stopping || pass == passes - 1);
245 node = node.transform(this);
246 if (passes > 1) {
247 var count = 0;
248 node.walk(new TreeWalker(function() {
249 count++;
250 }));
251 AST_Node.info("pass {pass}: last_count: {min_count}, count: {count}", {
252 pass: pass,
253 min_count: min_count,
254 count: count,
255 });
256 if (count < min_count) {
257 min_count = count;
258 stopping = false;
259 } else if (stopping) {
260 break;
261 } else {
262 stopping = true;
263 }
264 }
265 }
266 if (this.option("expression")) {
267 node.process_expression(false);
268 }
269 return node;
270};
271
272(function(OPT) {
273 OPT(AST_Node, function(self, compressor) {
274 return self;
275 });
276
277 AST_Node.DEFMETHOD("equivalent_to", function(node) {
278 return this.TYPE == node.TYPE && this.print_to_string() == node.print_to_string();
279 });
280
281 AST_Toplevel.DEFMETHOD("hoist_exports", function(compressor) {
282 if (!compressor.option("hoist_exports")) return;
283 var body = this.body, props = [];
284 for (var i = 0; i < body.length; i++) {
285 var stat = body[i];
286 if (stat instanceof AST_ExportDeclaration) {
287 body[i] = stat = stat.body;
288 if (stat instanceof AST_Definitions) {
289 stat.definitions.forEach(function(defn) {
290 defn.name.match_symbol(export_symbol, true);
291 });
292 } else {
293 export_symbol(stat.name);
294 }
295 } else if (stat instanceof AST_ExportReferences) {
296 body.splice(i--, 1);
297 [].push.apply(props, stat.properties);
298 }
299 }
300 if (props.length) body.push(make_node(AST_ExportReferences, this, { properties: props }));
301
302 function export_symbol(sym) {
303 if (!(sym instanceof AST_SymbolDeclaration)) return;
304 var node = make_node(AST_SymbolExport, sym, sym);
305 node.alias = node.name;
306 props.push(node);
307 }
308 });
309
310 AST_Scope.DEFMETHOD("process_expression", function(insert, transform) {
311 var self = this;
312 var tt = new TreeTransformer(function(node) {
313 if (insert && node instanceof AST_SimpleStatement) {
314 return transform ? transform(node) : make_node(AST_Return, node, { value: node.body });
315 }
316 if (!insert && node instanceof AST_Return) {
317 return transform ? transform(node) : make_node(AST_SimpleStatement, node, {
318 body: node.value || make_node(AST_UnaryPrefix, node, {
319 operator: "void",
320 expression: make_node(AST_Number, node, { value: 0 }),
321 }),
322 });
323 }
324 if (node instanceof AST_Block) {
325 if (node instanceof AST_Lambda) {
326 if (node !== self) return node;
327 } else if (insert === "awaits" && node instanceof AST_Try) {
328 if (node.bfinally) return node;
329 }
330 for (var index = node.body.length; --index >= 0;) {
331 var stat = node.body[index];
332 if (!is_declaration(stat, true)) {
333 node.body[index] = stat.transform(tt);
334 break;
335 }
336 }
337 } else if (node instanceof AST_If) {
338 node.body = node.body.transform(tt);
339 if (node.alternative) {
340 node.alternative = node.alternative.transform(tt);
341 }
342 } else if (node instanceof AST_With) {
343 node.body = node.body.transform(tt);
344 }
345 return node;
346 });
347 self.transform(tt);
348 });
349
350 function read_property(obj, node) {
351 var key = node.get_property();
352 if (key instanceof AST_Node) return;
353 var value;
354 if (obj instanceof AST_Array) {
355 var elements = obj.elements;
356 if (key == "length") return make_node_from_constant(elements.length, obj);
357 if (typeof key == "number" && key in elements) value = elements[key];
358 } else if (obj instanceof AST_Lambda) {
359 if (key == "length") {
360 obj.length_read = true;
361 return make_node_from_constant(obj.argnames.length, obj);
362 }
363 } else if (obj instanceof AST_Object) {
364 key = "" + key;
365 var props = obj.properties;
366 for (var i = props.length; --i >= 0;) {
367 var prop = props[i];
368 if (!can_hoist_property(prop)) return;
369 if (!value && props[i].key === key) value = props[i].value;
370 }
371 }
372 return value instanceof AST_SymbolRef && value.fixed_value() || value;
373 }
374
375 function is_read_only_fn(value, name) {
376 if (value instanceof AST_Boolean) return native_fns.Boolean[name];
377 if (value instanceof AST_Number) return native_fns.Number[name];
378 if (value instanceof AST_String) return native_fns.String[name];
379 if (name == "valueOf") return false;
380 if (value instanceof AST_Array) return native_fns.Array[name];
381 if (value instanceof AST_Lambda) return native_fns.Function[name];
382 if (value instanceof AST_Object) return native_fns.Object[name];
383 if (value instanceof AST_RegExp) return native_fns.RegExp[name] && !value.value.global;
384 }
385
386 function is_modified(compressor, tw, node, value, level, immutable, recursive) {
387 var parent = tw.parent(level);
388 if (compressor.option("unsafe") && parent instanceof AST_Dot && is_read_only_fn(value, parent.property)) {
389 return;
390 }
391 var lhs = is_lhs(node, parent);
392 if (lhs) return lhs;
393 if (level == 0 && value && value.is_constant()) return;
394 if (parent instanceof AST_Array) return is_modified(compressor, tw, parent, parent, level + 1);
395 if (parent instanceof AST_Assign) switch (parent.operator) {
396 case "=":
397 return is_modified(compressor, tw, parent, value, level + 1, immutable, recursive);
398 case "&&=":
399 case "||=":
400 case "??=":
401 return is_modified(compressor, tw, parent, parent, level + 1);
402 default:
403 return;
404 }
405 if (parent instanceof AST_Binary) {
406 if (!lazy_op[parent.operator]) return;
407 return is_modified(compressor, tw, parent, parent, level + 1);
408 }
409 if (parent instanceof AST_Call) {
410 return !immutable
411 && parent.expression === node
412 && !parent.is_expr_pure(compressor)
413 && (!(value instanceof AST_LambdaExpression) || !(parent instanceof AST_New) && value.contains_this());
414 }
415 if (parent instanceof AST_Conditional) {
416 if (parent.condition === node) return;
417 return is_modified(compressor, tw, parent, parent, level + 1);
418 }
419 if (parent instanceof AST_ForEnumeration) return parent.init === node;
420 if (parent instanceof AST_ObjectKeyVal) {
421 if (parent.value !== node) return;
422 var obj = tw.parent(level + 1);
423 return is_modified(compressor, tw, obj, obj, level + 2);
424 }
425 if (parent instanceof AST_PropAccess) {
426 if (parent.expression !== node) return;
427 var prop = read_property(value, parent);
428 return (!immutable || recursive) && is_modified(compressor, tw, parent, prop, level + 1);
429 }
430 if (parent instanceof AST_Sequence) {
431 if (parent.tail_node() !== node) return;
432 return is_modified(compressor, tw, parent, value, level + 1, immutable, recursive);
433 }
434 }
435
436 function is_lambda(node) {
437 return node instanceof AST_Class || node instanceof AST_Lambda;
438 }
439
440 function safe_for_extends(node) {
441 return node instanceof AST_Class || node instanceof AST_Defun || node instanceof AST_Function;
442 }
443
444 function is_arguments(def) {
445 return def.name == "arguments" && def.scope.uses_arguments;
446 }
447
448 function is_funarg(def) {
449 return def.orig[0] instanceof AST_SymbolFunarg || def.orig[1] instanceof AST_SymbolFunarg;
450 }
451
452 function cross_scope(def, sym) {
453 do {
454 if (def === sym) return false;
455 if (sym instanceof AST_Scope) return true;
456 } while (sym = sym.parent_scope);
457 }
458
459 function can_drop_symbol(ref, compressor, keep_lambda) {
460 var def = ref.redef || ref.definition();
461 if (ref.in_arg && is_funarg(def)) return false;
462 return all(def.orig, function(sym) {
463 if (sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet) {
464 return compressor && can_varify(compressor, sym);
465 }
466 return !(keep_lambda && sym instanceof AST_SymbolLambda);
467 });
468 }
469
470 function has_escaped(d, scope, node, parent) {
471 if (parent instanceof AST_Assign) return parent.operator == "=" && parent.right === node;
472 if (parent instanceof AST_Call) return parent.expression !== node || parent instanceof AST_New;
473 if (parent instanceof AST_Exit) return parent.value === node && scope.resolve() !== d.scope.resolve();
474 if (parent instanceof AST_VarDef) return parent.value === node;
475 }
476
477 var RE_POSITIVE_INTEGER = /^(0|[1-9][0-9]*)$/;
478 (function(def) {
479 def(AST_Node, noop);
480
481 function reset_def(tw, compressor, def) {
482 def.assignments = 0;
483 def.bool_fn = 0;
484 def.cross_loop = false;
485 def.direct_access = false;
486 def.escaped = [];
487 def.fixed = !def.const_redefs
488 && !def.scope.pinned()
489 && !compressor.exposed(def)
490 && !(def.init instanceof AST_LambdaExpression && def.init !== def.scope)
491 && def.init;
492 def.reassigned = 0;
493 def.recursive_refs = 0;
494 def.references = [];
495 def.should_replace = undefined;
496 def.single_use = undefined;
497 }
498
499 function reset_block_variables(tw, compressor, scope) {
500 scope.variables.each(function(def) {
501 reset_def(tw, compressor, def);
502 });
503 }
504
505 function reset_variables(tw, compressor, scope) {
506 scope.fn_defs = [];
507 scope.variables.each(function(def) {
508 reset_def(tw, compressor, def);
509 var init = def.init;
510 if (init instanceof AST_LambdaDefinition) {
511 scope.fn_defs.push(init);
512 init.safe_ids = null;
513 }
514 if (def.fixed === null) {
515 def.safe_ids = tw.safe_ids;
516 mark(tw, def);
517 } else if (def.fixed) {
518 tw.loop_ids[def.id] = tw.in_loop;
519 mark(tw, def);
520 }
521 });
522 scope.may_call_this = function() {
523 scope.may_call_this = scope.contains_this() ? return_true : return_false;
524 };
525 if (scope.uses_arguments) scope.each_argname(function(node) {
526 node.definition().last_ref = false;
527 });
528 if (compressor.option("ie")) scope.variables.each(function(def) {
529 var d = def.orig[0].definition();
530 if (d !== def) d.fixed = false;
531 });
532 }
533
534 function safe_to_visit(tw, fn) {
535 var marker = fn.safe_ids;
536 return marker === undefined || marker === tw.safe_ids;
537 }
538
539 function walk_fn_def(tw, fn) {
540 var was_scanning = tw.fn_scanning;
541 tw.fn_scanning = fn;
542 fn.walk(tw);
543 tw.fn_scanning = was_scanning;
544 }
545
546 function revisit_fn_def(tw, fn) {
547 fn.enclosed.forEach(function(d) {
548 if (fn.variables.get(d.name) === d) return;
549 if (safe_to_read(tw, d)) return;
550 d.single_use = false;
551 var fixed = d.fixed;
552 if (typeof fixed == "function") fixed = fixed();
553 if (fixed instanceof AST_Lambda && fixed.safe_ids !== undefined) return;
554 d.fixed = false;
555 });
556 }
557
558 function mark_fn_def(tw, def, fn) {
559 var marker = fn.safe_ids;
560 if (marker === undefined) return;
561 if (marker === false) return;
562 if (fn.parent_scope.resolve().may_call_this === return_true) {
563 if (member(fn, tw.fn_visited)) revisit_fn_def(tw, fn);
564 } else if (marker) {
565 var visited = member(fn, tw.fn_visited);
566 if (marker === tw.safe_ids) {
567 if (!visited) walk_fn_def(tw, fn);
568 } else if (visited) {
569 revisit_fn_def(tw, fn);
570 } else {
571 fn.safe_ids = false;
572 }
573 } else if (tw.fn_scanning && tw.fn_scanning !== def.scope.resolve()) {
574 fn.safe_ids = false;
575 } else {
576 fn.safe_ids = tw.safe_ids;
577 walk_fn_def(tw, fn);
578 }
579 }
580
581 function pop_scope(tw, scope) {
582 var fn_defs = scope.fn_defs;
583 var tangled = scope.may_call_this === return_true ? fn_defs : fn_defs.filter(function(fn) {
584 if (fn.safe_ids === false) return true;
585 fn.safe_ids = tw.safe_ids;
586 walk_fn_def(tw, fn);
587 return false;
588 });
589 pop(tw);
590 tangled.forEach(function(fn) {
591 fn.safe_ids = tw.safe_ids;
592 walk_fn_def(tw, fn);
593 });
594 fn_defs.forEach(function(fn) {
595 fn.safe_ids = undefined;
596 });
597 scope.fn_defs = undefined;
598 scope.may_call_this = undefined;
599 }
600
601 function push(tw) {
602 tw.safe_ids = Object.create(tw.safe_ids);
603 }
604
605 function pop(tw) {
606 tw.safe_ids = Object.getPrototypeOf(tw.safe_ids);
607 }
608
609 function mark(tw, def) {
610 tw.safe_ids[def.id] = {};
611 }
612
613 function push_ref(def, ref) {
614 def.references.push(ref);
615 if (def.last_ref !== false) def.last_ref = ref;
616 }
617
618 function safe_to_read(tw, def) {
619 if (def.single_use == "m") return false;
620 var safe = tw.safe_ids[def.id];
621 if (safe) {
622 var in_order = HOP(tw.safe_ids, def.id);
623 if (!in_order) safe.read = safe.read && safe.read !== tw.safe_ids ? true : tw.safe_ids;
624 if (def.fixed == null) {
625 if (is_arguments(def)) return false;
626 if (def.global && def.name == "arguments") return false;
627 tw.loop_ids[def.id] = null;
628 def.fixed = make_node(AST_Undefined, def.orig[0]);
629 if (in_order) def.safe_ids = undefined;
630 return true;
631 }
632 return !safe.assign || safe.assign === tw.safe_ids;
633 }
634 return def.fixed instanceof AST_LambdaDefinition;
635 }
636
637 function safe_to_assign(tw, def, declare) {
638 if (!declare) {
639 if (is_funarg(def) && def.scope.uses_arguments && !tw.has_directive("use strict")) return false;
640 if (!all(def.orig, function(sym) {
641 return !(sym instanceof AST_SymbolConst);
642 })) return false;
643 }
644 if (def.fixed === undefined) return declare || all(def.orig, function(sym) {
645 return !(sym instanceof AST_SymbolLet);
646 });
647 if (def.fixed === false || def.fixed === 0) return false;
648 var safe = tw.safe_ids[def.id];
649 if (def.safe_ids) {
650 def.safe_ids[def.id] = false;
651 def.safe_ids = undefined;
652 return def.fixed === null || HOP(tw.safe_ids, def.id) && !safe.read;
653 }
654 if (!HOP(tw.safe_ids, def.id)) {
655 if (!safe) return false;
656 if (safe.read) {
657 var scope = tw.find_parent(AST_BlockScope);
658 if (scope instanceof AST_Class) return false;
659 if (def.scope.resolve() !== scope.resolve()) return false;
660 }
661 safe.assign = safe.assign && safe.assign !== tw.safe_ids ? true : tw.safe_ids;
662 }
663 if (def.fixed != null && safe.read) {
664 if (safe.read !== tw.safe_ids) return false;
665 if (tw.loop_ids[def.id] !== tw.in_loop) return false;
666 }
667 return safe_to_read(tw, def) && all(def.orig, function(sym) {
668 return !(sym instanceof AST_SymbolLambda);
669 });
670 }
671
672 function make_ref(ref, fixed) {
673 var node = make_node(AST_SymbolRef, ref, ref);
674 node.fixed = fixed || make_node(AST_Undefined, ref);
675 return node;
676 }
677
678 function ref_once(compressor, def) {
679 return compressor.option("unused")
680 && !def.scope.pinned()
681 && def.single_use !== false
682 && def.references.length - def.recursive_refs == 1
683 && !(is_funarg(def) && def.scope.uses_arguments);
684 }
685
686 function is_immutable(value) {
687 if (!value) return false;
688 if (value instanceof AST_Assign) {
689 var op = value.operator;
690 return op == "=" ? is_immutable(value.right) : !lazy_op[op.slice(0, -1)];
691 }
692 if (value instanceof AST_Sequence) return is_immutable(value.tail_node());
693 return value.is_constant() || is_lambda(value) || value instanceof AST_ObjectIdentity;
694 }
695
696 function value_in_use(node, parent) {
697 if (parent instanceof AST_Array) return true;
698 if (parent instanceof AST_Binary) return lazy_op[parent.operator];
699 if (parent instanceof AST_Conditional) return parent.condition !== node;
700 if (parent instanceof AST_Sequence) return parent.tail_node() === node;
701 if (parent instanceof AST_Spread) return true;
702 }
703
704 function mark_escaped(tw, d, scope, node, value, level, depth) {
705 var parent = tw.parent(level);
706 if (value && value.is_constant()) return;
707 if (has_escaped(d, scope, node, parent)) {
708 d.escaped.push(parent);
709 if (depth > 1 && !(value && value.is_constant_expression(scope))) depth = 1;
710 if (!d.escaped.depth || d.escaped.depth > depth) d.escaped.depth = depth;
711 if (d.scope.resolve() !== scope.resolve()) d.escaped.cross_scope = true;
712 if (d.fixed) d.fixed.escaped = d.escaped;
713 return;
714 } else if (value_in_use(node, parent)) {
715 mark_escaped(tw, d, scope, parent, parent, level + 1, depth);
716 } else if (parent instanceof AST_ObjectKeyVal && parent.value === node) {
717 var obj = tw.parent(level + 1);
718 mark_escaped(tw, d, scope, obj, obj, level + 2, depth);
719 } else if (parent instanceof AST_PropAccess && parent.expression === node) {
720 value = read_property(value, parent);
721 mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1);
722 if (value) return;
723 }
724 if (level > 0) return;
725 if (parent instanceof AST_Call && parent.expression === node) return;
726 if (parent instanceof AST_Sequence && parent.tail_node() !== node) return;
727 if (parent instanceof AST_SimpleStatement) return;
728 if (parent instanceof AST_Unary && !unary_side_effects[parent.operator]) return;
729 d.direct_access = true;
730 if (d.fixed) d.fixed.direct_access = true;
731 }
732
733 function mark_assignment_to_arguments(node) {
734 if (!(node instanceof AST_Sub)) return;
735 var expr = node.expression;
736 if (!(expr instanceof AST_SymbolRef)) return;
737 var def = expr.definition();
738 if (!is_arguments(def)) return;
739 var key = node.property;
740 if (key.is_constant()) key = key.value;
741 if (!(key instanceof AST_Node) && !RE_POSITIVE_INTEGER.test(key)) return;
742 def.reassigned++;
743 (key instanceof AST_Node ? def.scope.argnames : [ def.scope.argnames[key] ]).forEach(function(argname) {
744 if (argname instanceof AST_SymbolFunarg) argname.definition().fixed = false;
745 });
746 }
747
748 function scan_declaration(tw, compressor, lhs, fixed, visit) {
749 var scanner = new TreeWalker(function(node) {
750 if (node instanceof AST_DefaultValue) {
751 reset_flags(node);
752 push(tw);
753 node.value.walk(tw);
754 pop(tw);
755 var save = fixed;
756 if (save) fixed = function() {
757 var value = save();
758 var ev;
759 if (is_undefined(value, compressor)
760 || (ev = fuzzy_eval(compressor, value, true)) === undefined) {
761 return make_sequence(node, [ value, node.value ]);
762 }
763 return ev instanceof AST_Node ? node : value;
764 };
765 node.name.walk(scanner);
766 fixed = save;
767 return true;
768 }
769 if (node instanceof AST_DestructuredArray) {
770 reset_flags(node);
771 var save = fixed;
772 node.elements.forEach(function(node, index) {
773 if (node instanceof AST_Hole) return reset_flags(node);
774 if (save) fixed = function() {
775 return make_node(AST_Sub, node, {
776 expression: save(),
777 property: make_node(AST_Number, node, { value: index }),
778 });
779 };
780 node.walk(scanner);
781 });
782 if (node.rest) {
783 var fixed_node;
784 if (save) fixed = compressor.option("rests") && function() {
785 var value = save();
786 if (!(value instanceof AST_Array)) return node;
787 if (!fixed_node) fixed_node = make_node(AST_Array, node);
788 fixed_node.elements = value.elements.slice(node.elements.length);
789 return fixed_node;
790 };
791 node.rest.walk(scanner);
792 }
793 fixed = save;
794 return true;
795 }
796 if (node instanceof AST_DestructuredObject) {
797 reset_flags(node);
798 var save = fixed;
799 node.properties.forEach(function(node) {
800 reset_flags(node);
801 if (node.key instanceof AST_Node) {
802 push(tw);
803 node.key.walk(tw);
804 pop(tw);
805 }
806 if (save) fixed = function() {
807 var key = node.key;
808 var type = AST_Sub;
809 if (typeof key == "string") {
810 if (is_identifier_string(key)) {
811 type = AST_Dot;
812 } else {
813 key = make_node_from_constant(key, node);
814 }
815 }
816 return make_node(type, node, {
817 expression: save(),
818 property: key
819 });
820 };
821 node.value.walk(scanner);
822 });
823 if (node.rest) {
824 fixed = false;
825 node.rest.walk(scanner);
826 }
827 fixed = save;
828 return true;
829 }
830 visit(node, fixed, function() {
831 var save_len = tw.stack.length;
832 for (var i = 0, len = scanner.stack.length - 1; i < len; i++) {
833 tw.stack.push(scanner.stack[i]);
834 }
835 node.walk(tw);
836 tw.stack.length = save_len;
837 });
838 return true;
839 });
840 lhs.walk(scanner);
841 }
842
843 function reduce_iife(tw, descend, compressor) {
844 var fn = this;
845 fn.inlined = false;
846 var iife = tw.parent();
847 var hit = is_async(fn) || is_generator(fn);
848 var aborts = false;
849 fn.walk(new TreeWalker(function(node) {
850 if (hit) return aborts = true;
851 if (node instanceof AST_Return) return hit = true;
852 if (node instanceof AST_Scope && node !== fn) return true;
853 }));
854 if (aborts) push(tw);
855 reset_variables(tw, compressor, fn);
856 // Virtually turn IIFE parameters into variable definitions:
857 // (function(a,b) {...})(c,d) ---> (function() {var a=c,b=d; ...})()
858 // So existing transformation rules can work on them.
859 var safe = !fn.uses_arguments || tw.has_directive("use strict");
860 fn.argnames.forEach(function(argname, i) {
861 var value = iife.args[i];
862 scan_declaration(tw, compressor, argname, function() {
863 var j = fn.argnames.indexOf(argname);
864 var arg = j < 0 ? value : iife.args[j];
865 if (arg instanceof AST_Sequence && arg.expressions.length < 2) arg = arg.expressions[0];
866 return arg || make_node(AST_Undefined, iife);
867 }, visit);
868 });
869 var rest = fn.rest, fixed_node;
870 if (rest) scan_declaration(tw, compressor, rest, compressor.option("rests") && function() {
871 if (fn.rest !== rest) return rest;
872 if (!fixed_node) fixed_node = make_node(AST_Array, fn);
873 fixed_node.elements = iife.args.slice(fn.argnames.length);
874 return fixed_node;
875 }, visit);
876 walk_lambda(fn, tw);
877 var safe_ids = tw.safe_ids;
878 pop_scope(tw, fn);
879 if (!aborts) tw.safe_ids = safe_ids;
880 return true;
881
882 function visit(node, fixed) {
883 var d = node.definition();
884 if (fixed && safe && d.fixed === undefined) {
885 mark(tw, d);
886 tw.loop_ids[d.id] = tw.in_loop;
887 d.fixed = fixed;
888 d.fixed.assigns = [ node ];
889 } else {
890 d.fixed = false;
891 }
892 }
893 }
894
895 def(AST_Assign, function(tw, descend, compressor) {
896 var node = this;
897 var left = node.left;
898 var right = node.right;
899 var ld = left instanceof AST_SymbolRef && left.definition();
900 var scan = ld || left instanceof AST_Destructured;
901 switch (node.operator) {
902 case "=":
903 if (left.equivalent_to(right) && !left.has_side_effects(compressor)) {
904 right.walk(tw);
905 walk_prop(left);
906 node.redundant = true;
907 return true;
908 }
909 if (ld && right instanceof AST_LambdaExpression) {
910 walk_assign();
911 right.parent_scope.resolve().fn_defs.push(right);
912 right.safe_ids = null;
913 if (!ld.fixed || !node.write_only) mark_fn_def(tw, ld, right);
914 return true;
915 }
916 if (scan) {
917 right.walk(tw);
918 walk_assign();
919 return true;
920 }
921 mark_assignment_to_arguments(left);
922 return;
923 case "&&=":
924 case "||=":
925 case "??=":
926 var lazy = true;
927 default:
928 if (!scan) {
929 mark_assignment_to_arguments(left);
930 return walk_lazy();
931 }
932 ld.assignments++;
933 var fixed = ld.fixed;
934 if (is_modified(compressor, tw, node, node, 0)) {
935 ld.fixed = false;
936 return walk_lazy();
937 }
938 var safe = safe_to_read(tw, ld);
939 if (lazy) push(tw);
940 right.walk(tw);
941 if (lazy) pop(tw);
942 if (safe && !left.in_arg && safe_to_assign(tw, ld)) {
943 push_ref(ld, left);
944 mark(tw, ld);
945 if (ld.single_use) ld.single_use = false;
946 left.fixed = ld.fixed = function() {
947 return make_node(AST_Binary, node, {
948 operator: node.operator.slice(0, -1),
949 left: make_ref(left, fixed),
950 right: node.right,
951 });
952 };
953 left.fixed.assigns = !fixed || !fixed.assigns ? [] : fixed.assigns.slice();
954 left.fixed.assigns.push(node);
955 } else {
956 left.walk(tw);
957 ld.fixed = false;
958 }
959 return true;
960 }
961
962 function walk_prop(lhs) {
963 if (lhs instanceof AST_Dot) {
964 walk_prop(lhs.expression);
965 } else if (lhs instanceof AST_Sub) {
966 walk_prop(lhs.expression);
967 lhs.property.walk(tw);
968 } else if (lhs instanceof AST_SymbolRef) {
969 var d = lhs.definition();
970 push_ref(d, lhs);
971 if (d.fixed) {
972 lhs.fixed = d.fixed;
973 if (lhs.fixed.assigns) {
974 lhs.fixed.assigns.push(node);
975 } else {
976 lhs.fixed.assigns = [ node ];
977 }
978 }
979 } else {
980 lhs.walk(tw);
981 }
982 }
983
984 function walk_assign() {
985 var recursive = ld && recursive_ref(tw, ld);
986 var modified = is_modified(compressor, tw, node, right, 0, is_immutable(right), recursive);
987 scan_declaration(tw, compressor, left, function() {
988 return node.right;
989 }, function(sym, fixed, walk) {
990 if (!(sym instanceof AST_SymbolRef)) {
991 mark_assignment_to_arguments(sym);
992 walk();
993 return;
994 }
995 var d = sym.definition();
996 d.assignments++;
997 if (!fixed || sym.in_arg || !safe_to_assign(tw, d)) {
998 walk();
999 d.fixed = false;
1000 } else if (modified) {
1001 walk();
1002 d.fixed = 0;
1003 } else {
1004 push_ref(d, sym);
1005 mark(tw, d);
1006 if (left instanceof AST_Destructured
1007 || d.orig.length == 1 && d.orig[0] instanceof AST_SymbolDefun) {
1008 d.single_use = false;
1009 }
1010 tw.loop_ids[d.id] = tw.in_loop;
1011 sym.fixed = d.fixed = fixed;
1012 sym.fixed.assigns = [ node ];
1013 mark_escaped(tw, d, sym.scope, node, right, 0, 1);
1014 }
1015 });
1016 }
1017
1018 function walk_lazy() {
1019 if (!lazy) return;
1020 left.walk(tw);
1021 push(tw);
1022 right.walk(tw);
1023 pop(tw);
1024 return true;
1025 }
1026 });
1027 def(AST_Binary, function(tw) {
1028 if (!lazy_op[this.operator]) return;
1029 this.left.walk(tw);
1030 push(tw);
1031 this.right.walk(tw);
1032 pop(tw);
1033 return true;
1034 });
1035 def(AST_BlockScope, function(tw, descend, compressor) {
1036 reset_block_variables(tw, compressor, this);
1037 });
1038 def(AST_Call, function(tw, descend) {
1039 var node = this;
1040 var exp = node.expression;
1041 if (exp instanceof AST_LambdaExpression) {
1042 var iife = is_iife_single(node);
1043 node.args.forEach(function(arg) {
1044 arg.walk(tw);
1045 if (arg instanceof AST_Spread) iife = false;
1046 });
1047 if (iife) exp.reduce_vars = reduce_iife;
1048 exp.walk(tw);
1049 if (iife) delete exp.reduce_vars;
1050 return true;
1051 }
1052 if (node.TYPE == "Call" && tw.in_boolean_context()) {
1053 if (exp instanceof AST_SymbolRef) {
1054 exp.definition().bool_fn++;
1055 } else if (exp instanceof AST_Assign && exp.operator == "=" && exp.left instanceof AST_SymbolRef) {
1056 exp.left.definition().bool_fn++;
1057 }
1058 }
1059 exp.walk(tw);
1060 var optional = node.optional;
1061 if (optional) push(tw);
1062 node.args.forEach(function(arg) {
1063 arg.walk(tw);
1064 });
1065 if (optional) pop(tw);
1066 var fixed = exp instanceof AST_SymbolRef && exp.fixed_value();
1067 if (fixed instanceof AST_Lambda) {
1068 mark_fn_def(tw, exp.definition(), fixed);
1069 } else {
1070 tw.find_parent(AST_Scope).may_call_this();
1071 }
1072 return true;
1073 });
1074 def(AST_Class, function(tw, descend, compressor) {
1075 var node = this;
1076 reset_block_variables(tw, compressor, node);
1077 if (node.extends) node.extends.walk(tw);
1078 var props = node.properties.filter(function(prop) {
1079 reset_flags(prop);
1080 if (prop.key instanceof AST_Node) prop.key.walk(tw);
1081 return prop.value;
1082 });
1083 if (node.name) {
1084 var d = node.name.definition();
1085 var parent = tw.parent();
1086 if (parent instanceof AST_ExportDeclaration || parent instanceof AST_ExportDefault) d.single_use = false;
1087 if (safe_to_assign(tw, d, true)) {
1088 mark(tw, d);
1089 tw.loop_ids[d.id] = tw.in_loop;
1090 d.fixed = function() {
1091 return node;
1092 };
1093 d.fixed.assigns = [ node ];
1094 if (!is_safe_lexical(d)) d.single_use = false;
1095 } else {
1096 d.fixed = false;
1097 }
1098 }
1099 props.forEach(function(prop) {
1100 if (!prop.static || prop instanceof AST_ClassField && prop.value.contains_this()) {
1101 push(tw);
1102 prop.value.walk(tw);
1103 pop(tw);
1104 } else {
1105 prop.value.walk(tw);
1106 }
1107 });
1108 return true;
1109 });
1110 def(AST_Conditional, function(tw) {
1111 this.condition.walk(tw);
1112 push(tw);
1113 this.consequent.walk(tw);
1114 pop(tw);
1115 push(tw);
1116 this.alternative.walk(tw);
1117 pop(tw);
1118 return true;
1119 });
1120 def(AST_DefaultValue, function(tw) {
1121 this.name.walk(tw);
1122 push(tw);
1123 this.value.walk(tw);
1124 pop(tw);
1125 return true;
1126 });
1127 def(AST_Do, function(tw) {
1128 var save_loop = tw.in_loop;
1129 tw.in_loop = this;
1130 push(tw);
1131 this.body.walk(tw);
1132 if (has_loop_control(this, tw.parent())) {
1133 pop(tw);
1134 push(tw);
1135 }
1136 this.condition.walk(tw);
1137 pop(tw);
1138 tw.in_loop = save_loop;
1139 return true;
1140 });
1141 def(AST_For, function(tw, descend, compressor) {
1142 var node = this;
1143 reset_block_variables(tw, compressor, node);
1144 if (node.init) node.init.walk(tw);
1145 var save_loop = tw.in_loop;
1146 tw.in_loop = node;
1147 push(tw);
1148 if (node.condition) node.condition.walk(tw);
1149 node.body.walk(tw);
1150 if (node.step) {
1151 if (has_loop_control(node, tw.parent())) {
1152 pop(tw);
1153 push(tw);
1154 }
1155 node.step.walk(tw);
1156 }
1157 pop(tw);
1158 tw.in_loop = save_loop;
1159 return true;
1160 });
1161 def(AST_ForEnumeration, function(tw, descend, compressor) {
1162 var node = this;
1163 reset_block_variables(tw, compressor, node);
1164 node.object.walk(tw);
1165 var save_loop = tw.in_loop;
1166 tw.in_loop = node;
1167 push(tw);
1168 var init = node.init;
1169 if (init instanceof AST_Definitions) {
1170 init.definitions[0].name.mark_symbol(function(node) {
1171 if (node instanceof AST_SymbolDeclaration) {
1172 var def = node.definition();
1173 def.assignments++;
1174 def.fixed = false;
1175 }
1176 }, tw);
1177 } else if (init instanceof AST_Destructured || init instanceof AST_SymbolRef) {
1178 init.mark_symbol(function(node) {
1179 if (node instanceof AST_SymbolRef) {
1180 var def = node.definition();
1181 push_ref(def, node);
1182 def.assignments++;
1183 if (!node.is_immutable()) def.fixed = false;
1184 }
1185 }, tw);
1186 } else {
1187 init.walk(tw);
1188 }
1189 node.body.walk(tw);
1190 pop(tw);
1191 tw.in_loop = save_loop;
1192 return true;
1193 });
1194 def(AST_If, function(tw) {
1195 this.condition.walk(tw);
1196 push(tw);
1197 this.body.walk(tw);
1198 pop(tw);
1199 if (this.alternative) {
1200 push(tw);
1201 this.alternative.walk(tw);
1202 pop(tw);
1203 }
1204 return true;
1205 });
1206 def(AST_LabeledStatement, function(tw) {
1207 push(tw);
1208 this.body.walk(tw);
1209 pop(tw);
1210 return true;
1211 });
1212 def(AST_Lambda, function(tw, descend, compressor) {
1213 var fn = this;
1214 if (!safe_to_visit(tw, fn)) return true;
1215 if (!push_uniq(tw.fn_visited, fn)) return true;
1216 fn.inlined = false;
1217 push(tw);
1218 reset_variables(tw, compressor, fn);
1219 descend();
1220 pop_scope(tw, fn);
1221 if (fn.name) mark_escaped(tw, fn.name.definition(), fn, fn.name, fn, 0, 1);
1222 return true;
1223 });
1224 def(AST_LambdaDefinition, function(tw, descend, compressor) {
1225 var fn = this;
1226 var def = fn.name.definition();
1227 var parent = tw.parent();
1228 if (parent instanceof AST_ExportDeclaration || parent instanceof AST_ExportDefault) def.single_use = false;
1229 if (!safe_to_visit(tw, fn)) return true;
1230 if (!push_uniq(tw.fn_visited, fn)) return true;
1231 fn.inlined = false;
1232 push(tw);
1233 reset_variables(tw, compressor, fn);
1234 descend();
1235 pop_scope(tw, fn);
1236 return true;
1237 });
1238 def(AST_Sub, function(tw) {
1239 if (!this.optional) return;
1240 this.expression.walk(tw);
1241 push(tw);
1242 this.property.walk(tw);
1243 pop(tw);
1244 return true;
1245 });
1246 def(AST_Switch, function(tw, descend, compressor) {
1247 var node = this;
1248 reset_block_variables(tw, compressor, node);
1249 node.expression.walk(tw);
1250 var first = true;
1251 node.body.forEach(function(branch) {
1252 if (branch instanceof AST_Default) return;
1253 branch.expression.walk(tw);
1254 if (first) {
1255 first = false;
1256 push(tw);
1257 }
1258 })
1259 if (!first) pop(tw);
1260 walk_body(node, tw);
1261 return true;
1262 });
1263 def(AST_SwitchBranch, function(tw) {
1264 push(tw);
1265 walk_body(this, tw);
1266 pop(tw);
1267 return true;
1268 });
1269 def(AST_SymbolCatch, function() {
1270 this.definition().fixed = false;
1271 });
1272 def(AST_SymbolImport, function() {
1273 this.definition().fixed = false;
1274 });
1275 def(AST_SymbolRef, function(tw, descend, compressor) {
1276 var d = this.definition();
1277 push_ref(d, this);
1278 if (d.references.length == 1 && !d.fixed && d.orig[0] instanceof AST_SymbolDefun) {
1279 tw.loop_ids[d.id] = tw.in_loop;
1280 }
1281 var recursive = recursive_ref(tw, d);
1282 if (recursive) recursive.enclosed.forEach(function(def) {
1283 if (d === def) return;
1284 if (def.scope.resolve() === recursive) return;
1285 var assigns = def.fixed && def.fixed.assigns;
1286 if (!assigns) return;
1287 if (assigns[assigns.length - 1] instanceof AST_VarDef) return;
1288 var safe = tw.safe_ids[def.id];
1289 if (!safe) return;
1290 safe.assign = true;
1291 });
1292 if (d.fixed === false || d.fixed === 0) {
1293 var redef = d.redefined();
1294 if (redef && cross_scope(d.scope, this.scope)) redef.single_use = false;
1295 } else if (d.fixed === undefined || !safe_to_read(tw, d)) {
1296 d.fixed = false;
1297 } else if (d.fixed) {
1298 if (this.in_arg && d.orig[0] instanceof AST_SymbolLambda) this.fixed = d.scope;
1299 var value = this.fixed_value();
1300 if (recursive) {
1301 d.recursive_refs++;
1302 } else if (value && ref_once(compressor, d)) {
1303 d.in_loop = tw.loop_ids[d.id] !== tw.in_loop;
1304 d.single_use = is_lambda(value)
1305 && !value.pinned()
1306 && (!d.in_loop || tw.parent() instanceof AST_Call)
1307 || !d.in_loop
1308 && d.scope === this.scope.resolve()
1309 && value.is_constant_expression();
1310 } else {
1311 d.single_use = false;
1312 }
1313 if (is_modified(compressor, tw, this, value, 0, is_immutable(value), recursive)) {
1314 if (d.single_use) {
1315 d.single_use = "m";
1316 } else {
1317 d.fixed = 0;
1318 }
1319 }
1320 if (d.fixed && tw.loop_ids[d.id] !== tw.in_loop) d.cross_loop = true;
1321 mark_escaped(tw, d, this.scope, this, value, 0, 1);
1322 }
1323 if (!this.fixed) this.fixed = d.fixed;
1324 var parent;
1325 if (value instanceof AST_Lambda
1326 && !((parent = tw.parent()) instanceof AST_Call && parent.expression === this)) {
1327 mark_fn_def(tw, d, value);
1328 }
1329 });
1330 def(AST_Template, function(tw, descend) {
1331 var node = this;
1332 var tag = node.tag;
1333 if (!tag) return;
1334 if (tag instanceof AST_LambdaExpression) {
1335 node.expressions.forEach(function(exp) {
1336 exp.walk(tw);
1337 });
1338 tag.walk(tw);
1339 return true;
1340 }
1341 tag.walk(tw);
1342 node.expressions.forEach(function(exp) {
1343 exp.walk(tw);
1344 });
1345 var fixed = tag instanceof AST_SymbolRef && tag.fixed_value();
1346 if (fixed instanceof AST_Lambda) {
1347 mark_fn_def(tw, tag.definition(), fixed);
1348 } else {
1349 tw.find_parent(AST_Scope).may_call_this();
1350 }
1351 return true;
1352 });
1353 def(AST_Toplevel, function(tw, descend, compressor) {
1354 var node = this;
1355 node.globals.each(function(def) {
1356 reset_def(tw, compressor, def);
1357 });
1358 push(tw);
1359 reset_variables(tw, compressor, node);
1360 descend();
1361 pop_scope(tw, node);
1362 return true;
1363 });
1364 def(AST_Try, function(tw, descend, compressor) {
1365 var node = this;
1366 reset_block_variables(tw, compressor, node);
1367 push(tw);
1368 walk_body(node, tw);
1369 pop(tw);
1370 if (node.bcatch) {
1371 push(tw);
1372 node.bcatch.walk(tw);
1373 pop(tw);
1374 }
1375 if (node.bfinally) node.bfinally.walk(tw);
1376 return true;
1377 });
1378 def(AST_Unary, function(tw, descend) {
1379 var node = this;
1380 if (!UNARY_POSTFIX[node.operator]) return;
1381 var exp = node.expression;
1382 if (!(exp instanceof AST_SymbolRef)) {
1383 mark_assignment_to_arguments(exp);
1384 return;
1385 }
1386 var d = exp.definition();
1387 d.assignments++;
1388 var fixed = d.fixed;
1389 if (safe_to_read(tw, d) && !exp.in_arg && safe_to_assign(tw, d)) {
1390 push_ref(d, exp);
1391 mark(tw, d);
1392 if (d.single_use) d.single_use = false;
1393 d.fixed = function() {
1394 return make_node(AST_Binary, node, {
1395 operator: node.operator.slice(0, -1),
1396 left: make_node(AST_UnaryPrefix, node, {
1397 operator: "+",
1398 expression: make_ref(exp, fixed)
1399 }),
1400 right: make_node(AST_Number, node, {
1401 value: 1
1402 })
1403 });
1404 };
1405 d.fixed.assigns = fixed && fixed.assigns ? fixed.assigns.slice() : [];
1406 d.fixed.assigns.push(node);
1407 if (node instanceof AST_UnaryPrefix) {
1408 exp.fixed = d.fixed;
1409 } else {
1410 exp.fixed = function() {
1411 return make_node(AST_UnaryPrefix, node, {
1412 operator: "+",
1413 expression: make_ref(exp, fixed)
1414 });
1415 };
1416 exp.fixed.assigns = fixed && fixed.assigns;
1417 }
1418 } else {
1419 exp.walk(tw);
1420 d.fixed = false;
1421 }
1422 return true;
1423 });
1424 def(AST_VarDef, function(tw, descend, compressor) {
1425 var node = this;
1426 var value = node.value;
1427 if (value instanceof AST_LambdaExpression && node.name instanceof AST_SymbolDeclaration) {
1428 walk_defn();
1429 value.parent_scope.resolve().fn_defs.push(value);
1430 value.safe_ids = null;
1431 var ld = node.name.definition();
1432 if (!ld.fixed) mark_fn_def(tw, ld, value);
1433 } else if (value) {
1434 value.walk(tw);
1435 walk_defn();
1436 } else if (tw.parent() instanceof AST_Let) {
1437 walk_defn();
1438 }
1439 return true;
1440
1441 function walk_defn() {
1442 scan_declaration(tw, compressor, node.name, function() {
1443 return node.value || make_node(AST_Undefined, node);
1444 }, function(name, fixed) {
1445 var d = name.definition();
1446 if (fixed && safe_to_assign(tw, d, true)) {
1447 mark(tw, d);
1448 tw.loop_ids[d.id] = tw.in_loop;
1449 d.fixed = fixed;
1450 d.fixed.assigns = [ node ];
1451 if (name instanceof AST_SymbolConst && d.redefined()
1452 || !(can_drop_symbol(name) || is_safe_lexical(d))) {
1453 d.single_use = false;
1454 }
1455 } else {
1456 d.fixed = false;
1457 }
1458 });
1459 }
1460 });
1461 def(AST_While, function(tw, descend) {
1462 var save_loop = tw.in_loop;
1463 tw.in_loop = this;
1464 push(tw);
1465 descend();
1466 pop(tw);
1467 tw.in_loop = save_loop;
1468 return true;
1469 });
1470 })(function(node, func) {
1471 node.DEFMETHOD("reduce_vars", func);
1472 });
1473
1474 function reset_flags(node) {
1475 node._squeezed = false;
1476 node._optimized = false;
1477 if (node instanceof AST_BlockScope) node._var_names = undefined;
1478 if (node instanceof AST_SymbolRef) node.fixed = undefined;
1479 }
1480
1481 AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) {
1482 var tw = new TreeWalker(compressor.option("reduce_vars") ? function(node, descend) {
1483 reset_flags(node);
1484 return node.reduce_vars(tw, descend, compressor);
1485 } : reset_flags);
1486 // Flow control for visiting lambda definitions
1487 tw.fn_scanning = null;
1488 tw.fn_visited = [];
1489 // Record the loop body in which `AST_SymbolDeclaration` is first encountered
1490 tw.in_loop = null;
1491 tw.loop_ids = Object.create(null);
1492 // Stack of look-up tables to keep track of whether a `SymbolDef` has been
1493 // properly assigned before use:
1494 // - `push()` & `pop()` when visiting conditional branches
1495 // - backup & restore via `save_ids` when visiting out-of-order sections
1496 tw.safe_ids = Object.create(null);
1497 this.walk(tw);
1498 });
1499
1500 AST_Symbol.DEFMETHOD("fixed_value", function() {
1501 var fixed = this.definition().fixed;
1502 if (fixed) {
1503 if (this.fixed) fixed = this.fixed;
1504 return fixed instanceof AST_Node ? fixed : fixed();
1505 }
1506 fixed = fixed === 0 && this.fixed;
1507 if (!fixed) return fixed;
1508 var value = fixed instanceof AST_Node ? fixed : fixed();
1509 return value.is_constant() && value;
1510 });
1511
1512 AST_SymbolRef.DEFMETHOD("is_immutable", function() {
1513 var def = this.redef || this.definition();
1514 return (this.in_arg || def.orig.length == 1) && def.orig[0] instanceof AST_SymbolLambda;
1515 });
1516
1517 AST_Node.DEFMETHOD("convert_symbol", noop);
1518 function convert_destructured(type, process) {
1519 return this.transform(new TreeTransformer(function(node, descend) {
1520 if (node instanceof AST_DefaultValue) {
1521 node = node.clone();
1522 node.name = node.name.transform(this);
1523 return node;
1524 }
1525 if (node instanceof AST_Destructured) {
1526 node = node.clone();
1527 descend(node, this);
1528 return node;
1529 }
1530 if (node instanceof AST_DestructuredKeyVal) {
1531 node = node.clone();
1532 node.value = node.value.transform(this);
1533 return node;
1534 }
1535 return node.convert_symbol(type, process);
1536 }));
1537 }
1538 AST_DefaultValue.DEFMETHOD("convert_symbol", convert_destructured);
1539 AST_Destructured.DEFMETHOD("convert_symbol", convert_destructured);
1540 function convert_symbol(type, process) {
1541 var node = make_node(type, this, this);
1542 process(node, this);
1543 return node;
1544 }
1545 AST_SymbolDeclaration.DEFMETHOD("convert_symbol", convert_symbol);
1546 AST_SymbolRef.DEFMETHOD("convert_symbol", convert_symbol);
1547
1548 function mark_destructured(process, tw) {
1549 var marker = new TreeWalker(function(node) {
1550 if (node instanceof AST_DefaultValue) {
1551 node.value.walk(tw);
1552 node.name.walk(marker);
1553 return true;
1554 }
1555 if (node instanceof AST_DestructuredKeyVal) {
1556 if (node.key instanceof AST_Node) node.key.walk(tw);
1557 node.value.walk(marker);
1558 return true;
1559 }
1560 return process(node);
1561 });
1562 this.walk(marker);
1563 }
1564 AST_DefaultValue.DEFMETHOD("mark_symbol", mark_destructured);
1565 AST_Destructured.DEFMETHOD("mark_symbol", mark_destructured);
1566 function mark_symbol(process) {
1567 return process(this);
1568 }
1569 AST_SymbolDeclaration.DEFMETHOD("mark_symbol", mark_symbol);
1570 AST_SymbolRef.DEFMETHOD("mark_symbol", mark_symbol);
1571
1572 AST_Node.DEFMETHOD("match_symbol", function(predicate) {
1573 return predicate(this);
1574 });
1575 function match_destructured(predicate, ignore_side_effects) {
1576 var found = false;
1577 var tw = new TreeWalker(function(node) {
1578 if (found) return true;
1579 if (node instanceof AST_DefaultValue) {
1580 if (!ignore_side_effects) return found = true;
1581 node.name.walk(tw);
1582 return true;
1583 }
1584 if (node instanceof AST_DestructuredKeyVal) {
1585 if (!ignore_side_effects && node.key instanceof AST_Node) return found = true;
1586 node.value.walk(tw);
1587 return true;
1588 }
1589 if (predicate(node)) return found = true;
1590 });
1591 this.walk(tw);
1592 return found;
1593 }
1594 AST_DefaultValue.DEFMETHOD("match_symbol", match_destructured);
1595 AST_Destructured.DEFMETHOD("match_symbol", match_destructured);
1596
1597 function in_async_generator(scope) {
1598 return scope instanceof AST_AsyncGeneratorDefun || scope instanceof AST_AsyncGeneratorFunction;
1599 }
1600
1601 function find_scope(compressor) {
1602 var level = 0, node;
1603 while (node = compressor.parent(level++)) {
1604 if (node.variables) return node;
1605 }
1606 }
1607
1608 var identifier_atom = makePredicate("Infinity NaN undefined");
1609 function is_lhs_read_only(lhs, compressor) {
1610 if (lhs instanceof AST_ObjectIdentity) return true;
1611 if (lhs instanceof AST_PropAccess) {
1612 if (lhs.property === "__proto__") return true;
1613 lhs = lhs.expression;
1614 if (lhs instanceof AST_SymbolRef) {
1615 if (lhs.is_immutable()) return false;
1616 lhs = lhs.fixed_value();
1617 }
1618 if (!lhs) return true;
1619 if (lhs.tail_node().is_constant()) return true;
1620 return is_lhs_read_only(lhs, compressor);
1621 }
1622 if (lhs instanceof AST_SymbolRef) {
1623 if (lhs.is_immutable()) return true;
1624 var def = lhs.definition();
1625 return compressor.exposed(def) && identifier_atom[def.name];
1626 }
1627 return false;
1628 }
1629
1630 function make_node(ctor, orig, props) {
1631 if (!props) props = {};
1632 if (orig) {
1633 if (!props.start) props.start = orig.start;
1634 if (!props.end) props.end = orig.end;
1635 }
1636 return new ctor(props);
1637 }
1638
1639 function make_sequence(orig, expressions) {
1640 if (expressions.length == 1) return expressions[0];
1641 return make_node(AST_Sequence, orig, {
1642 expressions: expressions.reduce(merge_sequence, [])
1643 });
1644 }
1645
1646 function make_node_from_constant(val, orig) {
1647 switch (typeof val) {
1648 case "string":
1649 return make_node(AST_String, orig, {
1650 value: val
1651 });
1652 case "number":
1653 if (isNaN(val)) return make_node(AST_NaN, orig);
1654 if (isFinite(val)) {
1655 return 1 / val < 0 ? make_node(AST_UnaryPrefix, orig, {
1656 operator: "-",
1657 expression: make_node(AST_Number, orig, { value: -val })
1658 }) : make_node(AST_Number, orig, { value: val });
1659 }
1660 return val < 0 ? make_node(AST_UnaryPrefix, orig, {
1661 operator: "-",
1662 expression: make_node(AST_Infinity, orig)
1663 }) : make_node(AST_Infinity, orig);
1664 case "boolean":
1665 return make_node(val ? AST_True : AST_False, orig);
1666 case "undefined":
1667 return make_node(AST_Undefined, orig);
1668 default:
1669 if (val === null) {
1670 return make_node(AST_Null, orig, { value: null });
1671 }
1672 if (val instanceof RegExp) {
1673 return make_node(AST_RegExp, orig, { value: val });
1674 }
1675 throw new Error(string_template("Can't handle constant of type: {type}", {
1676 type: typeof val
1677 }));
1678 }
1679 }
1680
1681 function needs_unbinding(compressor, val) {
1682 return val instanceof AST_PropAccess
1683 || is_undeclared_ref(val) && val.name == "eval";
1684 }
1685
1686 // we shouldn't compress (1,func)(something) to
1687 // func(something) because that changes the meaning of
1688 // the func (becomes lexical instead of global).
1689 function maintain_this_binding(compressor, parent, orig, val) {
1690 var wrap = false;
1691 if (parent.TYPE == "Call") {
1692 wrap = parent.expression === orig && needs_unbinding(compressor, val);
1693 } else if (parent instanceof AST_Template) {
1694 wrap = parent.tag === orig && needs_unbinding(compressor, val);
1695 } else if (parent instanceof AST_UnaryPrefix) {
1696 wrap = parent.operator == "delete"
1697 || parent.operator == "typeof" && is_undeclared_ref(val);
1698 }
1699 return wrap ? make_sequence(orig, [ make_node(AST_Number, orig, { value: 0 }), val ]) : val;
1700 }
1701
1702 function merge_sequence(array, node) {
1703 if (node instanceof AST_Sequence) {
1704 array.push.apply(array, node.expressions);
1705 } else {
1706 array.push(node);
1707 }
1708 return array;
1709 }
1710
1711 function is_lexical_definition(stat) {
1712 return stat instanceof AST_Const || stat instanceof AST_DefClass || stat instanceof AST_Let;
1713 }
1714
1715 function safe_to_trim(stat) {
1716 if (stat instanceof AST_LambdaDefinition) {
1717 var def = stat.name.definition();
1718 var scope = stat.name.scope;
1719 return def.scope === scope || all(def.references, function(ref) {
1720 var s = ref.scope;
1721 do {
1722 if (s === scope) return true;
1723 } while (s = s.parent_scope);
1724 });
1725 }
1726 return !is_lexical_definition(stat);
1727 }
1728
1729 function as_statement_array(thing) {
1730 if (thing === null) return [];
1731 if (thing instanceof AST_BlockStatement) return all(thing.body, safe_to_trim) ? thing.body : [ thing ];
1732 if (thing instanceof AST_EmptyStatement) return [];
1733 if (is_statement(thing)) return [ thing ];
1734 throw new Error("Can't convert thing to statement array");
1735 }
1736
1737 function is_empty(thing) {
1738 if (thing === null) return true;
1739 if (thing instanceof AST_EmptyStatement) return true;
1740 if (thing instanceof AST_BlockStatement) return thing.body.length == 0;
1741 return false;
1742 }
1743
1744 function has_declarations_only(block) {
1745 return all(block.body, function(stat) {
1746 return is_empty(stat)
1747 || stat instanceof AST_Defun
1748 || stat instanceof AST_Var && declarations_only(stat);
1749 });
1750 }
1751
1752 function loop_body(x) {
1753 if (x instanceof AST_IterationStatement) {
1754 return x.body instanceof AST_BlockStatement ? x.body : x;
1755 }
1756 return x;
1757 }
1758
1759 function is_iife_call(node) {
1760 if (node.TYPE != "Call") return false;
1761 do {
1762 node = node.expression;
1763 } while (node instanceof AST_PropAccess);
1764 return node instanceof AST_LambdaExpression ? !is_arrow(node) : is_iife_call(node);
1765 }
1766
1767 function is_iife_single(call) {
1768 var exp = call.expression;
1769 if (exp.name) return false;
1770 if (!(call instanceof AST_New)) return true;
1771 var found = false;
1772 exp.walk(new TreeWalker(function(node) {
1773 if (found) return true;
1774 if (node instanceof AST_NewTarget) return found = true;
1775 if (node instanceof AST_Scope && node !== exp) return true;
1776 }));
1777 return !found;
1778 }
1779
1780 function is_undeclared_ref(node) {
1781 return node instanceof AST_SymbolRef && node.definition().undeclared;
1782 }
1783
1784 var global_names = makePredicate("Array Boolean clearInterval clearTimeout console Date decodeURI decodeURIComponent encodeURI encodeURIComponent Error escape eval EvalError Function isFinite isNaN JSON Map Math Number parseFloat parseInt RangeError ReferenceError RegExp Object Set setInterval setTimeout String SyntaxError TypeError unescape URIError WeakMap WeakSet");
1785 AST_SymbolRef.DEFMETHOD("is_declared", function(compressor) {
1786 return this.defined
1787 || !this.definition().undeclared
1788 || compressor.option("unsafe") && global_names[this.name];
1789 });
1790
1791 function declarations_only(node) {
1792 return all(node.definitions, function(var_def) {
1793 return !var_def.value;
1794 });
1795 }
1796
1797 function is_declaration(stat, lexical) {
1798 if (stat instanceof AST_DefClass) return lexical && !stat.extends && all(stat.properties, function(prop) {
1799 if (prop.key instanceof AST_Node) return false;
1800 if (prop instanceof AST_ClassField && prop.static && prop.value) return false;
1801 return true;
1802 });
1803 if (stat instanceof AST_Definitions) return (lexical || stat instanceof AST_Var) && declarations_only(stat);
1804 if (stat instanceof AST_ExportDeclaration) return is_declaration(stat.body, lexical);
1805 if (stat instanceof AST_ExportDefault) return is_declaration(stat.body, lexical);
1806 return stat instanceof AST_LambdaDefinition;
1807 }
1808
1809 function is_last_statement(body, stat) {
1810 var index = body.lastIndexOf(stat);
1811 if (index < 0) return false;
1812 while (++index < body.length) {
1813 if (!is_declaration(body[index], true)) return false;
1814 }
1815 return true;
1816 }
1817
1818 function tighten_body(statements, compressor) {
1819 var in_loop, in_try, scope;
1820 find_loop_scope_try();
1821 var CHANGED, max_iter = 10;
1822 do {
1823 CHANGED = false;
1824 eliminate_spurious_blocks(statements);
1825 if (compressor.option("dead_code")) {
1826 eliminate_dead_code(statements, compressor);
1827 }
1828 if (compressor.option("if_return")) {
1829 handle_if_return(statements, compressor);
1830 }
1831 if (compressor.sequences_limit > 0) {
1832 sequencesize(statements, compressor);
1833 sequencesize_2(statements, compressor);
1834 }
1835 if (compressor.option("join_vars")) {
1836 join_consecutive_vars(statements);
1837 }
1838 if (compressor.option("collapse_vars")) {
1839 collapse(statements, compressor);
1840 }
1841 } while (CHANGED && max_iter-- > 0);
1842 return statements;
1843
1844 function find_loop_scope_try() {
1845 var node = compressor.self(), level = 0;
1846 do {
1847 if (node instanceof AST_Catch) {
1848 if (compressor.parent(level).bfinally) {
1849 if (!in_try) in_try = {};
1850 in_try.bfinally = true;
1851 }
1852 level++;
1853 } else if (node instanceof AST_Finally) {
1854 level++;
1855 } else if (node instanceof AST_IterationStatement) {
1856 in_loop = true;
1857 } else if (node instanceof AST_Scope) {
1858 scope = node;
1859 break;
1860 } else if (node instanceof AST_Try) {
1861 if (!in_try) in_try = {};
1862 if (node.bcatch) in_try.bcatch = true;
1863 if (node.bfinally) in_try.bfinally = true;
1864 }
1865 } while (node = compressor.parent(level++));
1866 }
1867
1868 // Search from right to left for assignment-like expressions:
1869 // - `var a = x;`
1870 // - `a = x;`
1871 // - `++a`
1872 // For each candidate, scan from left to right for first usage, then try
1873 // to fold assignment into the site for compression.
1874 // Will not attempt to collapse assignments into or past code blocks
1875 // which are not sequentially executed, e.g. loops and conditionals.
1876 function collapse(statements, compressor) {
1877 if (scope.pinned()) return statements;
1878 var args;
1879 var assignments = new Dictionary();
1880 var candidates = [];
1881 var declare_only = new Dictionary();
1882 var force_single;
1883 var stat_index = statements.length;
1884 var scanner = new TreeTransformer(function(node, descend) {
1885 if (abort) return node;
1886 // Skip nodes before `candidate` as quickly as possible
1887 if (!hit) {
1888 if (node !== hit_stack[hit_index]) return node;
1889 hit_index++;
1890 if (hit_index < hit_stack.length) return handle_custom_scan_order(node, scanner);
1891 hit = true;
1892 stop_after = (value_def ? find_stop_value : find_stop)(node, 0);
1893 if (stop_after === node) abort = true;
1894 return node;
1895 }
1896 // Stop immediately if these node types are encountered
1897 var parent = scanner.parent();
1898 if (should_stop(node, parent)) {
1899 abort = true;
1900 return node;
1901 }
1902 // Stop only if candidate is found within conditional branches
1903 if (!stop_if_hit && in_conditional(node, parent)) {
1904 stop_if_hit = parent;
1905 }
1906 // Skip transient nodes caused by single-use variable replacement
1907 if (node.single_use && parent instanceof AST_VarDef && parent.value === node) return node;
1908 // Replace variable with assignment when found
1909 var hit_rhs;
1910 if (!(node instanceof AST_SymbolDeclaration)
1911 && (scan_lhs && lhs.equivalent_to(node)
1912 || scan_rhs && (hit_rhs = scan_rhs(node, this)))) {
1913 if (!can_replace || stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) {
1914 if (!hit_rhs && !value_def) abort = true;
1915 return node;
1916 }
1917 if (is_lhs(node, parent)) {
1918 if (value_def && !hit_rhs) assign_used = true;
1919 return node;
1920 } else if (value_def) {
1921 if (stop_if_hit && assign_pos == 0) assign_pos = remaining - replaced;
1922 if (!hit_rhs) replaced++;
1923 return node;
1924 } else {
1925 replaced++;
1926 }
1927 CHANGED = abort = true;
1928 AST_Node.info("Collapsing {node} [{file}:{line},{col}]", {
1929 node: node,
1930 file: node.start.file,
1931 line: node.start.line,
1932 col: node.start.col,
1933 });
1934 if (candidate.TYPE == "Binary") return make_node(AST_Assign, candidate, {
1935 operator: "=",
1936 left: candidate.right.left,
1937 right: make_node(AST_Conditional, candidate, {
1938 condition: candidate.operator == "&&" ? candidate.left : candidate.left.negate(compressor),
1939 consequent: candidate.right.right,
1940 alternative: node,
1941 }),
1942 });
1943 if (candidate instanceof AST_UnaryPostfix) {
1944 if (lhs instanceof AST_SymbolRef) lhs.definition().fixed = false;
1945 return make_node(AST_UnaryPrefix, candidate, candidate);
1946 }
1947 if (candidate instanceof AST_VarDef) {
1948 var def = candidate.name.definition();
1949 if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
1950 def.replaced++;
1951 return maintain_this_binding(compressor, parent, node, candidate.value);
1952 }
1953 return make_node(AST_Assign, candidate, {
1954 operator: "=",
1955 left: make_node(AST_SymbolRef, candidate.name, candidate.name),
1956 right: candidate.value,
1957 });
1958 }
1959 var assign = candidate;
1960 while (assign.write_only) {
1961 assign.write_only = false;
1962 if (!(assign instanceof AST_Assign)) break;
1963 assign = assign.right;
1964 }
1965 return candidate;
1966 }
1967 // These node types have child nodes that execute sequentially,
1968 // but are otherwise not safe to scan into or beyond them.
1969 if (is_last_node(node, parent) || may_throw(node)) {
1970 stop_after = node;
1971 if (node instanceof AST_Scope) abort = true;
1972 }
1973 // Scan but don't replace inside getter/setter
1974 if (node instanceof AST_Accessor) {
1975 var replace = can_replace;
1976 can_replace = false;
1977 descend(node, scanner);
1978 can_replace = replace;
1979 return signal_abort(node);
1980 }
1981 // Scan but don't replace inside destructuring expression
1982 if (node instanceof AST_Destructured) {
1983 var replace = can_replace;
1984 can_replace = false;
1985 descend(node, scanner);
1986 can_replace = replace;
1987 return signal_abort(node);
1988 }
1989 // Scan but don't replace inside default value
1990 if (node instanceof AST_DefaultValue) {
1991 node.name = node.name.transform(scanner);
1992 var replace = can_replace;
1993 can_replace = false;
1994 node.value = node.value.transform(scanner);
1995 can_replace = replace;
1996 return signal_abort(node);
1997 }
1998 // Scan but don't replace inside block scope with colliding variable
1999 if (node instanceof AST_BlockScope
2000 && !(node instanceof AST_Scope)
2001 && !(node.variables && node.variables.all(function(def) {
2002 return !lvalues.has(def.name);
2003 }))) {
2004 var replace = can_replace;
2005 can_replace = false;
2006 if (!handle_custom_scan_order(node, scanner)) descend(node, scanner);
2007 can_replace = replace;
2008 return signal_abort(node);
2009 }
2010 return handle_custom_scan_order(node, scanner);
2011 }, signal_abort);
2012 var multi_replacer = new TreeTransformer(function(node) {
2013 if (abort) return node;
2014 // Skip nodes before `candidate` as quickly as possible
2015 if (!hit) {
2016 if (node !== hit_stack[hit_index]) return node;
2017 hit_index++;
2018 switch (hit_stack.length - hit_index) {
2019 case 0:
2020 hit = true;
2021 if (assign_used) return node;
2022 if (node !== candidate) return node;
2023 if (node instanceof AST_VarDef) return node;
2024 def.replaced++;
2025 var parent = multi_replacer.parent();
2026 if (parent instanceof AST_Sequence && parent.tail_node() !== node) {
2027 value_def.replaced++;
2028 return List.skip;
2029 }
2030 return rvalue;
2031 case 1:
2032 if (!assign_used && node.body === candidate) {
2033 hit = true;
2034 def.replaced++;
2035 value_def.replaced++;
2036 return null;
2037 }
2038 default:
2039 return handle_custom_scan_order(node, multi_replacer);
2040 }
2041 }
2042 // Replace variable when found
2043 if (node instanceof AST_SymbolRef && node.definition() === def) {
2044 if (is_lhs(node, multi_replacer.parent())) return node;
2045 if (!--replaced) abort = true;
2046 var ref = rvalue.clone();
2047 ref.scope = node.scope;
2048 ref.reference();
2049 if (replaced == assign_pos) {
2050 abort = true;
2051 return make_node(AST_Assign, candidate, {
2052 operator: "=",
2053 left: node,
2054 right: ref,
2055 });
2056 }
2057 def.replaced++;
2058 return ref;
2059 }
2060 // Skip (non-executed) functions and (leading) default case in switch statements
2061 if (node instanceof AST_Default || node instanceof AST_Scope) return node;
2062 }, patch_sequence);
2063 while (--stat_index >= 0) {
2064 // Treat parameters as collapsible in IIFE, i.e.
2065 // function(a, b){ ... }(x());
2066 // would be translated into equivalent assignments:
2067 // var a = x(), b = undefined;
2068 if (stat_index == 0 && compressor.option("unused")) extract_args();
2069 // Find collapsible assignments
2070 var hit_stack = [];
2071 extract_candidates(statements[stat_index]);
2072 while (candidates.length > 0) {
2073 hit_stack = candidates.pop();
2074 var hit_index = 0;
2075 var candidate = hit_stack[hit_stack.length - 1];
2076 var assign_pos = -1;
2077 var assign_used = false;
2078 var remaining;
2079 var value_def = null;
2080 var stop_after = null;
2081 var stop_if_hit = null;
2082 var lhs = get_lhs(candidate);
2083 var side_effects = lhs && lhs.has_side_effects(compressor);
2084 var scan_lhs = lhs && !side_effects && !is_lhs_read_only(lhs, compressor);
2085 var scan_rhs = foldable(candidate);
2086 if (!scan_lhs && !scan_rhs) continue;
2087 var funarg = candidate.name instanceof AST_SymbolFunarg;
2088 var may_throw = return_false;
2089 if (candidate.may_throw(compressor)) {
2090 if (funarg && is_async(scope)) continue;
2091 may_throw = in_try ? function(node) {
2092 return node.has_side_effects(compressor);
2093 } : side_effects_external;
2094 }
2095 var read_toplevel = false;
2096 var modify_toplevel = false;
2097 // Locate symbols which may execute code outside of scanning range
2098 var well_defined = true;
2099 var lvalues = get_lvalues(candidate);
2100 var lhs_local = is_lhs_local(lhs);
2101 var rvalue = get_rvalue(candidate);
2102 if (!side_effects) side_effects = value_has_side_effects();
2103 var check_destructured = in_try || !lhs_local ? function(node) {
2104 return node instanceof AST_Destructured;
2105 } : return_false;
2106 var replace_all = replace_all_symbols(candidate);
2107 var hit = funarg;
2108 var abort = false;
2109 var replaced = 0;
2110 var can_replace = !args || !hit;
2111 if (!can_replace) {
2112 for (var j = candidate.arg_index + 1; !abort && j < args.length; j++) {
2113 if (args[j]) args[j].transform(scanner);
2114 }
2115 can_replace = true;
2116 }
2117 for (var i = stat_index; !abort && i < statements.length; i++) {
2118 statements[i].transform(scanner);
2119 }
2120 if (value_def) {
2121 if (!replaced || remaining > replaced + assign_used) {
2122 candidates.push(hit_stack);
2123 force_single = true;
2124 continue;
2125 }
2126 if (replaced == assign_pos) assign_used = true;
2127 var def = lhs.definition();
2128 abort = false;
2129 hit_index = 0;
2130 hit = funarg;
2131 for (var i = stat_index; !abort && i < statements.length; i++) {
2132 if (!statements[i].transform(multi_replacer)) statements.splice(i--, 1);
2133 }
2134 replaced = candidate instanceof AST_VarDef
2135 && candidate === hit_stack[hit_stack.length - 1]
2136 && def.references.length == def.replaced
2137 && !compressor.exposed(def);
2138 value_def.last_ref = false;
2139 value_def.single_use = false;
2140 CHANGED = true;
2141 }
2142 if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
2143 }
2144 }
2145
2146 function signal_abort(node) {
2147 if (abort) return node;
2148 if (stop_after === node) abort = true;
2149 if (stop_if_hit === node) stop_if_hit = null;
2150 return node;
2151 }
2152
2153 function handle_custom_scan_order(node, tt) {
2154 if (!(node instanceof AST_BlockScope)) {
2155 if (!(node instanceof AST_ClassProperty && !node.static)) return;
2156 // Skip non-static class property values
2157 if (node.key instanceof AST_Node) node.key = node.key.transform(tt);
2158 return node;
2159 }
2160 // Skip (non-executed) functions
2161 if (node instanceof AST_Scope) return node;
2162 // Scan object only in a for-in/of statement
2163 if (node instanceof AST_ForEnumeration) {
2164 node.object = node.object.transform(tt);
2165 abort = true;
2166 return node;
2167 }
2168 // Scan first case expression only in a switch statement
2169 if (node instanceof AST_Switch) {
2170 node.expression = node.expression.transform(tt);
2171 for (var i = 0; !abort && i < node.body.length; i++) {
2172 var branch = node.body[i];
2173 if (branch instanceof AST_Case) {
2174 if (!hit) {
2175 if (branch !== hit_stack[hit_index]) continue;
2176 hit_index++;
2177 }
2178 branch.expression = branch.expression.transform(tt);
2179 if (!replace_all) break;
2180 scan_rhs = false;
2181 }
2182 }
2183 abort = true;
2184 return node;
2185 }
2186 }
2187
2188 function is_direct_assignment(node, parent) {
2189 if (parent instanceof AST_Assign) return parent.operator == "=" && parent.left === node;
2190 if (parent instanceof AST_DefaultValue) return parent.name === node;
2191 if (parent instanceof AST_DestructuredArray) return true;
2192 if (parent instanceof AST_DestructuredKeyVal) return parent.value === node;
2193 }
2194
2195 function should_stop(node, parent) {
2196 if (node === rvalue) return true;
2197 if (parent instanceof AST_For) {
2198 if (node !== parent.init) return true;
2199 }
2200 if (node instanceof AST_Assign) {
2201 return node.operator != "=" && lhs.equivalent_to(node.left);
2202 }
2203 if (node instanceof AST_Call) {
2204 if (!(lhs instanceof AST_PropAccess)) return false;
2205 if (!lhs.equivalent_to(node.expression)) return false;
2206 return !(rvalue instanceof AST_LambdaExpression && !rvalue.contains_this());
2207 }
2208 if (node instanceof AST_Class) return !compressor.has_directive("use strict");
2209 if (node instanceof AST_Debugger) return true;
2210 if (node instanceof AST_Defun) return funarg && lhs.name === node.name.name;
2211 if (node instanceof AST_DestructuredKeyVal) return node.key instanceof AST_Node;
2212 if (node instanceof AST_DWLoop) return true;
2213 if (node instanceof AST_LoopControl) return true;
2214 if (node instanceof AST_SymbolRef) {
2215 if (node.is_declared(compressor)) {
2216 if (node.fixed_value()) return false;
2217 if (can_drop_symbol(node)) {
2218 return !(parent instanceof AST_PropAccess && parent.expression === node)
2219 && is_arguments(node.definition());
2220 }
2221 } else if (is_direct_assignment(node, parent)) {
2222 return false;
2223 }
2224 if (!replace_all) return true;
2225 scan_rhs = false;
2226 return false;
2227 }
2228 if (node instanceof AST_Try) return true;
2229 if (node instanceof AST_With) return true;
2230 return false;
2231 }
2232
2233 function in_conditional(node, parent) {
2234 if (parent instanceof AST_Assign) return parent.left !== node && lazy_op[parent.operator.slice(0, -1)];
2235 if (parent instanceof AST_Binary) return parent.left !== node && lazy_op[parent.operator];
2236 if (parent instanceof AST_Call) return parent.optional && parent.expression !== node;
2237 if (parent instanceof AST_Case) return parent.expression !== node;
2238 if (parent instanceof AST_Conditional) return parent.condition !== node;
2239 if (parent instanceof AST_If) return parent.condition !== node;
2240 if (parent instanceof AST_Sub) return parent.optional && parent.expression !== node;
2241 }
2242
2243 function is_last_node(node, parent) {
2244 if (node instanceof AST_Await) return true;
2245 if (node.TYPE == "Binary") return node.operator == "in" && !is_object(node.right);
2246 if (node instanceof AST_Call) {
2247 var def, fn = node.expression;
2248 if (fn instanceof AST_SymbolRef) {
2249 def = fn.definition();
2250 fn = fn.fixed_value();
2251 }
2252 if (!(fn instanceof AST_Lambda)) return !node.is_expr_pure(compressor);
2253 if (def && recursive_ref(compressor, def, fn)) return true;
2254 if (fn.collapse_scanning) return false;
2255 fn.collapse_scanning = true;
2256 var replace = can_replace;
2257 can_replace = false;
2258 var after = stop_after;
2259 var if_hit = stop_if_hit;
2260 if (!all(fn.argnames, function(argname) {
2261 if (argname instanceof AST_DefaultValue) {
2262 argname.value.transform(scanner);
2263 if (abort) return false;
2264 argname = argname.name;
2265 }
2266 return !(argname instanceof AST_Destructured);
2267 })) {
2268 abort = true;
2269 } else if (is_arrow(fn) && fn.value) {
2270 fn.value.transform(scanner);
2271 } else for (var i = 0; !abort && i < fn.body.length; i++) {
2272 var stat = fn.body[i];
2273 if (stat instanceof AST_Return) {
2274 if (stat.value) stat.value.transform(scanner);
2275 break;
2276 }
2277 stat.transform(scanner);
2278 }
2279 stop_if_hit = if_hit;
2280 stop_after = after;
2281 can_replace = replace;
2282 fn.collapse_scanning = false;
2283 if (!abort) return false;
2284 abort = false;
2285 return true;
2286 }
2287 if (node instanceof AST_Exit) {
2288 if (in_try) {
2289 if (in_try.bfinally) return true;
2290 if (in_try.bcatch && node instanceof AST_Throw) return true;
2291 }
2292 return side_effects || lhs instanceof AST_PropAccess || may_modify(lhs);
2293 }
2294 if (node instanceof AST_Function) {
2295 return compressor.option("ie") && node.name && lvalues.has(node.name.name);
2296 }
2297 if (node instanceof AST_ObjectIdentity) return symbol_in_lvalues(node, parent);
2298 if (node instanceof AST_PropAccess) {
2299 if (side_effects) return true;
2300 var exp = node.expression;
2301 if (exp instanceof AST_SymbolRef && is_arguments(exp.definition())) return true;
2302 if (compressor.option("unsafe")) {
2303 if (is_undeclared_ref(exp) && global_names[exp.name]) return false;
2304 if (is_static_fn(exp)) return false;
2305 }
2306 if (!well_defined) return true;
2307 if (value_def) return false;
2308 if (!in_try && lhs_local) return false;
2309 if (node.optional) return false;
2310 return exp.may_throw_on_access(compressor);
2311 }
2312 if (node instanceof AST_Spread) return true;
2313 if (node instanceof AST_SymbolRef) {
2314 if (symbol_in_lvalues(node, parent)) return !is_direct_assignment(node, parent);
2315 if (side_effects && may_modify(node)) return true;
2316 var def = node.definition();
2317 return (in_try || def.scope.resolve() !== scope) && !can_drop_symbol(node);
2318 }
2319 if (node instanceof AST_Template) return !node.is_expr_pure(compressor);
2320 if (node instanceof AST_VarDef) {
2321 if (check_destructured(node.name)) return true;
2322 return (node.value || parent instanceof AST_Let) && node.name.match_symbol(function(node) {
2323 return node instanceof AST_SymbolDeclaration
2324 && (lvalues.has(node.name) || side_effects && may_modify(node));
2325 }, true);
2326 }
2327 if (node instanceof AST_Yield) return true;
2328 var sym = is_lhs(node.left, node);
2329 if (!sym) return false;
2330 if (sym instanceof AST_PropAccess) return true;
2331 if (check_destructured(sym)) return true;
2332 return sym.match_symbol(function(node) {
2333 return node instanceof AST_SymbolRef
2334 && (lvalues.has(node.name) || read_toplevel && compressor.exposed(node.definition()));
2335 }, true);
2336 }
2337
2338 function may_throw_destructured(node, value) {
2339 if (!value) return !(node instanceof AST_Symbol);
2340 if (node instanceof AST_DefaultValue) {
2341 return value.has_side_effects(compressor)
2342 || node.value.has_side_effects(compressor)
2343 || may_throw_destructured(node.name, is_undefined(value) && node.value);
2344 }
2345 if (node instanceof AST_Destructured) {
2346 if (node.rest && may_throw_destructured(node.rest)) return true;
2347 if (node instanceof AST_DestructuredArray) {
2348 if (!(value instanceof AST_Array || value.is_string(compressor))) return true;
2349 return !all(node.elements, function(element) {
2350 return !may_throw_destructured(element);
2351 });
2352 }
2353 if (node instanceof AST_DestructuredObject) {
2354 if (!value.is_defined(compressor)) return true;
2355 return !all(node.properties, function(prop) {
2356 if (prop instanceof AST_Node && prop.has_side_effects(compressor)) return false;
2357 return !may_throw_destructured(prop.value);
2358 });
2359 }
2360 }
2361 }
2362
2363 function extract_args() {
2364 var iife, fn = compressor.self();
2365 if (fn instanceof AST_LambdaExpression
2366 && !is_generator(fn)
2367 && !fn.uses_arguments
2368 && !fn.pinned()
2369 && (iife = compressor.parent()) instanceof AST_Call
2370 && iife.expression === fn
2371 && is_iife_single(iife)
2372 && all(iife.args, function(arg) {
2373 return !(arg instanceof AST_Spread);
2374 })) {
2375 var fn_strict = compressor.has_directive("use strict");
2376 if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
2377 var has_await = is_async(fn) ? function(node) {
2378 return node instanceof AST_Symbol && node.name == "await";
2379 } : function(node) {
2380 return node instanceof AST_Await && !tw.find_parent(AST_Scope);
2381 };
2382 var arg_scope = null;
2383 var tw = new TreeWalker(function(node, descend) {
2384 if (!arg) return true;
2385 if (has_await(node) || node instanceof AST_Yield) {
2386 arg = null;
2387 return true;
2388 }
2389 if (node instanceof AST_ObjectIdentity && (fn_strict || !arg_scope)) {
2390 arg = null;
2391 return true;
2392 }
2393 if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
2394 var s = node.definition().scope;
2395 if (s !== scope) while (s = s.parent_scope) {
2396 if (s === scope) return true;
2397 }
2398 arg = null;
2399 }
2400 if (node instanceof AST_Scope && !is_arrow(node)) {
2401 var save_scope = arg_scope;
2402 arg_scope = node;
2403 descend();
2404 arg_scope = save_scope;
2405 return true;
2406 }
2407 });
2408 args = iife.args.slice();
2409 var len = args.length;
2410 var names = new Dictionary();
2411 for (var i = fn.argnames.length; --i >= 0;) {
2412 var sym = fn.argnames[i];
2413 var arg = args[i];
2414 var value;
2415 if (sym instanceof AST_DefaultValue) {
2416 value = sym.value;
2417 sym = sym.name;
2418 args[len + i] = value;
2419 }
2420 if (sym instanceof AST_Destructured) {
2421 if (!may_throw_destructured(sym, arg)) continue;
2422 candidates.length = 0;
2423 break;
2424 }
2425 if (names.has(sym.name)) continue;
2426 names.set(sym.name, true);
2427 if (value) arg = !arg || is_undefined(arg) ? value : null;
2428 if (!arg && !value) {
2429 arg = make_node(AST_Undefined, sym).transform(compressor);
2430 } else if (arg instanceof AST_Lambda && arg.pinned()) {
2431 arg = null;
2432 } else if (arg) {
2433 arg.walk(tw);
2434 }
2435 if (!arg) continue;
2436 var candidate = make_node(AST_VarDef, sym, {
2437 name: sym,
2438 value: arg,
2439 });
2440 candidate.name_index = i;
2441 candidate.arg_index = value ? len + i : i;
2442 candidates.unshift([ candidate ]);
2443 }
2444 }
2445 }
2446
2447 function extract_candidates(expr, unused) {
2448 hit_stack.push(expr);
2449 if (expr instanceof AST_Array) {
2450 expr.elements.forEach(function(node) {
2451 extract_candidates(node, unused);
2452 });
2453 } else if (expr instanceof AST_Assign) {
2454 var lhs = expr.left;
2455 if (!(lhs instanceof AST_Destructured)) candidates.push(hit_stack.slice());
2456 extract_candidates(lhs);
2457 extract_candidates(expr.right);
2458 if (lhs instanceof AST_SymbolRef && expr.operator == "=") {
2459 assignments.set(lhs.name, (assignments.get(lhs.name) || 0) + 1);
2460 }
2461 } else if (expr instanceof AST_Await) {
2462 extract_candidates(expr.expression, unused);
2463 } else if (expr instanceof AST_Binary) {
2464 var lazy = lazy_op[expr.operator];
2465 if (unused
2466 && lazy
2467 && expr.operator != "??"
2468 && expr.right instanceof AST_Assign
2469 && expr.right.operator == "="
2470 && !(expr.right.left instanceof AST_Destructured)) {
2471 candidates.push(hit_stack.slice());
2472 }
2473 extract_candidates(expr.left, !lazy && unused);
2474 extract_candidates(expr.right, unused);
2475 } else if (expr instanceof AST_Call) {
2476 extract_candidates(expr.expression);
2477 expr.args.forEach(extract_candidates);
2478 } else if (expr instanceof AST_Case) {
2479 extract_candidates(expr.expression);
2480 } else if (expr instanceof AST_Conditional) {
2481 extract_candidates(expr.condition);
2482 extract_candidates(expr.consequent, unused);
2483 extract_candidates(expr.alternative, unused);
2484 } else if (expr instanceof AST_Definitions) {
2485 expr.definitions.forEach(extract_candidates);
2486 } else if (expr instanceof AST_Dot) {
2487 extract_candidates(expr.expression);
2488 } else if (expr instanceof AST_DWLoop) {
2489 extract_candidates(expr.condition);
2490 if (!(expr.body instanceof AST_Block)) {
2491 extract_candidates(expr.body);
2492 }
2493 } else if (expr instanceof AST_Exit) {
2494 if (expr.value) extract_candidates(expr.value);
2495 } else if (expr instanceof AST_For) {
2496 if (expr.init) extract_candidates(expr.init, true);
2497 if (expr.condition) extract_candidates(expr.condition);
2498 if (expr.step) extract_candidates(expr.step, true);
2499 if (!(expr.body instanceof AST_Block)) {
2500 extract_candidates(expr.body);
2501 }
2502 } else if (expr instanceof AST_ForEnumeration) {
2503 extract_candidates(expr.object);
2504 if (!(expr.body instanceof AST_Block)) {
2505 extract_candidates(expr.body);
2506 }
2507 } else if (expr instanceof AST_If) {
2508 extract_candidates(expr.condition);
2509 if (!(expr.body instanceof AST_Block)) {
2510 extract_candidates(expr.body);
2511 }
2512 if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
2513 extract_candidates(expr.alternative);
2514 }
2515 } else if (expr instanceof AST_Object) {
2516 expr.properties.forEach(function(prop) {
2517 hit_stack.push(prop);
2518 if (prop.key instanceof AST_Node) extract_candidates(prop.key);
2519 if (prop instanceof AST_ObjectKeyVal) extract_candidates(prop.value, unused);
2520 hit_stack.pop();
2521 });
2522 } else if (expr instanceof AST_Sequence) {
2523 var end = expr.expressions.length - (unused ? 0 : 1);
2524 expr.expressions.forEach(function(node, index) {
2525 extract_candidates(node, index < end);
2526 });
2527 } else if (expr instanceof AST_SimpleStatement) {
2528 extract_candidates(expr.body, true);
2529 } else if (expr instanceof AST_Spread) {
2530 extract_candidates(expr.expression);
2531 } else if (expr instanceof AST_Sub) {
2532 extract_candidates(expr.expression);
2533 extract_candidates(expr.property);
2534 } else if (expr instanceof AST_Switch) {
2535 extract_candidates(expr.expression);
2536 expr.body.forEach(extract_candidates);
2537 } else if (expr instanceof AST_Unary) {
2538 if (UNARY_POSTFIX[expr.operator]) {
2539 candidates.push(hit_stack.slice());
2540 } else {
2541 extract_candidates(expr.expression);
2542 }
2543 } else if (expr instanceof AST_VarDef) {
2544 if (expr.name instanceof AST_SymbolVar) {
2545 if (expr.value) {
2546 var def = expr.name.definition();
2547 if (def.references.length > def.replaced) {
2548 candidates.push(hit_stack.slice());
2549 }
2550 } else {
2551 declare_only.set(expr.name.name, (declare_only.get(expr.name.name) || 0) + 1);
2552 }
2553 }
2554 if (expr.value) extract_candidates(expr.value);
2555 } else if (expr instanceof AST_Yield) {
2556 if (expr.expression) extract_candidates(expr.expression);
2557 }
2558 hit_stack.pop();
2559 }
2560
2561 function find_stop(node, level) {
2562 var parent = scanner.parent(level);
2563 if (parent instanceof AST_Array) return node;
2564 if (parent instanceof AST_Assign) return node;
2565 if (parent instanceof AST_Await) return node;
2566 if (parent instanceof AST_Binary) return node;
2567 if (parent instanceof AST_Call) return node;
2568 if (parent instanceof AST_Case) return node;
2569 if (parent instanceof AST_Conditional) return node;
2570 if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
2571 if (parent instanceof AST_Exit) return node;
2572 if (parent instanceof AST_If) return node;
2573 if (parent instanceof AST_IterationStatement) return node;
2574 if (parent instanceof AST_ObjectProperty) return node;
2575 if (parent instanceof AST_PropAccess) return node;
2576 if (parent instanceof AST_Sequence) {
2577 return (parent.tail_node() === node ? find_stop : find_stop_unused)(parent, level + 1);
2578 }
2579 if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
2580 if (parent instanceof AST_Spread) return node;
2581 if (parent instanceof AST_Switch) return node;
2582 if (parent instanceof AST_Unary) return node;
2583 if (parent instanceof AST_VarDef) return node;
2584 if (parent instanceof AST_Yield) return node;
2585 return null;
2586 }
2587
2588 function find_stop_logical(parent, op, level) {
2589 var node;
2590 do {
2591 node = parent;
2592 parent = scanner.parent(++level);
2593 } while (parent instanceof AST_Assign && parent.operator.slice(0, -1) == op
2594 || parent instanceof AST_Binary && parent.operator == op);
2595 return node;
2596 }
2597
2598 function find_stop_expr(expr, cont, node, parent, level) {
2599 var replace = can_replace;
2600 can_replace = false;
2601 var after = stop_after;
2602 var if_hit = stop_if_hit;
2603 var stack = scanner.stack;
2604 scanner.stack = [ parent ];
2605 expr.transform(scanner);
2606 scanner.stack = stack;
2607 stop_if_hit = if_hit;
2608 stop_after = after;
2609 can_replace = replace;
2610 if (abort) {
2611 abort = false;
2612 return node;
2613 }
2614 return cont(parent, level + 1);
2615 }
2616
2617 function find_stop_value(node, level) {
2618 var parent = scanner.parent(level);
2619 if (parent instanceof AST_Array) return find_stop_value(parent, level + 1);
2620 if (parent instanceof AST_Assign) {
2621 if (may_throw(parent)) return node;
2622 if (parent.left.match_symbol(function(ref) {
2623 return ref instanceof AST_SymbolRef && (lhs.name == ref.name || value_def.name == ref.name);
2624 })) return node;
2625 var op;
2626 if (parent.left === node || !lazy_op[op = parent.operator.slice(0, -1)]) {
2627 return find_stop_value(parent, level + 1);
2628 }
2629 return find_stop_logical(parent, op, level);
2630 }
2631 if (parent instanceof AST_Binary) {
2632 var op;
2633 if (parent.left === node || !lazy_op[op = parent.operator]) {
2634 return find_stop_value(parent, level + 1);
2635 }
2636 return find_stop_logical(parent, op, level);
2637 }
2638 if (parent instanceof AST_Call) return parent;
2639 if (parent instanceof AST_Case) {
2640 if (parent.expression !== node) return node;
2641 return find_stop_value(parent, level + 1);
2642 }
2643 if (parent instanceof AST_Conditional) {
2644 if (parent.condition !== node) return node;
2645 return find_stop_value(parent, level + 1);
2646 }
2647 if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
2648 if (parent instanceof AST_Do) return node;
2649 if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1);
2650 if (parent instanceof AST_For) {
2651 if (parent.init !== node && parent.condition !== node) return node;
2652 return find_stop_value(parent, level + 1);
2653 }
2654 if (parent instanceof AST_ForEnumeration) {
2655 if (parent.init !== node) return node;
2656 return find_stop_value(parent, level + 1);
2657 }
2658 if (parent instanceof AST_If) {
2659 if (parent.condition !== node) return node;
2660 return find_stop_value(parent, level + 1);
2661 }
2662 if (parent instanceof AST_ObjectProperty) {
2663 var obj = scanner.parent(level + 1);
2664 return all(obj.properties, function(prop) {
2665 return prop instanceof AST_ObjectKeyVal;
2666 }) ? find_stop_value(obj, level + 2) : obj;
2667 }
2668 if (parent instanceof AST_PropAccess) {
2669 var exp = parent.expression;
2670 return exp === node ? find_stop_value(parent, level + 1) : node;
2671 }
2672 if (parent instanceof AST_Sequence) {
2673 return (parent.tail_node() === node ? find_stop_value : find_stop_unused)(parent, level + 1);
2674 }
2675 if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
2676 if (parent instanceof AST_Spread) return find_stop_value(parent, level + 1);
2677 if (parent instanceof AST_Switch) {
2678 if (parent.expression !== node) return node;
2679 return find_stop_value(parent, level + 1);
2680 }
2681 if (parent instanceof AST_Unary) {
2682 if (parent.operator == "delete") return node;
2683 return find_stop_value(parent, level + 1);
2684 }
2685 if (parent instanceof AST_VarDef) return parent.name.match_symbol(function(sym) {
2686 return sym instanceof AST_SymbolDeclaration && (lhs.name == sym.name || value_def.name == sym.name);
2687 }) ? node : find_stop_value(parent, level + 1);
2688 if (parent instanceof AST_While) {
2689 if (parent.condition !== node) return node;
2690 return find_stop_value(parent, level + 1);
2691 }
2692 if (parent instanceof AST_Yield) return find_stop_value(parent, level + 1);
2693 return null;
2694 }
2695
2696 function find_stop_unused(node, level) {
2697 var parent = scanner.parent(level);
2698 if (is_last_node(node, parent)) return node;
2699 if (in_conditional(node, parent)) return node;
2700 if (parent instanceof AST_Array) return find_stop_unused(parent, level + 1);
2701 if (parent instanceof AST_Assign) return check_assignment(parent.left);
2702 if (parent instanceof AST_Await) return node;
2703 if (parent instanceof AST_Binary) return find_stop_unused(parent, level + 1);
2704 if (parent instanceof AST_Call) return find_stop_unused(parent, level + 1);
2705 if (parent instanceof AST_Case) return find_stop_unused(parent, level + 1);
2706 if (parent instanceof AST_Conditional) return find_stop_unused(parent, level + 1);
2707 if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
2708 if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1);
2709 if (parent instanceof AST_If) return find_stop_unused(parent, level + 1);
2710 if (parent instanceof AST_IterationStatement) return node;
2711 if (parent instanceof AST_ObjectProperty) {
2712 var obj = scanner.parent(level + 1);
2713 return all(obj.properties, function(prop) {
2714 return prop instanceof AST_ObjectKeyVal;
2715 }) ? find_stop_unused(obj, level + 2) : obj;
2716 }
2717 if (parent instanceof AST_PropAccess) {
2718 var exp = parent.expression;
2719 if (exp === node) return find_stop_unused(parent, level + 1);
2720 return find_stop_expr(exp, find_stop_unused, node, parent, level);
2721 }
2722 if (parent instanceof AST_Sequence) return find_stop_unused(parent, level + 1);
2723 if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
2724 if (parent instanceof AST_Spread) return node;
2725 if (parent instanceof AST_Switch) return find_stop_unused(parent, level + 1);
2726 if (parent instanceof AST_Unary) return find_stop_unused(parent, level + 1);
2727 if (parent instanceof AST_VarDef) return check_assignment(parent.name);
2728 if (parent instanceof AST_Yield) return node;
2729 return null;
2730
2731 function check_assignment(lhs) {
2732 if (may_throw(parent)) return node;
2733 if (lhs !== node && lhs instanceof AST_Destructured) {
2734 return find_stop_expr(lhs, find_stop_unused, node, parent, level);
2735 }
2736 return find_stop_unused(parent, level + 1);
2737 }
2738 }
2739
2740 function mangleable_var(rhs) {
2741 if (force_single) {
2742 force_single = false;
2743 return;
2744 }
2745 if (remaining < 1) return;
2746 var value = rhs instanceof AST_Assign && rhs.operator == "=" ? rhs.left : rhs;
2747 if (!(value instanceof AST_SymbolRef)) return;
2748 var def = value.definition();
2749 if (def.undeclared) return;
2750 if (is_arguments(def)) return;
2751 if (value !== rhs) {
2752 if (is_lhs_read_only(value, compressor)) return;
2753 var referenced = def.references.length - def.replaced;
2754 if (referenced < 2) return;
2755 var expr = candidate.clone();
2756 expr[expr instanceof AST_Assign ? "right" : "value"] = value;
2757 if (candidate.name_index >= 0) {
2758 expr.name_index = candidate.name_index;
2759 expr.arg_index = candidate.arg_index;
2760 }
2761 candidate = expr;
2762 }
2763 return value_def = def;
2764 }
2765
2766 function remaining_refs(def) {
2767 return def.references.length - def.replaced - (assignments.get(def.name) || 0);
2768 }
2769
2770 function get_lhs(expr) {
2771 if (expr instanceof AST_Assign) {
2772 var lhs = expr.left;
2773 if (expr.operator != "=") return lhs;
2774 if (!(lhs instanceof AST_SymbolRef)) return lhs;
2775 var def = lhs.definition();
2776 if (scope.uses_arguments && is_funarg(def)) return lhs;
2777 if (compressor.exposed(def)) return lhs;
2778 remaining = remaining_refs(def);
2779 if (def.fixed && lhs.fixed) {
2780 var matches = def.references.filter(function(ref) {
2781 return ref.fixed === lhs.fixed;
2782 }).length - 1;
2783 if (matches < remaining) {
2784 remaining = matches;
2785 assign_pos = 0;
2786 }
2787 }
2788 mangleable_var(expr.right);
2789 return lhs;
2790 }
2791 if (expr instanceof AST_Binary) return expr.right.left;
2792 if (expr instanceof AST_Unary) return expr.expression;
2793 if (expr instanceof AST_VarDef) {
2794 var lhs = expr.name;
2795 var def = lhs.definition();
2796 if (def.const_redefs) return;
2797 if (!member(lhs, def.orig)) return;
2798 if (scope.uses_arguments && is_funarg(def)) return;
2799 var declared = def.orig.length - def.eliminated - (declare_only.get(def.name) || 0);
2800 remaining = remaining_refs(def);
2801 if (def.fixed) remaining = Math.min(remaining, def.references.filter(function(ref) {
2802 if (!ref.fixed) return true;
2803 if (!ref.fixed.assigns) return true;
2804 var assign = ref.fixed.assigns[0];
2805 return assign === lhs || get_rvalue(assign) === expr.value;
2806 }).length);
2807 if (declared > 1 && !(lhs instanceof AST_SymbolFunarg)) {
2808 mangleable_var(expr.value);
2809 return make_node(AST_SymbolRef, lhs, lhs);
2810 }
2811 if (mangleable_var(expr.value) || remaining == 1 && !compressor.exposed(def)) {
2812 return make_node(AST_SymbolRef, lhs, lhs);
2813 }
2814 return;
2815 }
2816 }
2817
2818 function get_rvalue(expr) {
2819 if (expr instanceof AST_Assign) return expr.right;
2820 if (expr instanceof AST_Binary) {
2821 var node = expr.clone();
2822 node.right = expr.right.right;
2823 return node;
2824 }
2825 if (expr instanceof AST_VarDef) return expr.value;
2826 }
2827
2828 function invariant(expr) {
2829 if (expr instanceof AST_Array) return false;
2830 if (expr instanceof AST_Binary && lazy_op[expr.operator]) {
2831 return invariant(expr.left) && invariant(expr.right);
2832 }
2833 if (expr instanceof AST_Call) return false;
2834 if (expr instanceof AST_Conditional) {
2835 return invariant(expr.consequent) && invariant(expr.alternative);
2836 }
2837 if (expr instanceof AST_Object) return false;
2838 return !expr.has_side_effects(compressor);
2839 }
2840
2841 function foldable(expr) {
2842 if (expr instanceof AST_Assign && expr.right.single_use) return;
2843 var lhs_ids = Object.create(null);
2844 var marker = new TreeWalker(function(node) {
2845 if (node instanceof AST_SymbolRef) lhs_ids[node.definition().id] = true;
2846 });
2847 while (expr instanceof AST_Assign && expr.operator == "=") {
2848 expr.left.walk(marker);
2849 expr = expr.right;
2850 }
2851 if (expr instanceof AST_ObjectIdentity) return rhs_exact_match;
2852 if (expr instanceof AST_SymbolRef) {
2853 var value = expr.evaluate(compressor);
2854 if (value === expr) return rhs_exact_match;
2855 return rhs_fuzzy_match(value, rhs_exact_match);
2856 }
2857 if (expr.is_truthy()) return rhs_fuzzy_match(true, return_false);
2858 if (expr.is_constant()) {
2859 var ev = expr.evaluate(compressor);
2860 if (!(ev instanceof AST_Node)) return rhs_fuzzy_match(ev, rhs_exact_match);
2861 }
2862 if (!(lhs instanceof AST_SymbolRef)) return false;
2863 if (!invariant(expr)) return false;
2864 var circular;
2865 expr.walk(new TreeWalker(function(node) {
2866 if (circular) return true;
2867 if (node instanceof AST_SymbolRef && lhs_ids[node.definition().id]) circular = true;
2868 }));
2869 return !circular && rhs_exact_match;
2870
2871 function rhs_exact_match(node) {
2872 return expr.equivalent_to(node);
2873 }
2874 }
2875
2876 function rhs_fuzzy_match(value, fallback) {
2877 return function(node, tw) {
2878 if (tw.in_boolean_context()) {
2879 if (value && node.is_truthy() && !node.has_side_effects(compressor)) {
2880 return true;
2881 }
2882 if (node.is_constant()) {
2883 var ev = node.evaluate(compressor);
2884 if (!(ev instanceof AST_Node)) return !ev == !value;
2885 }
2886 }
2887 return fallback(node);
2888 };
2889 }
2890
2891 function may_be_global(node) {
2892 if (node instanceof AST_SymbolRef) {
2893 node = node.fixed_value();
2894 if (!node) return true;
2895 }
2896 if (node instanceof AST_Assign) return node.operator == "=" && may_be_global(node.right);
2897 return node instanceof AST_PropAccess || node instanceof AST_ObjectIdentity;
2898 }
2899
2900 function get_lvalues(expr) {
2901 var lvalues = new Dictionary();
2902 if (expr instanceof AST_VarDef) {
2903 if (!expr.name.definition().fixed) well_defined = false;
2904 lvalues.add(expr.name.name, lhs);
2905 }
2906 var find_arguments = scope.uses_arguments && !compressor.has_directive("use strict");
2907 var scan_toplevel = scope instanceof AST_Toplevel;
2908 var tw = new TreeWalker(function(node) {
2909 var value;
2910 if (node instanceof AST_SymbolRef) {
2911 value = node.fixed_value();
2912 if (!value) {
2913 value = node;
2914 var def = node.definition();
2915 var escaped = node.fixed && node.fixed.escaped || def.escaped;
2916 if (!def.undeclared
2917 && (def.assignments || !escaped || escaped.cross_scope)
2918 && (has_escaped(def, node.scope, node, tw.parent()) || !same_scope(def))) {
2919 well_defined = false;
2920 }
2921 }
2922 } else if (node instanceof AST_ObjectIdentity) {
2923 value = node;
2924 }
2925 if (value) {
2926 lvalues.add(node.name, is_modified(compressor, tw, node, value, 0));
2927 } else if (node instanceof AST_Lambda) {
2928 for (var level = 0, parent, child = node; parent = tw.parent(level++); child = parent) {
2929 if (parent instanceof AST_Assign) {
2930 if (parent.left === child) break;
2931 if (parent.operator == "=") continue;
2932 if (lazy_op[parent.operator.slice(0, -1)]) continue;
2933 break;
2934 }
2935 if (parent instanceof AST_Binary) {
2936 if (lazy_op[parent.operator]) continue;
2937 break;
2938 }
2939 if (parent instanceof AST_Call) return;
2940 if (parent instanceof AST_Scope) return;
2941 if (parent instanceof AST_Sequence) {
2942 if (parent.tail_node() === child) continue;
2943 break;
2944 }
2945 if (parent instanceof AST_Template) {
2946 if (parent.tag) return;
2947 break;
2948 }
2949 }
2950 return true;
2951 } else if (find_arguments && node instanceof AST_Sub) {
2952 scope.each_argname(function(argname) {
2953 if (!compressor.option("reduce_vars") || argname.definition().assignments) {
2954 if (!argname.definition().fixed) well_defined = false;
2955 lvalues.add(argname.name, true);
2956 }
2957 });
2958 find_arguments = false;
2959 }
2960 if (!scan_toplevel) return;
2961 if (node.TYPE == "Call") {
2962 if (modify_toplevel) return;
2963 var exp = node.expression;
2964 if (exp instanceof AST_PropAccess) return;
2965 if (exp instanceof AST_LambdaExpression && !exp.contains_this()) return;
2966 modify_toplevel = true;
2967 } else if (node instanceof AST_PropAccess && may_be_global(node.expression)) {
2968 if (node === lhs && !(expr instanceof AST_Unary)) {
2969 modify_toplevel = true;
2970 } else {
2971 read_toplevel = true;
2972 }
2973 }
2974 });
2975 expr.walk(tw);
2976 return lvalues;
2977 }
2978
2979 function remove_candidate(expr) {
2980 var index = expr.name_index;
2981 if (index >= 0) {
2982 var argname = scope.argnames[index];
2983 if (argname instanceof AST_DefaultValue) {
2984 argname.value = make_node(AST_Number, argname, {
2985 value: 0
2986 });
2987 argname.name.definition().fixed = false;
2988 } else {
2989 var args = compressor.parent().args;
2990 if (args[index]) {
2991 args[index] = make_node(AST_Number, args[index], {
2992 value: 0
2993 });
2994 argname.definition().fixed = false;
2995 }
2996 }
2997 return true;
2998 }
2999 var end = hit_stack.length - 1;
3000 if (hit_stack[end - 1].body === hit_stack[end]) end--;
3001 var tt = new TreeTransformer(function(node, descend, in_list) {
3002 if (hit) return node;
3003 if (node !== hit_stack[hit_index]) return node;
3004 hit_index++;
3005 if (hit_index <= end) return handle_custom_scan_order(node, tt);
3006 hit = true;
3007 if (node instanceof AST_VarDef) {
3008 declare_only.set(node.name.name, (declare_only.get(node.name.name) || 0) + 1);
3009 if (value_def) value_def.replaced++;
3010 node = node.clone();
3011 node.value = null;
3012 return node;
3013 }
3014 return in_list ? List.skip : null;
3015 }, patch_sequence);
3016 abort = false;
3017 hit = false;
3018 hit_index = 0;
3019 return statements[stat_index].transform(tt);
3020 }
3021
3022 function patch_sequence(node) {
3023 if (node instanceof AST_Sequence) switch (node.expressions.length) {
3024 case 0: return null;
3025 case 1: return maintain_this_binding(compressor, this.parent(), node, node.expressions[0]);
3026 }
3027 }
3028
3029 function is_lhs_local(lhs) {
3030 var sym = root_expr(lhs);
3031 return sym instanceof AST_SymbolRef
3032 && sym.definition().scope.resolve() === scope
3033 && !(in_loop
3034 && (lvalues.has(sym.name) && lvalues.get(sym.name)[0] !== lhs
3035 || candidate instanceof AST_Unary
3036 || candidate instanceof AST_Assign && candidate.operator != "="));
3037 }
3038
3039 function value_has_side_effects() {
3040 if (candidate instanceof AST_Unary) return false;
3041 return rvalue.has_side_effects(compressor);
3042 }
3043
3044 function replace_all_symbols(expr) {
3045 if (expr instanceof AST_Unary) return false;
3046 if (side_effects) return false;
3047 if (value_def) return true;
3048 if (!(lhs instanceof AST_SymbolRef)) return false;
3049 var referenced;
3050 if (expr instanceof AST_VarDef) {
3051 referenced = 1;
3052 } else if (expr.operator == "=") {
3053 referenced = 2;
3054 } else {
3055 return false;
3056 }
3057 var def = lhs.definition();
3058 if (def.references.length - def.replaced == referenced) return true;
3059 return def.fixed && lhs.fixed && def.references.filter(function(ref) {
3060 return ref.fixed === lhs.fixed;
3061 }).length == referenced;
3062 }
3063
3064 function symbol_in_lvalues(sym, parent) {
3065 var lvalue = lvalues.get(sym.name);
3066 if (!lvalue || all(lvalue, function(lhs) {
3067 return !lhs;
3068 })) return;
3069 if (lvalue[0] !== lhs) return true;
3070 scan_rhs = false;
3071 }
3072
3073 function may_modify(sym) {
3074 var def = sym.definition();
3075 if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return false;
3076 if (def.scope.resolve() !== scope) return true;
3077 if (modify_toplevel && compressor.exposed(def)) return true;
3078 return !all(def.references, function(ref) {
3079 return ref.scope.resolve() === scope;
3080 });
3081 }
3082
3083 function side_effects_external(node, lhs) {
3084 if (node instanceof AST_Assign) return side_effects_external(node.left, true);
3085 if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
3086 if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
3087 if (lhs) {
3088 if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
3089 if (node instanceof AST_Sub) return side_effects_external(node.expression, true);
3090 if (node instanceof AST_SymbolRef) return node.definition().scope.resolve() !== scope;
3091 }
3092 return false;
3093 }
3094 }
3095
3096 function eliminate_spurious_blocks(statements) {
3097 var seen_dirs = [];
3098 for (var i = 0; i < statements.length;) {
3099 var stat = statements[i];
3100 if (stat instanceof AST_BlockStatement) {
3101 if (all(stat.body, safe_to_trim)) {
3102 CHANGED = true;
3103 eliminate_spurious_blocks(stat.body);
3104 [].splice.apply(statements, [i, 1].concat(stat.body));
3105 i += stat.body.length;
3106 continue;
3107 }
3108 }
3109 if (stat instanceof AST_Directive) {
3110 if (member(stat.value, seen_dirs)) {
3111 CHANGED = true;
3112 statements.splice(i, 1);
3113 continue;
3114 }
3115 seen_dirs.push(stat.value);
3116 }
3117 if (stat instanceof AST_EmptyStatement) {
3118 CHANGED = true;
3119 statements.splice(i, 1);
3120 continue;
3121 }
3122 i++;
3123 }
3124 }
3125
3126 function handle_if_return(statements, compressor) {
3127 var self = compressor.self();
3128 var parent = compressor.parent();
3129 var in_lambda = last_of(function(node) {
3130 return node instanceof AST_Lambda;
3131 });
3132 var in_iife = in_lambda && parent && parent.TYPE == "Call";
3133 var multiple_if_returns = has_multiple_if_returns(statements);
3134 for (var i = statements.length; --i >= 0;) {
3135 var stat = statements[i];
3136 var j = next_index(i);
3137 var next = statements[j];
3138
3139 if (in_lambda && !next && stat instanceof AST_Return) {
3140 if (!stat.value) {
3141 CHANGED = true;
3142 statements.splice(i, 1);
3143 continue;
3144 }
3145 var tail = stat.value.tail_node();
3146 if (tail instanceof AST_UnaryPrefix && tail.operator == "void") {
3147 CHANGED = true;
3148 var body;
3149 if (tail === stat.value) {
3150 body = tail.expression;
3151 } else {
3152 body = stat.value.clone();
3153 body.expressions[body.length - 1] = tail.expression;
3154 }
3155 statements[i] = make_node(AST_SimpleStatement, stat, {
3156 body: body,
3157 });
3158 continue;
3159 }
3160 }
3161
3162 if (stat instanceof AST_If) {
3163 var ab = aborts(stat.body);
3164 if (can_merge_flow(ab)) {
3165 if (ab.label) remove(ab.label.thedef.references, ab);
3166 CHANGED = true;
3167 stat = stat.clone();
3168 stat.condition = stat.condition.negate(compressor);
3169 var body = as_statement_array_with_return(stat.body, ab);
3170 stat.body = make_node(AST_BlockStatement, stat, {
3171 body: as_statement_array(stat.alternative).concat(extract_functions())
3172 });
3173 stat.alternative = make_node(AST_BlockStatement, stat, {
3174 body: body
3175 });
3176 statements[i] = stat;
3177 statements[i] = stat.transform(compressor);
3178 continue;
3179 }
3180
3181 if (ab && !stat.alternative && stat.body instanceof AST_BlockStatement && next instanceof AST_Jump) {
3182 var negated = stat.condition.negate(compressor);
3183 if (negated.print_to_string().length <= stat.condition.print_to_string().length) {
3184 CHANGED = true;
3185 stat = stat.clone();
3186 stat.condition = negated;
3187 statements[j] = stat.body;
3188 stat.body = next;
3189 statements[i] = stat;
3190 statements[i] = stat.transform(compressor);
3191 continue;
3192 }
3193 }
3194
3195 var alt = aborts(stat.alternative);
3196 if (can_merge_flow(alt)) {
3197 if (alt.label) remove(alt.label.thedef.references, alt);
3198 CHANGED = true;
3199 stat = stat.clone();
3200 stat.body = make_node(AST_BlockStatement, stat.body, {
3201 body: as_statement_array(stat.body).concat(extract_functions())
3202 });
3203 var body = as_statement_array_with_return(stat.alternative, alt);
3204 stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
3205 body: body
3206 });
3207 statements[i] = stat;
3208 statements[i] = stat.transform(compressor);
3209 continue;
3210 }
3211
3212 if (compressor.option("typeofs")) {
3213 if (ab && !alt) {
3214 mark_locally_defined(stat.condition, null, make_node(AST_BlockStatement, self, {
3215 body: statements.slice(i + 1)
3216 }));
3217 }
3218 if (!ab && alt) {
3219 mark_locally_defined(stat.condition, make_node(AST_BlockStatement, self, {
3220 body: statements.slice(i + 1)
3221 }));
3222 }
3223 }
3224 }
3225
3226 if (stat instanceof AST_If && stat.body instanceof AST_Return) {
3227 var value = stat.body.value;
3228 var in_bool = stat.body.in_bool || next instanceof AST_Return && next.in_bool;
3229 //---
3230 // pretty silly case, but:
3231 // if (foo()) return; return; ---> foo(); return;
3232 if (!value && !stat.alternative
3233 && (in_lambda && !next || next instanceof AST_Return && !next.value)) {
3234 CHANGED = true;
3235 statements[i] = make_node(AST_SimpleStatement, stat.condition, {
3236 body: stat.condition
3237 });
3238 continue;
3239 }
3240 //---
3241 // if (foo()) return x; return y; ---> return foo() ? x : y;
3242 if (!stat.alternative && next instanceof AST_Return) {
3243 CHANGED = true;
3244 stat = stat.clone();
3245 stat.alternative = next;
3246 statements.splice(i, 1, stat.transform(compressor));
3247 statements.splice(j, 1);
3248 continue;
3249 }
3250 //---
3251 // if (foo()) return x; [ return ; ] ---> return foo() ? x : undefined;
3252 if (!stat.alternative && !next && in_lambda && (in_bool || value && multiple_if_returns)) {
3253 CHANGED = true;
3254 stat = stat.clone();
3255 stat.alternative = make_node(AST_Return, stat, {
3256 value: null
3257 });
3258 statements.splice(i, 1, stat.transform(compressor));
3259 continue;
3260 }
3261 //---
3262 // if (a) return b; if (c) return d; e; ---> return a ? b : c ? d : void e;
3263 //
3264 // if sequences is not enabled, this can lead to an endless loop (issue #866).
3265 // however, with sequences on this helps producing slightly better output for
3266 // the example code.
3267 var prev = statements[prev_index(i)];
3268 if (compressor.option("sequences") && in_lambda && !stat.alternative
3269 && (!prev && in_iife || prev instanceof AST_If && prev.body instanceof AST_Return)
3270 && next_index(j) == statements.length && next instanceof AST_SimpleStatement) {
3271 CHANGED = true;
3272 stat = stat.clone();
3273 stat.alternative = make_node(AST_BlockStatement, next, {
3274 body: [
3275 next,
3276 make_node(AST_Return, next, {
3277 value: null
3278 })
3279 ]
3280 });
3281 statements.splice(i, 1, stat.transform(compressor));
3282 statements.splice(j, 1);
3283 continue;
3284 }
3285 }
3286 }
3287
3288 function has_multiple_if_returns(statements) {
3289 var n = 0;
3290 for (var i = statements.length; --i >= 0;) {
3291 var stat = statements[i];
3292 if (stat instanceof AST_If && stat.body instanceof AST_Return) {
3293 if (++n > 1) return true;
3294 }
3295 }
3296 return false;
3297 }
3298
3299 function is_return_void(value) {
3300 return !value || value instanceof AST_UnaryPrefix && value.operator == "void";
3301 }
3302
3303 function last_of(predicate) {
3304 var block = self, stat, level = 0;
3305 do {
3306 do {
3307 if (predicate(block)) return true;
3308 block = compressor.parent(level++);
3309 } while (block instanceof AST_If && (stat = block));
3310 } while ((block instanceof AST_BlockStatement || block instanceof AST_Scope)
3311 && is_last_statement(block.body, stat));
3312 }
3313
3314 function match_target(target) {
3315 return last_of(function(node) {
3316 return node === target;
3317 });
3318 }
3319
3320 function can_drop_abort(ab) {
3321 if (ab instanceof AST_Return) return in_lambda && is_return_void(ab.value);
3322 if (!(ab instanceof AST_LoopControl)) return false;
3323 var lct = compressor.loopcontrol_target(ab);
3324 if (ab instanceof AST_Continue) return match_target(loop_body(lct));
3325 if (lct instanceof AST_IterationStatement) return false;
3326 return match_target(lct);
3327 }
3328
3329 function can_merge_flow(ab) {
3330 if (!can_drop_abort(ab)) return false;
3331 for (var j = statements.length; --j > i;) {
3332 var stat = statements[j];
3333 if (stat instanceof AST_DefClass) {
3334 if (stat.name.definition().preinit) return false;
3335 } else if (stat instanceof AST_Const || stat instanceof AST_Let) {
3336 if (!all(stat.definitions, function(defn) {
3337 return !defn.name.match_symbol(function(node) {
3338 return node instanceof AST_SymbolDeclaration && node.definition().preinit;
3339 });
3340 })) return false;
3341 }
3342 }
3343 return true;
3344 }
3345
3346 function extract_functions() {
3347 var defuns = [];
3348 var lexical = false;
3349 var tail = statements.splice(i + 1).filter(function(stat) {
3350 if (stat instanceof AST_LambdaDefinition) {
3351 defuns.push(stat);
3352 return false;
3353 }
3354 if (is_lexical_definition(stat)) lexical = true;
3355 return true;
3356 });
3357 [].push.apply(lexical ? tail : statements, defuns);
3358 return tail;
3359 }
3360
3361 function as_statement_array_with_return(node, ab) {
3362 var body = as_statement_array(node);
3363 var block = body, last;
3364 while ((last = block[block.length - 1]) !== ab) {
3365 block = last.body;
3366 }
3367 block.pop();
3368 if (ab.value) block.push(make_node(AST_SimpleStatement, ab.value, {
3369 body: ab.value.expression
3370 }));
3371 return body;
3372 }
3373
3374 function next_index(i) {
3375 for (var j = i + 1; j < statements.length; j++) {
3376 if (!is_declaration(statements[j])) break;
3377 }
3378 return j;
3379 }
3380
3381 function prev_index(i) {
3382 for (var j = i; --j >= 0;) {
3383 if (!is_declaration(statements[j])) break;
3384 }
3385 return j;
3386 }
3387 }
3388
3389 function eliminate_dead_code(statements, compressor) {
3390 var has_quit;
3391 var self = compressor.self();
3392 for (var i = 0, n = 0, len = statements.length; i < len; i++) {
3393 var stat = statements[i];
3394 if (stat instanceof AST_LoopControl) {
3395 var lct = compressor.loopcontrol_target(stat);
3396 if (loop_body(lct) !== self
3397 || stat instanceof AST_Break && lct instanceof AST_IterationStatement) {
3398 statements[n++] = stat;
3399 } else if (stat.label) {
3400 remove(stat.label.thedef.references, stat);
3401 }
3402 } else {
3403 statements[n++] = stat;
3404 }
3405 if (aborts(stat)) {
3406 has_quit = statements.slice(i + 1);
3407 break;
3408 }
3409 }
3410 statements.length = n;
3411 if (has_quit) has_quit.forEach(function(stat) {
3412 extract_declarations_from_unreachable_code(compressor, stat, statements);
3413 });
3414 CHANGED = statements.length != len;
3415 }
3416
3417 function sequencesize(statements, compressor) {
3418 if (statements.length < 2) return;
3419 var seq = [], n = 0;
3420 function push_seq() {
3421 if (!seq.length) return;
3422 var body = make_sequence(seq[0], seq);
3423 statements[n++] = make_node(AST_SimpleStatement, body, { body: body });
3424 seq = [];
3425 }
3426 for (var i = 0, len = statements.length; i < len; i++) {
3427 var stat = statements[i];
3428 if (stat instanceof AST_SimpleStatement) {
3429 if (seq.length >= compressor.sequences_limit) push_seq();
3430 var body = stat.body;
3431 if (seq.length > 0) body = body.drop_side_effect_free(compressor);
3432 if (body) merge_sequence(seq, body);
3433 } else if (is_declaration(stat)) {
3434 statements[n++] = stat;
3435 } else {
3436 push_seq();
3437 statements[n++] = stat;
3438 }
3439 }
3440 push_seq();
3441 statements.length = n;
3442 if (n != len) CHANGED = true;
3443 }
3444
3445 function to_simple_statement(block, decls) {
3446 if (!(block instanceof AST_BlockStatement)) return block;
3447 var stat = null;
3448 for (var i = 0; i < block.body.length; i++) {
3449 var line = block.body[i];
3450 if (line instanceof AST_Var && declarations_only(line)) {
3451 decls.push(line);
3452 } else if (stat || is_lexical_definition(line)) {
3453 return false;
3454 } else {
3455 stat = line;
3456 }
3457 }
3458 return stat;
3459 }
3460
3461 function sequencesize_2(statements, compressor) {
3462 function cons_seq(right) {
3463 n--;
3464 CHANGED = true;
3465 var left = prev.body;
3466 return make_sequence(left, [ left, right ]);
3467 }
3468 var n = 0, prev;
3469 for (var i = 0; i < statements.length; i++) {
3470 var stat = statements[i];
3471 if (prev) {
3472 if (stat instanceof AST_Exit) {
3473 if (stat.value || !in_async_generator(scope)) {
3474 stat.value = cons_seq(stat.value || make_node(AST_Undefined, stat)).optimize(compressor);
3475 }
3476 } else if (stat instanceof AST_For) {
3477 if (!(stat.init instanceof AST_Definitions)) {
3478 var abort = false;
3479 prev.body.walk(new TreeWalker(function(node) {
3480 if (abort || node instanceof AST_Scope) return true;
3481 if (node instanceof AST_Binary && node.operator == "in") {
3482 abort = true;
3483 return true;
3484 }
3485 }));
3486 if (!abort) {
3487 if (stat.init) stat.init = cons_seq(stat.init);
3488 else {
3489 stat.init = prev.body;
3490 n--;
3491 CHANGED = true;
3492 }
3493 }
3494 }
3495 } else if (stat instanceof AST_ForIn) {
3496 if (!is_lexical_definition(stat.init)) stat.object = cons_seq(stat.object);
3497 } else if (stat instanceof AST_If) {
3498 stat.condition = cons_seq(stat.condition);
3499 } else if (stat instanceof AST_Switch) {
3500 stat.expression = cons_seq(stat.expression);
3501 } else if (stat instanceof AST_With) {
3502 stat.expression = cons_seq(stat.expression);
3503 }
3504 }
3505 if (compressor.option("conditionals") && stat instanceof AST_If) {
3506 var decls = [];
3507 var body = to_simple_statement(stat.body, decls);
3508 var alt = to_simple_statement(stat.alternative, decls);
3509 if (body !== false && alt !== false && decls.length > 0) {
3510 var len = decls.length;
3511 decls.push(make_node(AST_If, stat, {
3512 condition: stat.condition,
3513 body: body || make_node(AST_EmptyStatement, stat.body),
3514 alternative: alt
3515 }));
3516 decls.unshift(n, 1);
3517 [].splice.apply(statements, decls);
3518 i += len;
3519 n += len + 1;
3520 prev = null;
3521 CHANGED = true;
3522 continue;
3523 }
3524 }
3525 statements[n++] = stat;
3526 prev = stat instanceof AST_SimpleStatement ? stat : null;
3527 }
3528 statements.length = n;
3529 }
3530
3531 function extract_exprs(body) {
3532 if (body instanceof AST_Assign) return [ body ];
3533 if (body instanceof AST_Sequence) return body.expressions.slice();
3534 }
3535
3536 function join_assigns(defn, body, keep) {
3537 var exprs = extract_exprs(body);
3538 if (!exprs) return;
3539 var trimmed = false;
3540 for (var i = exprs.length - (keep || 0); --i >= 0;) {
3541 var expr = exprs[i];
3542 if (!can_trim(expr)) continue;
3543 var tail;
3544 if (expr.left instanceof AST_SymbolRef) {
3545 tail = exprs.slice(i + 1);
3546 } else if (expr.left instanceof AST_PropAccess && can_trim(expr.left.expression)) {
3547 tail = exprs.slice(i + 1);
3548 var flattened = expr.clone();
3549 expr = expr.left.expression;
3550 flattened.left = flattened.left.clone();
3551 flattened.left.expression = expr.left.clone();
3552 tail.unshift(flattened);
3553 } else {
3554 continue;
3555 }
3556 if (tail.length == 0) continue;
3557 if (!trim_assigns(expr.left, expr.right, tail)) continue;
3558 trimmed = true;
3559 exprs = exprs.slice(0, i).concat(expr, tail);
3560 }
3561 if (defn instanceof AST_Definitions) {
3562 keep = keep || 0;
3563 for (var i = defn.definitions.length; --i >= 0;) {
3564 var def = defn.definitions[i];
3565 if (!def.value) continue;
3566 if (trim_assigns(def.name, def.value, exprs)) trimmed = true;
3567 if (merge_conditional_assignments(def, exprs, keep)) trimmed = true;
3568 break;
3569 }
3570 if (defn instanceof AST_Var && join_var_assign(defn.definitions, exprs, keep)) trimmed = true;
3571 }
3572 return trimmed && exprs;
3573
3574 function can_trim(node) {
3575 return node instanceof AST_Assign && node.operator == "=";
3576 }
3577 }
3578
3579 function merge_assigns(prev, defn) {
3580 if (!(prev instanceof AST_SimpleStatement)) return;
3581 if (declarations_only(defn)) return;
3582 var exprs = extract_exprs(prev.body);
3583 if (!exprs) return;
3584 var definitions = [];
3585 if (!join_var_assign(definitions, exprs.reverse(), 0)) return;
3586 defn.definitions = definitions.reverse().concat(defn.definitions);
3587 return exprs.reverse();
3588 }
3589
3590 function merge_conditional_assignments(var_def, exprs, keep) {
3591 if (!compressor.option("conditionals")) return;
3592 if (var_def.name instanceof AST_Destructured) return;
3593 var trimmed = false;
3594 var def = var_def.name.definition();
3595 while (exprs.length > keep) {
3596 var cond = to_conditional_assignment(compressor, def, var_def.value, exprs[0]);
3597 if (!cond) break;
3598 var_def.value = cond;
3599 exprs.shift();
3600 trimmed = true;
3601 }
3602 return trimmed;
3603 }
3604
3605 function join_var_assign(definitions, exprs, keep) {
3606 var trimmed = false;
3607 while (exprs.length > keep) {
3608 var expr = exprs[0];
3609 if (!(expr instanceof AST_Assign)) break;
3610 if (expr.operator != "=") break;
3611 var lhs = expr.left;
3612 if (!(lhs instanceof AST_SymbolRef)) break;
3613 if (is_undeclared_ref(lhs)) break;
3614 if (lhs.scope.resolve() !== scope) break;
3615 var def = lhs.definition();
3616 if (def.scope !== scope) break;
3617 if (def.orig.length > def.eliminated + 1) break;
3618 if (def.orig[0].TYPE != "SymbolVar") break;
3619 var name = make_node(AST_SymbolVar, lhs, lhs);
3620 definitions.push(make_node(AST_VarDef, expr, {
3621 name: name,
3622 value: expr.right
3623 }));
3624 def.orig.push(name);
3625 def.replaced++;
3626 exprs.shift();
3627 trimmed = true;
3628 }
3629 return trimmed;
3630 }
3631
3632 function trim_assigns(name, value, exprs) {
3633 var names = new Dictionary();
3634 names.set(name.name, true);
3635 while (value instanceof AST_Assign && value.operator == "=") {
3636 if (value.left instanceof AST_SymbolRef) names.set(value.left.name, true);
3637 value = value.right;
3638 }
3639 if (!(value instanceof AST_Object)) return;
3640 var trimmed = false;
3641 do {
3642 if (!try_join(exprs[0])) break;
3643 exprs.shift();
3644 trimmed = true;
3645 } while (exprs.length);
3646 return trimmed;
3647
3648 function try_join(node) {
3649 if (!(node instanceof AST_Assign)) return;
3650 if (node.operator != "=") return;
3651 if (!(node.left instanceof AST_PropAccess)) return;
3652 var sym = node.left.expression;
3653 if (!(sym instanceof AST_SymbolRef)) return;
3654 if (!names.has(sym.name)) return;
3655 if (!node.right.is_constant_expression(scope)) return;
3656 var prop = node.left.property;
3657 if (prop instanceof AST_Node) {
3658 if (try_join(prop)) prop = node.left.property = prop.right.clone();
3659 prop = prop.evaluate(compressor);
3660 }
3661 if (prop instanceof AST_Node) return;
3662 prop = "" + prop;
3663 var diff = prop == "__proto__" || compressor.has_directive("use strict") ? function(node) {
3664 var key = node.key;
3665 return typeof key == "string" && key != prop && key != "__proto__";
3666 } : function(node) {
3667 var key = node.key;
3668 if (node instanceof AST_ObjectGetter || node instanceof AST_ObjectSetter) {
3669 return typeof key == "string" && key != prop;
3670 }
3671 return key !== "__proto__";
3672 };
3673 if (!all(value.properties, diff)) return;
3674 value.properties.push(make_node(AST_ObjectKeyVal, node, {
3675 key: prop,
3676 value: node.right,
3677 }));
3678 return true;
3679 }
3680 }
3681
3682 function join_consecutive_vars(statements) {
3683 var defs;
3684 for (var i = 0, j = -1; i < statements.length; i++) {
3685 var stat = statements[i];
3686 var prev = statements[j];
3687 if (stat instanceof AST_Definitions) {
3688 if (prev && prev.TYPE == stat.TYPE) {
3689 prev.definitions = prev.definitions.concat(stat.definitions);
3690 CHANGED = true;
3691 } else if (defs && defs.TYPE == stat.TYPE && declarations_only(stat)) {
3692 defs.definitions = defs.definitions.concat(stat.definitions);
3693 CHANGED = true;
3694 } else if (stat instanceof AST_Var) {
3695 var exprs = merge_assigns(prev, stat);
3696 if (exprs) {
3697 if (exprs.length) {
3698 prev.body = make_sequence(prev, exprs);
3699 j++;
3700 }
3701 CHANGED = true;
3702 } else {
3703 j++;
3704 }
3705 statements[j] = defs = stat;
3706 } else {
3707 statements[++j] = stat;
3708 }
3709 continue;
3710 } else if (stat instanceof AST_Exit) {
3711 stat.value = join_assigns_expr(stat.value);
3712 } else if (stat instanceof AST_For) {
3713 var exprs = join_assigns(prev, stat.init);
3714 if (exprs) {
3715 CHANGED = true;
3716 stat.init = exprs.length ? make_sequence(stat.init, exprs) : null;
3717 } else if (prev instanceof AST_Var && (!stat.init || stat.init.TYPE == prev.TYPE)) {
3718 if (stat.init) {
3719 prev.definitions = prev.definitions.concat(stat.init.definitions);
3720 }
3721 defs = stat.init = prev;
3722 statements[j] = merge_defns(stat);
3723 CHANGED = true;
3724 continue;
3725 } else if (defs && stat.init && defs.TYPE == stat.init.TYPE && declarations_only(stat.init)) {
3726 defs.definitions = defs.definitions.concat(stat.init.definitions);
3727 stat.init = null;
3728 CHANGED = true;
3729 } else if (stat.init instanceof AST_Var) {
3730 defs = stat.init;
3731 exprs = merge_assigns(prev, stat.init);
3732 if (exprs) {
3733 CHANGED = true;
3734 if (exprs.length == 0) {
3735 statements[j] = merge_defns(stat);
3736 continue;
3737 }
3738 prev.body = make_sequence(prev, exprs);
3739 }
3740 }
3741 } else if (stat instanceof AST_ForEnumeration) {
3742 if (defs && defs.TYPE == stat.init.TYPE) {
3743 var defns = defs.definitions.slice();
3744 stat.init = stat.init.definitions[0].name.convert_symbol(AST_SymbolRef, function(ref, name) {
3745 defns.push(make_node(AST_VarDef, name, {
3746 name: name,
3747 value: null,
3748 }));
3749 name.definition().references.push(ref);
3750 });
3751 defs.definitions = defns;
3752 CHANGED = true;
3753 }
3754 stat.object = join_assigns_expr(stat.object);
3755 } else if (stat instanceof AST_If) {
3756 stat.condition = join_assigns_expr(stat.condition);
3757 } else if (stat instanceof AST_SimpleStatement) {
3758 var exprs = join_assigns(prev, stat.body);
3759 if (exprs) {
3760 CHANGED = true;
3761 if (!exprs.length) continue;
3762 stat.body = make_sequence(stat.body, exprs);
3763 }
3764 } else if (stat instanceof AST_Switch) {
3765 stat.expression = join_assigns_expr(stat.expression);
3766 } else if (stat instanceof AST_With) {
3767 stat.expression = join_assigns_expr(stat.expression);
3768 }
3769 statements[++j] = defs ? merge_defns(stat) : stat;
3770 }
3771 statements.length = j + 1;
3772
3773 function join_assigns_expr(value) {
3774 var exprs = join_assigns(prev, value, 1);
3775 if (!exprs) return value;
3776 CHANGED = true;
3777 var tail = value.tail_node();
3778 if (exprs[exprs.length - 1] !== tail) exprs.push(tail.left);
3779 return make_sequence(value, exprs);
3780 }
3781
3782 function merge_defns(stat) {
3783 return stat.transform(new TreeTransformer(function(node, descend, in_list) {
3784 if (node instanceof AST_Definitions) {
3785 if (defs === node) return node;
3786 if (defs.TYPE != node.TYPE) return node;
3787 var parent = this.parent();
3788 if (parent instanceof AST_ForEnumeration && parent.init === node) return node;
3789 if (!declarations_only(node)) return node;
3790 defs.definitions = defs.definitions.concat(node.definitions);
3791 CHANGED = true;
3792 if (parent instanceof AST_For && parent.init === node) return null;
3793 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
3794 }
3795 if (node instanceof AST_ExportDeclaration) return node;
3796 if (node instanceof AST_Scope) return node;
3797 if (!is_statement(node)) return node;
3798 }));
3799 }
3800 }
3801 }
3802
3803 function extract_declarations_from_unreachable_code(compressor, stat, target) {
3804 var block;
3805 var dropped = false;
3806 stat.walk(new TreeWalker(function(node, descend) {
3807 if (node instanceof AST_DefClass) {
3808 node.extends = null;
3809 node.properties = [];
3810 push(node);
3811 return true;
3812 }
3813 if (node instanceof AST_Definitions) {
3814 var defns = [];
3815 if (node.remove_initializers(compressor, defns)) {
3816 AST_Node.warn("Dropping initialization in unreachable code [{file}:{line},{col}]", node.start);
3817 }
3818 if (defns.length > 0) {
3819 node.definitions = defns;
3820 push(node);
3821 }
3822 return true;
3823 }
3824 if (node instanceof AST_LambdaDefinition) {
3825 push(node);
3826 return true;
3827 }
3828 if (node instanceof AST_Scope) return true;
3829 if (node instanceof AST_BlockScope) {
3830 var save = block;
3831 block = [];
3832 descend();
3833 if (block.required) {
3834 target.push(make_node(AST_BlockStatement, stat, { body: block }));
3835 } else if (block.length) {
3836 [].push.apply(target, block);
3837 }
3838 block = save;
3839 return true;
3840 }
3841 if (!(node instanceof AST_LoopControl)) dropped = true;
3842 }));
3843 if (dropped) AST_Node.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
3844
3845 function push(node) {
3846 if (block) {
3847 block.push(node);
3848 if (!safe_to_trim(node)) block.required = true;
3849 } else {
3850 target.push(node);
3851 }
3852 }
3853 }
3854
3855 function is_undefined(node, compressor) {
3856 return node.is_undefined
3857 || node instanceof AST_Undefined
3858 || node instanceof AST_UnaryPrefix
3859 && node.operator == "void"
3860 && !(compressor && node.expression.has_side_effects(compressor));
3861 }
3862
3863 // is_truthy()
3864 // return true if `!!node === true`
3865 (function(def) {
3866 def(AST_Node, return_false);
3867 def(AST_Array, return_true);
3868 def(AST_Assign, function() {
3869 return this.operator == "=" && this.right.is_truthy();
3870 });
3871 def(AST_Lambda, return_true);
3872 def(AST_Object, return_true);
3873 def(AST_RegExp, return_true);
3874 def(AST_Sequence, function() {
3875 return this.tail_node().is_truthy();
3876 });
3877 def(AST_SymbolRef, function() {
3878 var fixed = this.fixed_value();
3879 if (!fixed) return false;
3880 this.is_truthy = return_false;
3881 var result = fixed.is_truthy();
3882 delete this.is_truthy;
3883 return result;
3884 });
3885 })(function(node, func) {
3886 node.DEFMETHOD("is_truthy", func);
3887 });
3888
3889 // is_negative_zero()
3890 // return true if the node may represent -0
3891 (function(def) {
3892 def(AST_Node, return_true);
3893 def(AST_Array, return_false);
3894 function binary(op, left, right) {
3895 switch (op) {
3896 case "-":
3897 return left.is_negative_zero()
3898 && (!(right instanceof AST_Constant) || right.value == 0);
3899 case "&&":
3900 case "||":
3901 return left.is_negative_zero() || right.is_negative_zero();
3902 case "*":
3903 case "/":
3904 case "%":
3905 case "**":
3906 return true;
3907 default:
3908 return false;
3909 }
3910 }
3911 def(AST_Assign, function() {
3912 var op = this.operator;
3913 if (op == "=") return this.right.is_negative_zero();
3914 return binary(op.slice(0, -1), this.left, this.right);
3915 });
3916 def(AST_Binary, function() {
3917 return binary(this.operator, this.left, this.right);
3918 });
3919 def(AST_Constant, function() {
3920 return this.value == 0 && 1 / this.value < 0;
3921 });
3922 def(AST_Lambda, return_false);
3923 def(AST_Object, return_false);
3924 def(AST_RegExp, return_false);
3925 def(AST_Sequence, function() {
3926 return this.tail_node().is_negative_zero();
3927 });
3928 def(AST_SymbolRef, function() {
3929 var fixed = this.fixed_value();
3930 if (!fixed) return true;
3931 this.is_negative_zero = return_true;
3932 var result = fixed.is_negative_zero();
3933 delete this.is_negative_zero;
3934 return result;
3935 });
3936 def(AST_UnaryPrefix, function() {
3937 return this.operator == "+" && this.expression.is_negative_zero()
3938 || this.operator == "-";
3939 });
3940 })(function(node, func) {
3941 node.DEFMETHOD("is_negative_zero", func);
3942 });
3943
3944 // may_throw_on_access()
3945 // returns true if this node may be null, undefined or contain `AST_Accessor`
3946 (function(def) {
3947 AST_Node.DEFMETHOD("may_throw_on_access", function(compressor, force) {
3948 return !compressor.option("pure_getters") || this._dot_throw(compressor, force);
3949 });
3950 function is_strict(compressor, force) {
3951 return force || /strict/.test(compressor.option("pure_getters"));
3952 }
3953 def(AST_Node, is_strict);
3954 def(AST_Array, return_false);
3955 def(AST_Assign, function(compressor) {
3956 var op = this.operator;
3957 var sym = this.left;
3958 var rhs = this.right;
3959 if (op != "=") {
3960 return lazy_op[op.slice(0, -1)] && (sym._dot_throw(compressor) || rhs._dot_throw(compressor));
3961 }
3962 if (!rhs._dot_throw(compressor)) return false;
3963 if (!(sym instanceof AST_SymbolRef)) return true;
3964 if (rhs instanceof AST_Binary && rhs.operator == "||" && sym.name == rhs.left.name) {
3965 return rhs.right._dot_throw(compressor);
3966 }
3967 return true;
3968 });
3969 def(AST_Binary, function(compressor) {
3970 return lazy_op[this.operator] && (this.left._dot_throw(compressor) || this.right._dot_throw(compressor));
3971 });
3972 def(AST_Class, return_false);
3973 def(AST_Conditional, function(compressor) {
3974 return this.consequent._dot_throw(compressor) || this.alternative._dot_throw(compressor);
3975 });
3976 def(AST_Constant, return_false);
3977 def(AST_Dot, function(compressor, force) {
3978 if (!is_strict(compressor, force)) return false;
3979 var exp = this.expression;
3980 if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
3981 return !(this.property == "prototype" && is_lambda(exp));
3982 });
3983 def(AST_Lambda, return_false);
3984 def(AST_Null, return_true);
3985 def(AST_Object, function(compressor, force) {
3986 return is_strict(compressor, force) && !all(this.properties, function(prop) {
3987 if (!(prop instanceof AST_ObjectKeyVal)) return false;
3988 return !(prop.key === "__proto__" && prop.value._dot_throw(compressor, force));
3989 });
3990 });
3991 def(AST_ObjectIdentity, function(compressor, force) {
3992 return is_strict(compressor, force) && !this.scope.resolve().new;
3993 });
3994 def(AST_Sequence, function(compressor) {
3995 return this.tail_node()._dot_throw(compressor);
3996 });
3997 def(AST_SymbolRef, function(compressor, force) {
3998 if (this.is_undefined) return true;
3999 if (!is_strict(compressor, force)) return false;
4000 if (is_undeclared_ref(this) && this.is_declared(compressor)) return false;
4001 if (this.is_immutable()) return false;
4002 var def = this.definition();
4003 if (is_arguments(def) && !def.scope.rest && all(def.scope.argnames, function(argname) {
4004 return argname instanceof AST_SymbolFunarg;
4005 })) return def.scope.uses_arguments > 2;
4006 var fixed = this.fixed_value();
4007 if (!fixed) return true;
4008 this._dot_throw = return_true;
4009 if (fixed._dot_throw(compressor)) {
4010 delete this._dot_throw;
4011 return true;
4012 }
4013 this._dot_throw = return_false;
4014 return false;
4015 });
4016 def(AST_UnaryPrefix, function() {
4017 return this.operator == "void";
4018 });
4019 def(AST_UnaryPostfix, return_false);
4020 def(AST_Undefined, return_true);
4021 })(function(node, func) {
4022 node.DEFMETHOD("_dot_throw", func);
4023 });
4024
4025 (function(def) {
4026 def(AST_Node, return_false);
4027 def(AST_Array, return_true);
4028 function is_binary_defined(compressor, op, node) {
4029 switch (op) {
4030 case "&&":
4031 return node.left.is_defined(compressor) && node.right.is_defined(compressor);
4032 case "||":
4033 return node.left.is_truthy() || node.right.is_defined(compressor);
4034 case "??":
4035 return node.left.is_defined(compressor) || node.right.is_defined(compressor);
4036 default:
4037 return true;
4038 }
4039 }
4040 def(AST_Assign, function(compressor) {
4041 var op = this.operator;
4042 if (op == "=") return this.right.is_defined(compressor);
4043 return is_binary_defined(compressor, op.slice(0, -1), this);
4044 });
4045 def(AST_Binary, function(compressor) {
4046 return is_binary_defined(compressor, this.operator, this);
4047 });
4048 def(AST_Conditional, function(compressor) {
4049 return this.consequent.is_defined(compressor) && this.alternative.is_defined(compressor);
4050 });
4051 def(AST_Constant, return_true);
4052 def(AST_Hole, return_false);
4053 def(AST_Lambda, return_true);
4054 def(AST_Object, return_true);
4055 def(AST_Sequence, function(compressor) {
4056 return this.tail_node().is_defined(compressor);
4057 });
4058 def(AST_SymbolRef, function(compressor) {
4059 if (this.is_undefined) return false;
4060 if (is_undeclared_ref(this) && this.is_declared(compressor)) return true;
4061 if (this.is_immutable()) return true;
4062 var fixed = this.fixed_value();
4063 if (!fixed) return false;
4064 this.is_defined = return_false;
4065 var result = fixed.is_defined(compressor);
4066 delete this.is_defined;
4067 return result;
4068 });
4069 def(AST_UnaryPrefix, function() {
4070 return this.operator != "void";
4071 });
4072 def(AST_UnaryPostfix, return_true);
4073 def(AST_Undefined, return_false);
4074 })(function(node, func) {
4075 node.DEFMETHOD("is_defined", func);
4076 });
4077
4078 /* -----[ boolean/negation helpers ]----- */
4079
4080 // methods to determine whether an expression has a boolean result type
4081 (function(def) {
4082 def(AST_Node, return_false);
4083 def(AST_Assign, function(compressor) {
4084 return this.operator == "=" && this.right.is_boolean(compressor);
4085 });
4086 var binary = makePredicate("in instanceof == != === !== < <= >= >");
4087 def(AST_Binary, function(compressor) {
4088 return binary[this.operator] || lazy_op[this.operator]
4089 && this.left.is_boolean(compressor)
4090 && this.right.is_boolean(compressor);
4091 });
4092 def(AST_Boolean, return_true);
4093 var fn = makePredicate("every hasOwnProperty isPrototypeOf propertyIsEnumerable some");
4094 def(AST_Call, function(compressor) {
4095 if (!compressor.option("unsafe")) return false;
4096 var exp = this.expression;
4097 return exp instanceof AST_Dot && (fn[exp.property]
4098 || exp.property == "test" && exp.expression instanceof AST_RegExp);
4099 });
4100 def(AST_Conditional, function(compressor) {
4101 return this.consequent.is_boolean(compressor) && this.alternative.is_boolean(compressor);
4102 });
4103 def(AST_New, return_false);
4104 def(AST_Sequence, function(compressor) {
4105 return this.tail_node().is_boolean(compressor);
4106 });
4107 def(AST_SymbolRef, function(compressor) {
4108 var fixed = this.fixed_value();
4109 if (!fixed) return false;
4110 this.is_boolean = return_false;
4111 var result = fixed.is_boolean(compressor);
4112 delete this.is_boolean;
4113 return result;
4114 });
4115 var unary = makePredicate("! delete");
4116 def(AST_UnaryPrefix, function() {
4117 return unary[this.operator];
4118 });
4119 })(function(node, func) {
4120 node.DEFMETHOD("is_boolean", func);
4121 });
4122
4123 // methods to determine if an expression has a numeric result type
4124 (function(def) {
4125 def(AST_Node, return_false);
4126 var binary = makePredicate("- * / % ** & | ^ << >> >>>");
4127 def(AST_Assign, function(compressor) {
4128 return binary[this.operator.slice(0, -1)]
4129 || this.operator == "=" && this.right.is_number(compressor);
4130 });
4131 def(AST_Binary, function(compressor) {
4132 if (binary[this.operator]) return true;
4133 if (this.operator != "+") return false;
4134 return (this.left.is_boolean(compressor) || this.left.is_number(compressor))
4135 && (this.right.is_boolean(compressor) || this.right.is_number(compressor));
4136 });
4137 var fn = makePredicate([
4138 "charCodeAt",
4139 "getDate",
4140 "getDay",
4141 "getFullYear",
4142 "getHours",
4143 "getMilliseconds",
4144 "getMinutes",
4145 "getMonth",
4146 "getSeconds",
4147 "getTime",
4148 "getTimezoneOffset",
4149 "getUTCDate",
4150 "getUTCDay",
4151 "getUTCFullYear",
4152 "getUTCHours",
4153 "getUTCMilliseconds",
4154 "getUTCMinutes",
4155 "getUTCMonth",
4156 "getUTCSeconds",
4157 "getYear",
4158 "indexOf",
4159 "lastIndexOf",
4160 "localeCompare",
4161 "push",
4162 "search",
4163 "setDate",
4164 "setFullYear",
4165 "setHours",
4166 "setMilliseconds",
4167 "setMinutes",
4168 "setMonth",
4169 "setSeconds",
4170 "setTime",
4171 "setUTCDate",
4172 "setUTCFullYear",
4173 "setUTCHours",
4174 "setUTCMilliseconds",
4175 "setUTCMinutes",
4176 "setUTCMonth",
4177 "setUTCSeconds",
4178 "setYear",
4179 "toExponential",
4180 "toFixed",
4181 "toPrecision",
4182 ]);
4183 def(AST_Call, function(compressor) {
4184 if (!compressor.option("unsafe")) return false;
4185 var exp = this.expression;
4186 return exp instanceof AST_Dot && (fn[exp.property]
4187 || is_undeclared_ref(exp.expression) && exp.expression.name == "Math");
4188 });
4189 def(AST_Conditional, function(compressor) {
4190 return this.consequent.is_number(compressor) && this.alternative.is_number(compressor);
4191 });
4192 def(AST_New, return_false);
4193 def(AST_Number, return_true);
4194 def(AST_Sequence, function(compressor) {
4195 return this.tail_node().is_number(compressor);
4196 });
4197 def(AST_SymbolRef, function(compressor) {
4198 var fixed = this.fixed_value();
4199 if (!fixed) return false;
4200 this.is_number = return_false;
4201 var result = fixed.is_number(compressor);
4202 delete this.is_number;
4203 return result;
4204 });
4205 var unary = makePredicate("+ - ~ ++ --");
4206 def(AST_Unary, function() {
4207 return unary[this.operator];
4208 });
4209 })(function(node, func) {
4210 node.DEFMETHOD("is_number", func);
4211 });
4212
4213 // methods to determine if an expression has a string result type
4214 (function(def) {
4215 def(AST_Node, return_false);
4216 def(AST_Assign, function(compressor) {
4217 switch (this.operator) {
4218 case "+=":
4219 if (this.left.is_string(compressor)) return true;
4220 case "=":
4221 return this.right.is_string(compressor);
4222 }
4223 });
4224 def(AST_Binary, function(compressor) {
4225 return this.operator == "+" &&
4226 (this.left.is_string(compressor) || this.right.is_string(compressor));
4227 });
4228 var fn = makePredicate([
4229 "charAt",
4230 "substr",
4231 "substring",
4232 "toLowerCase",
4233 "toString",
4234 "toUpperCase",
4235 "trim",
4236 ]);
4237 def(AST_Call, function(compressor) {
4238 if (!compressor.option("unsafe")) return false;
4239 var exp = this.expression;
4240 return exp instanceof AST_Dot && fn[exp.property];
4241 });
4242 def(AST_Conditional, function(compressor) {
4243 return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
4244 });
4245 def(AST_Sequence, function(compressor) {
4246 return this.tail_node().is_string(compressor);
4247 });
4248 def(AST_String, return_true);
4249 def(AST_SymbolRef, function(compressor) {
4250 var fixed = this.fixed_value();
4251 if (!fixed) return false;
4252 this.is_string = return_false;
4253 var result = fixed.is_string(compressor);
4254 delete this.is_string;
4255 return result;
4256 });
4257 def(AST_Template, function(compressor) {
4258 return !this.tag || is_raw_tag(compressor, this.tag);
4259 });
4260 def(AST_UnaryPrefix, function() {
4261 return this.operator == "typeof";
4262 });
4263 })(function(node, func) {
4264 node.DEFMETHOD("is_string", func);
4265 });
4266
4267 var lazy_op = makePredicate("&& || ??");
4268
4269 (function(def) {
4270 function to_node(value, orig) {
4271 if (value instanceof AST_Node) return value.clone(true);
4272 if (Array.isArray(value)) return make_node(AST_Array, orig, {
4273 elements: value.map(function(value) {
4274 return to_node(value, orig);
4275 })
4276 });
4277 if (value && typeof value == "object") {
4278 var props = [];
4279 for (var key in value) if (HOP(value, key)) {
4280 props.push(make_node(AST_ObjectKeyVal, orig, {
4281 key: key,
4282 value: to_node(value[key], orig)
4283 }));
4284 }
4285 return make_node(AST_Object, orig, {
4286 properties: props
4287 });
4288 }
4289 return make_node_from_constant(value, orig);
4290 }
4291
4292 function warn(node) {
4293 AST_Node.warn("global_defs {node} redefined [{file}:{line},{col}]", {
4294 node: node,
4295 file: node.start.file,
4296 line: node.start.line,
4297 col: node.start.col,
4298 });
4299 }
4300
4301 AST_Toplevel.DEFMETHOD("resolve_defines", function(compressor) {
4302 if (!compressor.option("global_defs")) return this;
4303 this.figure_out_scope({ ie: compressor.option("ie") });
4304 return this.transform(new TreeTransformer(function(node) {
4305 var def = node._find_defs(compressor, "");
4306 if (!def) return;
4307 var level = 0, child = node, parent;
4308 while (parent = this.parent(level++)) {
4309 if (!(parent instanceof AST_PropAccess)) break;
4310 if (parent.expression !== child) break;
4311 child = parent;
4312 }
4313 if (is_lhs(child, parent)) {
4314 warn(node);
4315 return;
4316 }
4317 return def;
4318 }));
4319 });
4320 def(AST_Node, noop);
4321 def(AST_Dot, function(compressor, suffix) {
4322 return this.expression._find_defs(compressor, "." + this.property + suffix);
4323 });
4324 def(AST_SymbolDeclaration, function(compressor) {
4325 if (!this.definition().global) return;
4326 if (HOP(compressor.option("global_defs"), this.name)) warn(this);
4327 });
4328 def(AST_SymbolRef, function(compressor, suffix) {
4329 if (!this.definition().global) return;
4330 var defines = compressor.option("global_defs");
4331 var name = this.name + suffix;
4332 if (HOP(defines, name)) return to_node(defines[name], this);
4333 });
4334 })(function(node, func) {
4335 node.DEFMETHOD("_find_defs", func);
4336 });
4337
4338 function best_of_expression(ast1, ast2, threshold) {
4339 var delta = ast2.print_to_string().length - ast1.print_to_string().length;
4340 return delta < (threshold || 0) ? ast2 : ast1;
4341 }
4342
4343 function best_of_statement(ast1, ast2, threshold) {
4344 return best_of_expression(make_node(AST_SimpleStatement, ast1, {
4345 body: ast1
4346 }), make_node(AST_SimpleStatement, ast2, {
4347 body: ast2
4348 }), threshold).body;
4349 }
4350
4351 function best_of(compressor, ast1, ast2, threshold) {
4352 return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2, threshold);
4353 }
4354
4355 function convert_to_predicate(obj) {
4356 var map = Object.create(null);
4357 Object.keys(obj).forEach(function(key) {
4358 map[key] = makePredicate(obj[key]);
4359 });
4360 return map;
4361 }
4362
4363 function skip_directives(body) {
4364 for (var i = 0; i < body.length; i++) {
4365 var stat = body[i];
4366 if (!(stat instanceof AST_Directive)) return stat;
4367 }
4368 }
4369
4370 function arrow_first_statement() {
4371 if (this.value) return make_node(AST_Return, this.value, {
4372 value: this.value
4373 });
4374 return skip_directives(this.body);
4375 }
4376 AST_Arrow.DEFMETHOD("first_statement", arrow_first_statement);
4377 AST_AsyncArrow.DEFMETHOD("first_statement", arrow_first_statement);
4378 AST_Lambda.DEFMETHOD("first_statement", function() {
4379 return skip_directives(this.body);
4380 });
4381
4382 AST_Lambda.DEFMETHOD("length", function() {
4383 var argnames = this.argnames;
4384 for (var i = 0; i < argnames.length; i++) {
4385 if (argnames[i] instanceof AST_DefaultValue) break;
4386 }
4387 return i;
4388 });
4389
4390 function try_evaluate(compressor, node) {
4391 var ev = node.evaluate(compressor);
4392 if (ev === node) return node;
4393 ev = make_node_from_constant(ev, node).optimize(compressor);
4394 return best_of(compressor, node, ev, compressor.eval_threshold);
4395 }
4396
4397 var object_fns = [
4398 "constructor",
4399 "toString",
4400 "valueOf",
4401 ];
4402 var native_fns = convert_to_predicate({
4403 Array: [
4404 "indexOf",
4405 "join",
4406 "lastIndexOf",
4407 "slice",
4408 ].concat(object_fns),
4409 Boolean: object_fns,
4410 Function: object_fns,
4411 Number: [
4412 "toExponential",
4413 "toFixed",
4414 "toPrecision",
4415 ].concat(object_fns),
4416 Object: object_fns,
4417 RegExp: [
4418 "exec",
4419 "test",
4420 ].concat(object_fns),
4421 String: [
4422 "charAt",
4423 "charCodeAt",
4424 "concat",
4425 "indexOf",
4426 "italics",
4427 "lastIndexOf",
4428 "match",
4429 "replace",
4430 "search",
4431 "slice",
4432 "split",
4433 "substr",
4434 "substring",
4435 "toLowerCase",
4436 "toUpperCase",
4437 "trim",
4438 ].concat(object_fns),
4439 });
4440 var static_fns = convert_to_predicate({
4441 Array: [
4442 "isArray",
4443 ],
4444 Math: [
4445 "abs",
4446 "acos",
4447 "asin",
4448 "atan",
4449 "ceil",
4450 "cos",
4451 "exp",
4452 "floor",
4453 "log",
4454 "round",
4455 "sin",
4456 "sqrt",
4457 "tan",
4458 "atan2",
4459 "pow",
4460 "max",
4461 "min",
4462 ],
4463 Number: [
4464 "isFinite",
4465 "isNaN",
4466 ],
4467 Object: [
4468 "create",
4469 "getOwnPropertyDescriptor",
4470 "getOwnPropertyNames",
4471 "getPrototypeOf",
4472 "isExtensible",
4473 "isFrozen",
4474 "isSealed",
4475 "keys",
4476 ],
4477 String: [
4478 "fromCharCode",
4479 "raw",
4480 ],
4481 });
4482
4483 function is_static_fn(node) {
4484 if (!(node instanceof AST_Dot)) return false;
4485 var expr = node.expression;
4486 if (!is_undeclared_ref(expr)) return false;
4487 var static_fn = static_fns[expr.name];
4488 return static_fn && (static_fn[node.property] || expr.name == "Math" && node.property == "random");
4489 }
4490
4491 // Accomodate when compress option evaluate=false
4492 // as well as the common constant expressions !0 and -1
4493 (function(def) {
4494 def(AST_Node, return_false);
4495 def(AST_Constant, return_true);
4496 def(AST_RegExp, return_false);
4497 var unaryPrefix = makePredicate("! ~ - + void");
4498 def(AST_UnaryPrefix, function() {
4499 return unaryPrefix[this.operator] && this.expression instanceof AST_Constant;
4500 });
4501 })(function(node, func) {
4502 node.DEFMETHOD("is_constant", func);
4503 });
4504
4505 // methods to evaluate a constant expression
4506 (function(def) {
4507 // If the node has been successfully reduced to a constant,
4508 // then its value is returned; otherwise the element itself
4509 // is returned.
4510 //
4511 // They can be distinguished as constant value is never a
4512 // descendant of AST_Node.
4513 //
4514 // When `ignore_side_effects` is `true`, inspect the constant value
4515 // produced without worrying about any side effects caused by said
4516 // expression.
4517 AST_Node.DEFMETHOD("evaluate", function(compressor, ignore_side_effects) {
4518 if (!compressor.option("evaluate")) return this;
4519 var cached = [];
4520 var val = this._eval(compressor, ignore_side_effects, cached, 1);
4521 cached.forEach(function(node) {
4522 delete node._eval;
4523 });
4524 if (ignore_side_effects) return val;
4525 if (!val || val instanceof RegExp) return val;
4526 if (typeof val == "function" || typeof val == "object") return this;
4527 return val;
4528 });
4529 var scan_modified = new TreeWalker(function(node) {
4530 if (node instanceof AST_Assign) modified(node.left);
4531 if (node instanceof AST_Unary && UNARY_POSTFIX[node.operator]) modified(node.expression);
4532 });
4533 function modified(node) {
4534 if (node instanceof AST_DestructuredArray) {
4535 node.elements.forEach(modified);
4536 } else if (node instanceof AST_DestructuredObject) {
4537 node.properties.forEach(function(prop) {
4538 modified(prop.value);
4539 });
4540 } else if (node instanceof AST_PropAccess) {
4541 modified(node.expression);
4542 } else if (node instanceof AST_SymbolRef) {
4543 node.definition().references.forEach(function(ref) {
4544 delete ref._eval;
4545 });
4546 }
4547 }
4548 def(AST_Statement, function() {
4549 throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
4550 });
4551 def(AST_Accessor, return_this);
4552 def(AST_BigInt, return_this);
4553 def(AST_Class, return_this);
4554 def(AST_Node, return_this);
4555 def(AST_Constant, function() {
4556 return this.value;
4557 });
4558 def(AST_Assign, function(compressor, ignore_side_effects, cached, depth) {
4559 var lhs = this.left;
4560 if (!ignore_side_effects) {
4561 if (!(lhs instanceof AST_SymbolRef)) return this;
4562 if (!HOP(lhs, "_eval")) {
4563 if (!lhs.fixed) return this;
4564 var def = lhs.definition();
4565 if (!def.fixed) return this;
4566 if (def.undeclared) return this;
4567 if (def.last_ref !== lhs) return this;
4568 if (def.single_use == "m") return this;
4569 }
4570 }
4571 var op = this.operator;
4572 var node;
4573 if (!HOP(lhs, "_eval") && lhs instanceof AST_SymbolRef && lhs.fixed && lhs.definition().fixed) {
4574 node = lhs;
4575 } else if (op == "=") {
4576 node = this.right;
4577 } else {
4578 node = make_node(AST_Binary, this, {
4579 operator: op.slice(0, -1),
4580 left: lhs,
4581 right: this.right,
4582 });
4583 }
4584 lhs.walk(scan_modified);
4585 var value = node._eval(compressor, ignore_side_effects, cached, depth);
4586 if (typeof value == "object") return this;
4587 modified(lhs);
4588 return value;
4589 });
4590 def(AST_Sequence, function(compressor, ignore_side_effects, cached, depth) {
4591 if (!ignore_side_effects) return this;
4592 var exprs = this.expressions;
4593 for (var i = 0, last = exprs.length - 1; i < last; i++) {
4594 exprs[i].walk(scan_modified);
4595 }
4596 var tail = exprs[last];
4597 var value = tail._eval(compressor, ignore_side_effects, cached, depth);
4598 return value === tail ? this : value;
4599 });
4600 def(AST_Lambda, function(compressor) {
4601 if (compressor.option("unsafe")) {
4602 var fn = function() {};
4603 fn.node = this;
4604 fn.toString = function() {
4605 return "function(){}";
4606 };
4607 return fn;
4608 }
4609 return this;
4610 });
4611 def(AST_Array, function(compressor, ignore_side_effects, cached, depth) {
4612 if (compressor.option("unsafe")) {
4613 var elements = [];
4614 for (var i = 0; i < this.elements.length; i++) {
4615 var element = this.elements[i];
4616 if (element instanceof AST_Hole) return this;
4617 var value = element._eval(compressor, ignore_side_effects, cached, depth);
4618 if (element === value) return this;
4619 elements.push(value);
4620 }
4621 return elements;
4622 }
4623 return this;
4624 });
4625 def(AST_Object, function(compressor, ignore_side_effects, cached, depth) {
4626 if (compressor.option("unsafe")) {
4627 var val = {};
4628 for (var i = 0; i < this.properties.length; i++) {
4629 var prop = this.properties[i];
4630 if (!(prop instanceof AST_ObjectKeyVal)) return this;
4631 var key = prop.key;
4632 if (key instanceof AST_Node) {
4633 key = key._eval(compressor, ignore_side_effects, cached, depth);
4634 if (key === prop.key) return this;
4635 }
4636 switch (key) {
4637 case "__proto__":
4638 case "toString":
4639 case "valueOf":
4640 return this;
4641 }
4642 val[key] = prop.value._eval(compressor, ignore_side_effects, cached, depth);
4643 if (val[key] === prop.value) return this;
4644 }
4645 return val;
4646 }
4647 return this;
4648 });
4649 var non_converting_unary = makePredicate("! typeof void");
4650 def(AST_UnaryPrefix, function(compressor, ignore_side_effects, cached, depth) {
4651 var e = this.expression;
4652 var op = this.operator;
4653 // Function would be evaluated to an array and so typeof would
4654 // incorrectly return "object". Hence making is a special case.
4655 if (compressor.option("typeofs")
4656 && op == "typeof"
4657 && (e instanceof AST_Lambda
4658 || e instanceof AST_SymbolRef
4659 && e.fixed_value() instanceof AST_Lambda)) {
4660 return typeof function(){};
4661 }
4662 var def = e instanceof AST_SymbolRef && e.definition();
4663 if (!non_converting_unary[op] && !(def && def.fixed)) depth++;
4664 e.walk(scan_modified);
4665 var v = e._eval(compressor, ignore_side_effects, cached, depth);
4666 if (v === e) {
4667 if (ignore_side_effects && op == "void") return;
4668 return this;
4669 }
4670 switch (op) {
4671 case "!": return !v;
4672 case "typeof":
4673 // typeof <RegExp> returns "object" or "function" on different platforms
4674 // so cannot evaluate reliably
4675 if (v instanceof RegExp) return this;
4676 return typeof v;
4677 case "void": return;
4678 case "~": return ~v;
4679 case "-": return -v;
4680 case "+": return +v;
4681 case "++":
4682 case "--":
4683 if (!def) return this;
4684 if (!ignore_side_effects) {
4685 if (def.undeclared) return this;
4686 if (def.last_ref !== e) return this;
4687 }
4688 if (HOP(e, "_eval")) v = +(op[0] + 1) + +v;
4689 modified(e);
4690 return v;
4691 }
4692 return this;
4693 });
4694 def(AST_UnaryPostfix, function(compressor, ignore_side_effects, cached, depth) {
4695 var e = this.expression;
4696 if (!(e instanceof AST_SymbolRef)) {
4697 if (!ignore_side_effects) return this;
4698 } else if (!HOP(e, "_eval")) {
4699 if (!e.fixed) return this;
4700 if (!ignore_side_effects) {
4701 var def = e.definition();
4702 if (!def.fixed) return this;
4703 if (def.undeclared) return this;
4704 if (def.last_ref !== e) return this;
4705 }
4706 }
4707 if (!(e instanceof AST_SymbolRef && e.definition().fixed)) depth++;
4708 e.walk(scan_modified);
4709 var v = e._eval(compressor, ignore_side_effects, cached, depth);
4710 if (v === e) return this;
4711 modified(e);
4712 return +v;
4713 });
4714 var non_converting_binary = makePredicate("&& || === !==");
4715 def(AST_Binary, function(compressor, ignore_side_effects, cached, depth) {
4716 if (!non_converting_binary[this.operator]) depth++;
4717 var left = this.left._eval(compressor, ignore_side_effects, cached, depth);
4718 if (left === this.left) return this;
4719 if (this.operator == (left ? "||" : "&&")) return left;
4720 var rhs_ignore_side_effects = ignore_side_effects && !(left && typeof left == "object");
4721 var right = this.right._eval(compressor, rhs_ignore_side_effects, cached, depth);
4722 if (right === this.right) return this;
4723 var result;
4724 switch (this.operator) {
4725 case "&&" : result = left && right; break;
4726 case "||" : result = left || right; break;
4727 case "??" :
4728 result = left == null ? right : left;
4729 break;
4730 case "|" : result = left | right; break;
4731 case "&" : result = left & right; break;
4732 case "^" : result = left ^ right; break;
4733 case "+" : result = left + right; break;
4734 case "-" : result = left - right; break;
4735 case "*" : result = left * right; break;
4736 case "/" : result = left / right; break;
4737 case "%" : result = left % right; break;
4738 case "<<" : result = left << right; break;
4739 case ">>" : result = left >> right; break;
4740 case ">>>": result = left >>> right; break;
4741 case "==" : result = left == right; break;
4742 case "===": result = left === right; break;
4743 case "!=" : result = left != right; break;
4744 case "!==": result = left !== right; break;
4745 case "<" : result = left < right; break;
4746 case "<=" : result = left <= right; break;
4747 case ">" : result = left > right; break;
4748 case ">=" : result = left >= right; break;
4749 case "**":
4750 result = Math.pow(left, right);
4751 break;
4752 case "in":
4753 if (right && typeof right == "object" && HOP(right, left)) {
4754 result = true;
4755 break;
4756 }
4757 default:
4758 return this;
4759 }
4760 if (isNaN(result)) return compressor.find_parent(AST_With) ? this : result;
4761 if (compressor.option("unsafe_math")
4762 && !ignore_side_effects
4763 && result
4764 && typeof result == "number"
4765 && (this.operator == "+" || this.operator == "-")) {
4766 var digits = Math.max(0, decimals(left), decimals(right));
4767 // 53-bit significand ---> 15.95 decimal places
4768 if (digits < 16) return +result.toFixed(digits);
4769 }
4770 return result;
4771
4772 function decimals(operand) {
4773 var match = /(\.[0-9]*)?(e.+)?$/.exec(+operand);
4774 return (match[1] || ".").length - 1 - (match[2] || "").slice(1);
4775 }
4776 });
4777 def(AST_Conditional, function(compressor, ignore_side_effects, cached, depth) {
4778 var condition = this.condition._eval(compressor, ignore_side_effects, cached, depth);
4779 if (condition === this.condition) return this;
4780 var node = condition ? this.consequent : this.alternative;
4781 var value = node._eval(compressor, ignore_side_effects, cached, depth);
4782 return value === node ? this : value;
4783 });
4784 function verify_escaped(ref, depth) {
4785 var escaped = ref.definition().escaped;
4786 switch (escaped.length) {
4787 case 0:
4788 return true;
4789 case 1:
4790 var found = false;
4791 escaped[0].walk(new TreeWalker(function(node) {
4792 if (found) return true;
4793 if (node === ref) return found = true;
4794 if (node instanceof AST_Scope) return true;
4795 }));
4796 return found;
4797 default:
4798 return depth <= escaped.depth;
4799 }
4800 }
4801 def(AST_SymbolRef, function(compressor, ignore_side_effects, cached, depth) {
4802 var fixed = this.fixed_value();
4803 if (!fixed) return this;
4804 var value;
4805 if (HOP(fixed, "_eval")) {
4806 value = fixed._eval();
4807 } else {
4808 this._eval = return_this;
4809 value = fixed._eval(compressor, ignore_side_effects, cached, depth);
4810 delete this._eval;
4811 if (value === fixed) return this;
4812 fixed._eval = function() {
4813 return value;
4814 };
4815 cached.push(fixed);
4816 }
4817 return value && typeof value == "object" && !verify_escaped(this, depth) ? this : value;
4818 });
4819 var global_objs = {
4820 Array: Array,
4821 Math: Math,
4822 Number: Number,
4823 Object: Object,
4824 String: String,
4825 };
4826 var static_values = convert_to_predicate({
4827 Math: [
4828 "E",
4829 "LN10",
4830 "LN2",
4831 "LOG2E",
4832 "LOG10E",
4833 "PI",
4834 "SQRT1_2",
4835 "SQRT2",
4836 ],
4837 Number: [
4838 "MAX_VALUE",
4839 "MIN_VALUE",
4840 "NaN",
4841 "NEGATIVE_INFINITY",
4842 "POSITIVE_INFINITY",
4843 ],
4844 });
4845 var regexp_props = makePredicate("global ignoreCase multiline source");
4846 def(AST_PropAccess, function(compressor, ignore_side_effects, cached, depth) {
4847 if (compressor.option("unsafe")) {
4848 var val;
4849 var exp = this.expression;
4850 if (!is_undeclared_ref(exp)) {
4851 val = exp._eval(compressor, ignore_side_effects, cached, depth + 1);
4852 if (val == null || val === exp) return this;
4853 }
4854 var key = this.property;
4855 if (key instanceof AST_Node) {
4856 key = key._eval(compressor, ignore_side_effects, cached, depth);
4857 if (key === this.property) return this;
4858 }
4859 if (val === undefined) {
4860 var static_value = static_values[exp.name];
4861 if (!static_value || !static_value[key]) return this;
4862 val = global_objs[exp.name];
4863 } else if (val instanceof RegExp) {
4864 if (!regexp_props[key]) return this;
4865 } else if (typeof val == "object") {
4866 if (!HOP(val, key)) return this;
4867 } else if (typeof val == "function") switch (key) {
4868 case "name":
4869 return val.node.name ? val.node.name.name : "";
4870 case "length":
4871 return val.node.length();
4872 default:
4873 return this;
4874 }
4875 return val[key];
4876 }
4877 return this;
4878 });
4879 function eval_all(nodes, compressor, ignore_side_effects, cached, depth) {
4880 var values = [];
4881 for (var i = 0; i < nodes.length; i++) {
4882 var node = nodes[i];
4883 var value = node._eval(compressor, ignore_side_effects, cached, depth);
4884 if (node === value) return;
4885 values.push(value);
4886 }
4887 return values;
4888 }
4889 def(AST_Call, function(compressor, ignore_side_effects, cached, depth) {
4890 var exp = this.expression;
4891 var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
4892 if (fn instanceof AST_Arrow || fn instanceof AST_Defun || fn instanceof AST_Function) {
4893 if (fn.evaluating) return this;
4894 if (fn.name && fn.name.definition().recursive_refs > 0) return this;
4895 if (this.is_expr_pure(compressor)) return this;
4896 var args = eval_all(this.args, compressor, ignore_side_effects, cached, depth);
4897 if (!all(fn.argnames, function(sym, index) {
4898 if (sym instanceof AST_DefaultValue) {
4899 if (!args) return false;
4900 if (args[index] === undefined) {
4901 var value = sym.value._eval(compressor, ignore_side_effects, cached, depth);
4902 if (value === sym.value) return false;
4903 args[index] = value;
4904 }
4905 sym = sym.name;
4906 }
4907 return !(sym instanceof AST_Destructured);
4908 })) return this;
4909 if (fn.rest instanceof AST_Destructured) return this;
4910 if (!args && !ignore_side_effects) return this;
4911 var stat = fn.first_statement();
4912 if (!(stat instanceof AST_Return)) {
4913 if (ignore_side_effects) {
4914 fn.walk(scan_modified);
4915 var found = false;
4916 fn.evaluating = true;
4917 walk_body(fn, new TreeWalker(function(node) {
4918 if (found) return true;
4919 if (node instanceof AST_Return) {
4920 if (node.value && node.value._eval(compressor, true, cached, depth) !== undefined) {
4921 found = true;
4922 }
4923 return true;
4924 }
4925 if (node instanceof AST_Scope && node !== fn) return true;
4926 }));
4927 fn.evaluating = false;
4928 if (!found) return;
4929 }
4930 return this;
4931 }
4932 var val = stat.value;
4933 if (!val) return;
4934 var cached_args = [];
4935 if (!args || all(fn.argnames, function(sym, i) {
4936 return assign(sym, args[i]);
4937 }) && !(fn.rest && !assign(fn.rest, args.slice(fn.argnames.length))) || ignore_side_effects) {
4938 if (ignore_side_effects) fn.argnames.forEach(function(sym) {
4939 if (sym instanceof AST_DefaultValue) sym.value.walk(scan_modified);
4940 });
4941 fn.evaluating = true;
4942 val = val._eval(compressor, ignore_side_effects, cached, depth);
4943 fn.evaluating = false;
4944 }
4945 cached_args.forEach(function(node) {
4946 delete node._eval;
4947 });
4948 return val === stat.value ? this : val;
4949 } else if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
4950 var key = exp.property;
4951 if (key instanceof AST_Node) {
4952 key = key._eval(compressor, ignore_side_effects, cached, depth);
4953 if (key === exp.property) return this;
4954 }
4955 var val;
4956 var e = exp.expression;
4957 if (is_undeclared_ref(e)) {
4958 var static_fn = static_fns[e.name];
4959 if (!static_fn || !static_fn[key]) return this;
4960 val = global_objs[e.name];
4961 } else {
4962 val = e._eval(compressor, ignore_side_effects, cached, depth + 1);
4963 if (val == null || val === e) return this;
4964 var native_fn = native_fns[val.constructor.name];
4965 if (!native_fn || !native_fn[key]) return this;
4966 if (val instanceof RegExp && val.global && !(e instanceof AST_RegExp)) return this;
4967 }
4968 var args = eval_all(this.args, compressor, ignore_side_effects, cached, depth);
4969 if (!args) return this;
4970 if (key == "replace" && typeof args[1] == "function") return this;
4971 try {
4972 return val[key].apply(val, args);
4973 } catch (ex) {
4974 AST_Node.warn("Error evaluating {code} [{file}:{line},{col}]", {
4975 code: this,
4976 file: this.start.file,
4977 line: this.start.line,
4978 col: this.start.col,
4979 });
4980 } finally {
4981 if (val instanceof RegExp) val.lastIndex = 0;
4982 }
4983 }
4984 return this;
4985
4986 function assign(sym, arg) {
4987 if (sym instanceof AST_DefaultValue) sym = sym.name;
4988 var def = sym.definition();
4989 if (def.orig[def.orig.length - 1] !== sym) return false;
4990 var value = arg;
4991 def.references.forEach(function(node) {
4992 node._eval = function() {
4993 return value;
4994 };
4995 cached_args.push(node);
4996 });
4997 return true;
4998 }
4999 });
5000 def(AST_New, return_this);
5001 def(AST_Template, function(compressor, ignore_side_effects, cached, depth) {
5002 if (!compressor.option("templates")) return this;
5003 if (this.tag) {
5004 if (!is_raw_tag(compressor, this.tag)) return this;
5005 decode = function(str) {
5006 return str;
5007 };
5008 }
5009 var exprs = eval_all(this.expressions, compressor, ignore_side_effects, cached, depth);
5010 if (!exprs) return this;
5011 var malformed = false;
5012 var ret = decode(this.strings[0]);
5013 for (var i = 0; i < exprs.length; i++) {
5014 ret += exprs[i] + decode(this.strings[i + 1]);
5015 }
5016 if (!malformed) return ret;
5017 this._eval = return_this;
5018 return this;
5019
5020 function decode(str) {
5021 str = decode_template(str);
5022 if (typeof str != "string") malformed = true;
5023 return str;
5024 }
5025 });
5026 })(function(node, func) {
5027 node.DEFMETHOD("_eval", func);
5028 });
5029
5030 // method to negate an expression
5031 (function(def) {
5032 function basic_negation(exp) {
5033 return make_node(AST_UnaryPrefix, exp, {
5034 operator: "!",
5035 expression: exp
5036 });
5037 }
5038 function best(orig, alt, first_in_statement) {
5039 var negated = basic_negation(orig);
5040 if (first_in_statement) {
5041 var stat = make_node(AST_SimpleStatement, alt, {
5042 body: alt
5043 });
5044 return best_of_expression(negated, stat) === stat ? alt : negated;
5045 }
5046 return best_of_expression(negated, alt);
5047 }
5048 def(AST_Node, function() {
5049 return basic_negation(this);
5050 });
5051 def(AST_Statement, function() {
5052 throw new Error("Cannot negate a statement");
5053 });
5054 def(AST_Binary, function(compressor, first_in_statement) {
5055 var self = this.clone(), op = this.operator;
5056 if (compressor.option("unsafe_comps")) {
5057 switch (op) {
5058 case "<=" : self.operator = ">" ; return self;
5059 case "<" : self.operator = ">=" ; return self;
5060 case ">=" : self.operator = "<" ; return self;
5061 case ">" : self.operator = "<=" ; return self;
5062 }
5063 }
5064 switch (op) {
5065 case "==" : self.operator = "!="; return self;
5066 case "!=" : self.operator = "=="; return self;
5067 case "===": self.operator = "!=="; return self;
5068 case "!==": self.operator = "==="; return self;
5069 case "&&":
5070 self.operator = "||";
5071 self.left = self.left.negate(compressor, first_in_statement);
5072 self.right = self.right.negate(compressor);
5073 return best(this, self, first_in_statement);
5074 case "||":
5075 self.operator = "&&";
5076 self.left = self.left.negate(compressor, first_in_statement);
5077 self.right = self.right.negate(compressor);
5078 return best(this, self, first_in_statement);
5079 }
5080 return basic_negation(this);
5081 });
5082 def(AST_ClassExpression, function() {
5083 return basic_negation(this);
5084 });
5085 def(AST_Conditional, function(compressor, first_in_statement) {
5086 var self = this.clone();
5087 self.consequent = self.consequent.negate(compressor);
5088 self.alternative = self.alternative.negate(compressor);
5089 return best(this, self, first_in_statement);
5090 });
5091 def(AST_LambdaExpression, function() {
5092 return basic_negation(this);
5093 });
5094 def(AST_Sequence, function(compressor) {
5095 var expressions = this.expressions.slice();
5096 expressions.push(expressions.pop().negate(compressor));
5097 return make_sequence(this, expressions);
5098 });
5099 def(AST_UnaryPrefix, function() {
5100 if (this.operator == "!")
5101 return this.expression;
5102 return basic_negation(this);
5103 });
5104 })(function(node, func) {
5105 node.DEFMETHOD("negate", function(compressor, first_in_statement) {
5106 return func.call(this, compressor, first_in_statement);
5107 });
5108 });
5109
5110 var global_pure_fns = makePredicate("Boolean decodeURI decodeURIComponent Date encodeURI encodeURIComponent Error escape EvalError isFinite isNaN Number Object parseFloat parseInt RangeError ReferenceError String SyntaxError TypeError unescape URIError");
5111 var global_pure_constructors = makePredicate("Map Set WeakMap WeakSet");
5112 AST_Call.DEFMETHOD("is_expr_pure", function(compressor) {
5113 if (compressor.option("unsafe")) {
5114 var expr = this.expression;
5115 if (is_undeclared_ref(expr)) {
5116 if (global_pure_fns[expr.name]) return true;
5117 if (this instanceof AST_New && global_pure_constructors[expr.name]) return true;
5118 }
5119 if (is_static_fn(expr)) return true;
5120 }
5121 return compressor.option("annotations") && this.pure || !compressor.pure_funcs(this);
5122 });
5123 AST_Template.DEFMETHOD("is_expr_pure", function(compressor) {
5124 var tag = this.tag;
5125 if (!tag) return true;
5126 if (compressor.option("unsafe")) {
5127 if (is_undeclared_ref(tag) && global_pure_fns[tag.name]) return true;
5128 if (tag instanceof AST_Dot && is_undeclared_ref(tag.expression)) {
5129 var static_fn = static_fns[tag.expression.name];
5130 return static_fn && (static_fn[tag.property]
5131 || tag.expression.name == "Math" && tag.property == "random");
5132 }
5133 }
5134 return !compressor.pure_funcs(this);
5135 });
5136 AST_Node.DEFMETHOD("is_call_pure", return_false);
5137 AST_Call.DEFMETHOD("is_call_pure", function(compressor) {
5138 if (!compressor.option("unsafe")) return false;
5139 var dot = this.expression;
5140 if (!(dot instanceof AST_Dot)) return false;
5141 var exp = dot.expression;
5142 var map;
5143 var prop = dot.property;
5144 if (exp instanceof AST_Array) {
5145 map = native_fns.Array;
5146 } else if (exp.is_boolean(compressor)) {
5147 map = native_fns.Boolean;
5148 } else if (exp.is_number(compressor)) {
5149 map = native_fns.Number;
5150 } else if (exp instanceof AST_RegExp) {
5151 map = native_fns.RegExp;
5152 } else if (exp.is_string(compressor)) {
5153 map = native_fns.String;
5154 if (prop == "replace") {
5155 var arg = this.args[1];
5156 if (arg && !arg.is_string(compressor)) return false;
5157 }
5158 } else if (!dot.may_throw_on_access(compressor)) {
5159 map = native_fns.Object;
5160 }
5161 return map && map[prop];
5162 });
5163
5164 function spread_side_effects(exp) {
5165 while ((exp = exp.tail_node()) instanceof AST_SymbolRef) {
5166 exp = exp.fixed_value();
5167 if (!exp) return true;
5168 }
5169 return !(exp instanceof AST_Array
5170 || exp.TYPE == "Binary" && !lazy_op[exp.operator]
5171 || exp instanceof AST_Constant
5172 || exp instanceof AST_Lambda
5173 || exp instanceof AST_Object && all(exp.properties, function(prop) {
5174 return !(prop instanceof AST_ObjectGetter || prop instanceof AST_Spread);
5175 })
5176 || exp instanceof AST_ObjectIdentity
5177 || exp instanceof AST_Unary);
5178 }
5179
5180 // determine if expression has side effects
5181 (function(def) {
5182 function any(list, compressor, spread) {
5183 return !all(list, spread ? function(node) {
5184 return node instanceof AST_Spread ? !spread(node, compressor) : !node.has_side_effects(compressor);
5185 } : function(node) {
5186 return !node.has_side_effects(compressor);
5187 });
5188 }
5189 function array_spread(node, compressor) {
5190 return !node.expression.is_string(compressor) || node.expression.has_side_effects(compressor);
5191 }
5192 def(AST_Node, return_true);
5193 def(AST_Array, function(compressor) {
5194 return any(this.elements, compressor, array_spread);
5195 });
5196 def(AST_Assign, function(compressor) {
5197 var lhs = this.left;
5198 if (!(lhs instanceof AST_PropAccess)) return true;
5199 var node = lhs.expression;
5200 return !(node instanceof AST_ObjectIdentity)
5201 || !node.scope.resolve().new
5202 || lhs instanceof AST_Sub && lhs.property.has_side_effects(compressor)
5203 || this.right.has_side_effects(compressor);
5204 });
5205 def(AST_Binary, function(compressor) {
5206 return this.left.has_side_effects(compressor)
5207 || this.right.has_side_effects(compressor)
5208 || this.operator == "in" && !is_object(this.right);
5209 });
5210 def(AST_Block, function(compressor) {
5211 return any(this.body, compressor);
5212 });
5213 def(AST_Call, function(compressor) {
5214 if (!this.is_expr_pure(compressor)
5215 && (!this.is_call_pure(compressor) || this.expression.has_side_effects(compressor))) {
5216 return true;
5217 }
5218 return any(this.args, compressor, array_spread);
5219 });
5220 def(AST_Case, function(compressor) {
5221 return this.expression.has_side_effects(compressor)
5222 || any(this.body, compressor);
5223 });
5224 def(AST_Class, function(compressor) {
5225 var base = this.extends;
5226 if (base) {
5227 if (base instanceof AST_SymbolRef) base = base.fixed_value();
5228 if (!safe_for_extends(base)) return true;
5229 }
5230 return any(this.properties, compressor);
5231 });
5232 def(AST_ClassProperty, function(compressor) {
5233 return this.key instanceof AST_Node && this.key.has_side_effects(compressor)
5234 || this.static && this.value && this.value.has_side_effects(compressor);
5235 });
5236 def(AST_Conditional, function(compressor) {
5237 return this.condition.has_side_effects(compressor)
5238 || this.consequent.has_side_effects(compressor)
5239 || this.alternative.has_side_effects(compressor);
5240 });
5241 def(AST_Constant, return_false);
5242 def(AST_Definitions, function(compressor) {
5243 return any(this.definitions, compressor);
5244 });
5245 def(AST_DestructuredArray, function(compressor) {
5246 return any(this.elements, compressor);
5247 });
5248 def(AST_DestructuredKeyVal, function(compressor) {
5249 return this.key instanceof AST_Node && this.key.has_side_effects(compressor)
5250 || this.value.has_side_effects(compressor);
5251 });
5252 def(AST_DestructuredObject, function(compressor) {
5253 return any(this.properties, compressor);
5254 });
5255 def(AST_Dot, function(compressor) {
5256 return !this.optional && this.expression.may_throw_on_access(compressor)
5257 || this.expression.has_side_effects(compressor);
5258 });
5259 def(AST_EmptyStatement, return_false);
5260 def(AST_If, function(compressor) {
5261 return this.condition.has_side_effects(compressor)
5262 || this.body && this.body.has_side_effects(compressor)
5263 || this.alternative && this.alternative.has_side_effects(compressor);
5264 });
5265 def(AST_LabeledStatement, function(compressor) {
5266 return this.body.has_side_effects(compressor);
5267 });
5268 def(AST_Lambda, return_false);
5269 def(AST_Object, function(compressor) {
5270 return any(this.properties, compressor, function(node, compressor) {
5271 var exp = node.expression;
5272 return spread_side_effects(exp) || exp.has_side_effects(compressor);
5273 });
5274 });
5275 def(AST_ObjectIdentity, return_false);
5276 def(AST_ObjectProperty, function(compressor) {
5277 return this.key instanceof AST_Node && this.key.has_side_effects(compressor)
5278 || this.value.has_side_effects(compressor);
5279 });
5280 def(AST_Sequence, function(compressor) {
5281 return any(this.expressions, compressor);
5282 });
5283 def(AST_SimpleStatement, function(compressor) {
5284 return this.body.has_side_effects(compressor);
5285 });
5286 def(AST_Sub, function(compressor) {
5287 return !this.optional && this.expression.may_throw_on_access(compressor)
5288 || this.expression.has_side_effects(compressor)
5289 || this.property.has_side_effects(compressor);
5290 });
5291 def(AST_Switch, function(compressor) {
5292 return this.expression.has_side_effects(compressor)
5293 || any(this.body, compressor);
5294 });
5295 def(AST_SymbolDeclaration, return_false);
5296 def(AST_SymbolRef, function(compressor) {
5297 return !this.is_declared(compressor) || !can_drop_symbol(this, compressor);
5298 });
5299 def(AST_Template, function(compressor) {
5300 return !this.is_expr_pure(compressor) || any(this.expressions, compressor);
5301 });
5302 def(AST_Try, function(compressor) {
5303 return any(this.body, compressor)
5304 || this.bcatch && this.bcatch.has_side_effects(compressor)
5305 || this.bfinally && this.bfinally.has_side_effects(compressor);
5306 });
5307 def(AST_Unary, function(compressor) {
5308 return unary_side_effects[this.operator]
5309 || this.expression.has_side_effects(compressor);
5310 });
5311 def(AST_VarDef, function() {
5312 return this.value;
5313 });
5314 })(function(node, func) {
5315 node.DEFMETHOD("has_side_effects", func);
5316 });
5317
5318 // determine if expression may throw
5319 (function(def) {
5320 def(AST_Node, return_true);
5321
5322 def(AST_Constant, return_false);
5323 def(AST_Destructured, return_true);
5324 def(AST_EmptyStatement, return_false);
5325 def(AST_Lambda, return_false);
5326 def(AST_ObjectIdentity, return_false);
5327 def(AST_SymbolDeclaration, return_false);
5328
5329 function any(list, compressor) {
5330 for (var i = list.length; --i >= 0;)
5331 if (list[i].may_throw(compressor))
5332 return true;
5333 return false;
5334 }
5335
5336 function call_may_throw(exp, compressor) {
5337 if (exp.may_throw(compressor)) return true;
5338 if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
5339 if (!(exp instanceof AST_Lambda)) return true;
5340 if (any(exp.argnames, compressor)) return true;
5341 if (any(exp.body, compressor)) return true;
5342 return is_arrow(exp) && exp.value && exp.value.may_throw(compressor);
5343 }
5344
5345 def(AST_Array, function(compressor) {
5346 return any(this.elements, compressor);
5347 });
5348 def(AST_Assign, function(compressor) {
5349 if (this.right.may_throw(compressor)) return true;
5350 if (!compressor.has_directive("use strict")
5351 && this.operator == "="
5352 && this.left instanceof AST_SymbolRef) {
5353 return false;
5354 }
5355 return this.left.may_throw(compressor);
5356 });
5357 def(AST_Binary, function(compressor) {
5358 return this.left.may_throw(compressor)
5359 || this.right.may_throw(compressor)
5360 || this.operator == "in" && !is_object(this.right);
5361 });
5362 def(AST_Block, function(compressor) {
5363 return any(this.body, compressor);
5364 });
5365 def(AST_Call, function(compressor) {
5366 if (any(this.args, compressor)) return true;
5367 if (this.is_expr_pure(compressor)) return false;
5368 this.may_throw = return_true;
5369 var ret = call_may_throw(this.expression, compressor);
5370 delete this.may_throw;
5371 return ret;
5372 });
5373 def(AST_Case, function(compressor) {
5374 return this.expression.may_throw(compressor)
5375 || any(this.body, compressor);
5376 });
5377 def(AST_Conditional, function(compressor) {
5378 return this.condition.may_throw(compressor)
5379 || this.consequent.may_throw(compressor)
5380 || this.alternative.may_throw(compressor);
5381 });
5382 def(AST_DefaultValue, function(compressor) {
5383 return this.name.may_throw(compressor)
5384 || this.value && this.value.may_throw(compressor);
5385 });
5386 def(AST_Definitions, function(compressor) {
5387 return any(this.definitions, compressor);
5388 });
5389 def(AST_Dot, function(compressor) {
5390 return !this.optional && this.expression.may_throw_on_access(compressor)
5391 || this.expression.may_throw(compressor);
5392 });
5393 def(AST_If, function(compressor) {
5394 return this.condition.may_throw(compressor)
5395 || this.body && this.body.may_throw(compressor)
5396 || this.alternative && this.alternative.may_throw(compressor);
5397 });
5398 def(AST_LabeledStatement, function(compressor) {
5399 return this.body.may_throw(compressor);
5400 });
5401 def(AST_Object, function(compressor) {
5402 return any(this.properties, compressor);
5403 });
5404 def(AST_ObjectProperty, function(compressor) {
5405 return this.value.may_throw(compressor)
5406 || this.key instanceof AST_Node && this.key.may_throw(compressor);
5407 });
5408 def(AST_Return, function(compressor) {
5409 return this.value && this.value.may_throw(compressor);
5410 });
5411 def(AST_Sequence, function(compressor) {
5412 return any(this.expressions, compressor);
5413 });
5414 def(AST_SimpleStatement, function(compressor) {
5415 return this.body.may_throw(compressor);
5416 });
5417 def(AST_Sub, function(compressor) {
5418 return !this.optional && this.expression.may_throw_on_access(compressor)
5419 || this.expression.may_throw(compressor)
5420 || this.property.may_throw(compressor);
5421 });
5422 def(AST_Switch, function(compressor) {
5423 return this.expression.may_throw(compressor)
5424 || any(this.body, compressor);
5425 });
5426 def(AST_SymbolRef, function(compressor) {
5427 return !this.is_declared(compressor) || !can_drop_symbol(this, compressor);
5428 });
5429 def(AST_Template, function(compressor) {
5430 if (any(this.expressions, compressor)) return true;
5431 if (this.is_expr_pure(compressor)) return false;
5432 if (!this.tag) return false;
5433 this.may_throw = return_true;
5434 var ret = call_may_throw(this.tag, compressor);
5435 delete this.may_throw;
5436 return ret;
5437 });
5438 def(AST_Try, function(compressor) {
5439 return (this.bcatch ? this.bcatch.may_throw(compressor) : any(this.body, compressor))
5440 || this.bfinally && this.bfinally.may_throw(compressor);
5441 });
5442 def(AST_Unary, function(compressor) {
5443 return this.expression.may_throw(compressor)
5444 && !(this.operator == "typeof" && this.expression instanceof AST_SymbolRef);
5445 });
5446 def(AST_VarDef, function(compressor) {
5447 return this.name.may_throw(compressor)
5448 || this.value && this.value.may_throw(compressor);
5449 });
5450 })(function(node, func) {
5451 node.DEFMETHOD("may_throw", func);
5452 });
5453
5454 // determine if expression is constant
5455 (function(def) {
5456 function all_constant(list, scope) {
5457 for (var i = list.length; --i >= 0;)
5458 if (!list[i].is_constant_expression(scope))
5459 return false;
5460 return true;
5461 }
5462 def(AST_Node, return_false);
5463 def(AST_Array, function(scope) {
5464 return all_constant(this.elements, scope);
5465 });
5466 def(AST_Binary, function(scope) {
5467 return this.left.is_constant_expression(scope)
5468 && this.right.is_constant_expression(scope)
5469 && (this.operator != "in" || is_object(this.right));
5470 });
5471 def(AST_Class, function(scope) {
5472 var base = this.extends;
5473 if (base && !safe_for_extends(base)) return false;
5474 return all_constant(this.properties, scope);
5475 });
5476 def(AST_ClassProperty, function(scope) {
5477 return typeof this.key == "string" && (!this.value || this.value.is_constant_expression(scope));
5478 });
5479 def(AST_Constant, return_true);
5480 def(AST_Lambda, function(scope) {
5481 var self = this;
5482 var result = true;
5483 var scopes = [];
5484 self.walk(new TreeWalker(function(node, descend) {
5485 if (!result) return true;
5486 if (node instanceof AST_BlockScope) {
5487 if (node === self) return;
5488 scopes.push(node);
5489 descend();
5490 scopes.pop();
5491 return true;
5492 }
5493 if (node instanceof AST_SymbolRef) {
5494 if (self.inlined || node.redef) {
5495 result = false;
5496 return true;
5497 }
5498 if (self.variables.has(node.name)) return true;
5499 var def = node.definition();
5500 if (member(def.scope, scopes)) return true;
5501 if (scope && !def.redefined()) {
5502 var scope_def = scope.find_variable(node.name);
5503 if (scope_def ? scope_def === def : def.undeclared) {
5504 result = "f";
5505 return true;
5506 }
5507 }
5508 result = false;
5509 return true;
5510 }
5511 if (node instanceof AST_ObjectIdentity) {
5512 if (is_arrow(self) && all(scopes, function(s) {
5513 return !(s instanceof AST_Scope) || is_arrow(s);
5514 })) result = false;
5515 return true;
5516 }
5517 }));
5518 return result;
5519 });
5520 def(AST_Object, function(scope) {
5521 return all_constant(this.properties, scope);
5522 });
5523 def(AST_ObjectProperty, function(scope) {
5524 return typeof this.key == "string" && this.value.is_constant_expression(scope);
5525 });
5526 def(AST_Unary, function(scope) {
5527 return this.expression.is_constant_expression(scope);
5528 });
5529 })(function(node, func) {
5530 node.DEFMETHOD("is_constant_expression", func);
5531 });
5532
5533 // tell me if a statement aborts
5534 function aborts(thing) {
5535 return thing && thing.aborts();
5536 }
5537 (function(def) {
5538 def(AST_Statement, return_null);
5539 def(AST_Jump, return_this);
5540 function block_aborts() {
5541 var n = this.body.length;
5542 return n > 0 && aborts(this.body[n - 1]);
5543 }
5544 def(AST_BlockStatement, block_aborts);
5545 def(AST_SwitchBranch, block_aborts);
5546 def(AST_If, function() {
5547 return this.alternative && aborts(this.body) && aborts(this.alternative) && this;
5548 });
5549 })(function(node, func) {
5550 node.DEFMETHOD("aborts", func);
5551 });
5552
5553 /* -----[ optimizers ]----- */
5554
5555 var directives = makePredicate(["use asm", "use strict"]);
5556 OPT(AST_Directive, function(self, compressor) {
5557 if (compressor.option("directives")
5558 && (!directives[self.value] || compressor.has_directive(self.value) !== self)) {
5559 return make_node(AST_EmptyStatement, self);
5560 }
5561 return self;
5562 });
5563
5564 OPT(AST_Debugger, function(self, compressor) {
5565 if (compressor.option("drop_debugger"))
5566 return make_node(AST_EmptyStatement, self);
5567 return self;
5568 });
5569
5570 OPT(AST_LabeledStatement, function(self, compressor) {
5571 if (compressor.option("dead_code")
5572 && self.body instanceof AST_Break
5573 && compressor.loopcontrol_target(self.body) === self.body) {
5574 return make_node(AST_EmptyStatement, self);
5575 }
5576 return compressor.option("unused") && self.label.references.length == 0 ? self.body : self;
5577 });
5578
5579 OPT(AST_LoopControl, function(self, compressor) {
5580 if (!compressor.option("dead_code")) return self;
5581 var label = self.label;
5582 if (label) {
5583 var lct = compressor.loopcontrol_target(self);
5584 self.label = null;
5585 if (compressor.loopcontrol_target(self) === lct) {
5586 remove(label.thedef.references, self);
5587 } else {
5588 self.label = label;
5589 }
5590 }
5591 return self;
5592 });
5593
5594 OPT(AST_Block, function(self, compressor) {
5595 self.body = tighten_body(self.body, compressor);
5596 return self;
5597 });
5598
5599 function trim_block(node, parent, in_list) {
5600 switch (node.body.length) {
5601 case 0:
5602 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
5603 case 1:
5604 var stat = node.body[0];
5605 if (!safe_to_trim(stat)) return node;
5606 if (parent instanceof AST_IterationStatement && stat instanceof AST_LambdaDefinition) return node;
5607 return stat;
5608 }
5609 return node;
5610 }
5611
5612 OPT(AST_BlockStatement, function(self, compressor) {
5613 self.body = tighten_body(self.body, compressor);
5614 return trim_block(self, compressor.parent());
5615 });
5616
5617 function drop_rest_farg(fn, compressor) {
5618 if (!compressor.option("rests")) return;
5619 if (fn.uses_arguments) return;
5620 if (!(fn.rest instanceof AST_DestructuredArray)) return;
5621 if (!compressor.drop_fargs(fn, compressor.parent())) return;
5622 fn.argnames = fn.argnames.concat(fn.rest.elements);
5623 fn.rest = fn.rest.rest;
5624 }
5625
5626 OPT(AST_Lambda, function(self, compressor) {
5627 drop_rest_farg(self, compressor);
5628 self.body = tighten_body(self.body, compressor);
5629 return self;
5630 });
5631
5632 function opt_arrow(self, compressor) {
5633 if (!compressor.option("arrows")) return self;
5634 drop_rest_farg(self, compressor);
5635 var body = tighten_body(self.value ? [ self.first_statement() ] : self.body, compressor);
5636 switch (body.length) {
5637 case 1:
5638 var stat = body[0];
5639 if (stat instanceof AST_Return) {
5640 self.body.length = 0;
5641 self.value = stat.value;
5642 break;
5643 }
5644 default:
5645 self.body = body;
5646 self.value = null;
5647 break;
5648 }
5649 return self;
5650 }
5651 OPT(AST_Arrow, opt_arrow);
5652 OPT(AST_AsyncArrow, opt_arrow);
5653
5654 OPT(AST_Function, function(self, compressor) {
5655 drop_rest_farg(self, compressor);
5656 self.body = tighten_body(self.body, compressor);
5657 var parent = compressor.parent();
5658 if (compressor.option("inline")) for (var i = 0; i < self.body.length; i++) {
5659 var stat = self.body[i];
5660 if (stat instanceof AST_Directive) continue;
5661 if (stat instanceof AST_Return) {
5662 if (i != self.body.length - 1) break;
5663 var call = stat.value;
5664 if (!call || call.TYPE != "Call") break;
5665 if (call.is_expr_pure(compressor)) break;
5666 var fn = call.expression;
5667 if (fn instanceof AST_SymbolRef) {
5668 if (self.name && self.name.definition() === fn.definition()) break;
5669 fn = fn.fixed_value();
5670 }
5671 if (!(fn instanceof AST_Defun || fn instanceof AST_Function)) break;
5672 if (fn.rest) break;
5673 if (fn.uses_arguments) break;
5674 if (fn === call.expression) {
5675 if (fn.parent_scope !== self) break;
5676 if (!all(fn.enclosed, function(def) {
5677 return def.scope !== self;
5678 })) break;
5679 }
5680 if (fn.name
5681 && (parent instanceof AST_ClassMethod || parent instanceof AST_ObjectMethod)
5682 && parent.value === compressor.self()) break;
5683 if (fn.contains_this()) break;
5684 var len = fn.argnames.length;
5685 if (len > 0 && compressor.option("inline") < 2) break;
5686 if (len > self.argnames.length) break;
5687 if (!all(self.argnames, function(argname) {
5688 return argname instanceof AST_SymbolFunarg;
5689 })) break;
5690 if (!all(call.args, function(arg) {
5691 return !(arg instanceof AST_Spread);
5692 })) break;
5693 for (var j = 0; j < len; j++) {
5694 var arg = call.args[j];
5695 if (!(arg instanceof AST_SymbolRef)) break;
5696 if (arg.definition() !== self.argnames[j].definition()) break;
5697 }
5698 if (j < len) break;
5699 for (; j < call.args.length; j++) {
5700 if (call.args[j].has_side_effects(compressor)) break;
5701 }
5702 if (j < call.args.length) break;
5703 if (len < self.argnames.length && !compressor.drop_fargs(self, parent)) {
5704 if (!compressor.drop_fargs(fn, call)) break;
5705 do {
5706 fn.argnames.push(fn.make_var(AST_SymbolFunarg, fn, "argument_" + len));
5707 } while (++len < self.argnames.length);
5708 }
5709 return call.expression;
5710 }
5711 break;
5712 }
5713 return self;
5714 });
5715
5716 var NO_MERGE = makePredicate("arguments await yield");
5717 AST_Scope.DEFMETHOD("merge_variables", function(compressor) {
5718 if (!compressor.option("merge_vars")) return;
5719 var in_try, root, segment = {}, self = this;
5720 var first = [], last = [], index = 0;
5721 var declarations = new Dictionary();
5722 var references = Object.create(null);
5723 var prev = Object.create(null);
5724 var tw = new TreeWalker(function(node, descend) {
5725 if (node instanceof AST_Assign) {
5726 var lhs = node.left;
5727 var rhs = node.right;
5728 if (lhs instanceof AST_Destructured) {
5729 rhs.walk(tw);
5730 var marker = new TreeWalker(function(node) {
5731 if (node instanceof AST_Destructured) return;
5732 if (node instanceof AST_DefaultValue) {
5733 push();
5734 node.value.walk(tw);
5735 pop();
5736 node.name.walk(marker);
5737 } else if (node instanceof AST_DestructuredKeyVal) {
5738 if (node.key instanceof AST_Node) {
5739 push();
5740 segment.block = node;
5741 node.key.walk(tw);
5742 node.value.walk(marker);
5743 pop();
5744 } else {
5745 node.value.walk(marker);
5746 }
5747 } else if (node instanceof AST_SymbolRef) {
5748 mark(node);
5749 } else {
5750 node.walk(tw);
5751 }
5752 return true;
5753 });
5754 lhs.walk(marker);
5755 return true;
5756 }
5757 if (lazy_op[node.operator.slice(0, -1)]) {
5758 lhs.walk(tw);
5759 push();
5760 rhs.walk(tw);
5761 if (lhs instanceof AST_SymbolRef) mark(lhs);
5762 pop();
5763 return true;
5764 }
5765 if (lhs instanceof AST_SymbolRef) {
5766 if (node.operator != "=") mark(lhs, true);
5767 rhs.walk(tw);
5768 mark(lhs);
5769 return true;
5770 }
5771 return;
5772 }
5773 if (node instanceof AST_Binary) {
5774 if (!lazy_op[node.operator]) return;
5775 node.left.walk(tw);
5776 push();
5777 node.right.walk(tw);
5778 pop();
5779 return true;
5780 }
5781 if (node instanceof AST_Break) {
5782 var target = tw.loopcontrol_target(node);
5783 if (!(target instanceof AST_IterationStatement)) insert(target);
5784 return true;
5785 }
5786 if (node instanceof AST_Call) {
5787 var exp = node.expression;
5788 var tail = exp.tail_node();
5789 if (!is_lambda(tail)) {
5790 descend();
5791 return mark_expression(exp);
5792 }
5793 if (exp !== tail) exp.expressions.slice(0, -1).forEach(function(node) {
5794 node.walk(tw);
5795 });
5796 node.args.forEach(function(arg) {
5797 arg.walk(tw);
5798 });
5799 tail.walk(tw);
5800 return true;
5801 }
5802 if (node instanceof AST_Conditional) {
5803 node.condition.walk(tw);
5804 push();
5805 node.consequent.walk(tw);
5806 pop();
5807 push();
5808 node.alternative.walk(tw);
5809 pop();
5810 return true;
5811 }
5812 if (node instanceof AST_Continue) {
5813 var target = tw.loopcontrol_target(node);
5814 if (target instanceof AST_Do) insert(target);
5815 return true;
5816 }
5817 if (node instanceof AST_Do) {
5818 push();
5819 segment.block = node;
5820 segment.loop = true;
5821 var save = segment;
5822 node.body.walk(tw);
5823 if (segment.inserted === node) segment = save;
5824 node.condition.walk(tw);
5825 pop();
5826 return true;
5827 }
5828 if (node instanceof AST_For) {
5829 if (node.init) node.init.walk(tw);
5830 push();
5831 segment.block = node;
5832 segment.loop = true;
5833 if (node.condition) node.condition.walk(tw);
5834 node.body.walk(tw);
5835 if (node.step) node.step.walk(tw);
5836 pop();
5837 return true;
5838 }
5839 if (node instanceof AST_ForEnumeration) {
5840 node.object.walk(tw);
5841 push();
5842 segment.block = node;
5843 segment.loop = true;
5844 node.init.walk(tw);
5845 node.body.walk(tw);
5846 pop();
5847 return true;
5848 }
5849 if (node instanceof AST_If) {
5850 node.condition.walk(tw);
5851 push();
5852 node.body.walk(tw);
5853 pop();
5854 if (node.alternative) {
5855 push();
5856 node.alternative.walk(tw);
5857 pop();
5858 }
5859 return true;
5860 }
5861 if (node instanceof AST_LabeledStatement) {
5862 push();
5863 segment.block = node;
5864 var save = segment;
5865 node.body.walk(tw);
5866 if (segment.inserted === node) segment = save;
5867 pop();
5868 return true;
5869 }
5870 if (node instanceof AST_Scope) {
5871 push();
5872 segment.block = node;
5873 if (node === self) root = segment;
5874 if (node instanceof AST_Lambda) {
5875 if (node.name) references[node.name.definition().id] = false;
5876 var marker = node.uses_arguments && !tw.has_directive("use strict") ? function(node) {
5877 if (node instanceof AST_SymbolFunarg) references[node.definition().id] = false;
5878 } : function(node) {
5879 if (node instanceof AST_SymbolFunarg) mark(node);
5880 };
5881 var scanner = new TreeWalker(function(ref) {
5882 if (ref instanceof AST_SymbolDeclaration) references[ref.definition().id] = false;
5883 if (!(ref instanceof AST_SymbolRef)) return;
5884 var def = ref.definition();
5885 var ldef = node.variables.get(ref.name);
5886 if (ldef && (ldef === def
5887 || def.undeclared
5888 || node.parent_scope.find_variable(ref.name) === def)) {
5889 references[def.id] = false;
5890 references[ldef.id] = false;
5891 } else {
5892 var save = segment;
5893 pop();
5894 mark(ref, true);
5895 segment = save;
5896 }
5897 return true;
5898 });
5899 node.argnames.forEach(function(argname) {
5900 argname.mark_symbol(marker, scanner);
5901 });
5902 if (node.rest) node.rest.mark_symbol(marker, scanner);
5903 }
5904 walk_lambda(node, tw);
5905 pop();
5906 return true;
5907 }
5908 if (node instanceof AST_Sub) {
5909 var exp = node.expression;
5910 if (node.optional) {
5911 exp.walk(tw);
5912 push();
5913 node.property.walk(tw);
5914 pop();
5915 } else {
5916 descend();
5917 }
5918 return mark_expression(exp);
5919 }
5920 if (node instanceof AST_Switch) {
5921 node.expression.walk(tw);
5922 var save = segment;
5923 node.body.forEach(function(branch) {
5924 if (branch instanceof AST_Default) return;
5925 branch.expression.walk(tw);
5926 if (save === segment) push();
5927 });
5928 segment = save;
5929 node.body.forEach(function(branch) {
5930 push();
5931 segment.block = node;
5932 var save = segment;
5933 walk_body(branch, tw);
5934 if (segment.inserted === node) segment = save;
5935 pop();
5936 });
5937 return true;
5938 }
5939 if (node instanceof AST_SymbolConst || node instanceof AST_SymbolLet) {
5940 references[node.definition().id] = false;
5941 return true;
5942 }
5943 if (node instanceof AST_SymbolRef) {
5944 mark(node, true);
5945 return true;
5946 }
5947 if (node instanceof AST_Try) {
5948 var save_try = in_try;
5949 in_try = node;
5950 var save = segment;
5951 walk_body(node, tw);
5952 segment = save;
5953 if (node.bcatch) {
5954 if (node.bcatch.argname) node.bcatch.argname.mark_symbol(function(node) {
5955 if (node instanceof AST_SymbolCatch) {
5956 var def = node.definition();
5957 references[def.id] = false;
5958 if (def = def.redefined()) references[def.id] = false;
5959 }
5960 }, tw);
5961 if (node.bfinally || (in_try = save_try)) {
5962 walk_body(node.bcatch, tw);
5963 } else {
5964 push();
5965 walk_body(node.bcatch, tw);
5966 pop();
5967 }
5968 }
5969 in_try = save_try;
5970 segment = save;
5971 if (node.bfinally) node.bfinally.walk(tw);
5972 return true;
5973 }
5974 if (node instanceof AST_Unary) {
5975 if (!UNARY_POSTFIX[node.operator]) return;
5976 var sym = node.expression;
5977 if (!(sym instanceof AST_SymbolRef)) return;
5978 mark(sym, true);
5979 return true;
5980 }
5981 if (node instanceof AST_VarDef) {
5982 var assigned = node.value;
5983 if (assigned) {
5984 assigned.walk(tw);
5985 } else {
5986 assigned = segment.block instanceof AST_ForEnumeration && segment.block.init === tw.parent();
5987 }
5988 node.name.mark_symbol(assigned ? function(node) {
5989 if (!(node instanceof AST_SymbolDeclaration)) return;
5990 if (node instanceof AST_SymbolVar) {
5991 mark(node);
5992 } else {
5993 references[node.definition().id] = false;
5994 }
5995 return true;
5996 } : function(node) {
5997 if (!(node instanceof AST_SymbolDeclaration)) return;
5998 var id = node.definition().id;
5999 if (!(node instanceof AST_SymbolVar)) {
6000 references[id] = false;
6001 } else if (!(id in references)) {
6002 declarations.add(id, node);
6003 } else if (references[id]) {
6004 references[id].push(node);
6005 }
6006 return true;
6007 }, tw);
6008 return true;
6009 }
6010 if (node instanceof AST_While) {
6011 push();
6012 segment.block = node;
6013 segment.loop = true;
6014 descend();
6015 pop();
6016 return true;
6017 }
6018
6019 function mark_expression(exp) {
6020 if (compressor.option("ie")) {
6021 var sym = root_expr(exp);
6022 if (sym instanceof AST_SymbolRef) sym.walk(tw);
6023 }
6024 return true;
6025 }
6026 });
6027 tw.directives = Object.create(compressor.directives);
6028 self.walk(tw);
6029 var changed = false;
6030 var merged = Object.create(null);
6031 while (first.length && last.length) {
6032 var head = first.pop();
6033 var def = head.definition;
6034 if (!(def.id in prev)) continue;
6035 if (!references[def.id]) continue;
6036 var head_refs = {
6037 start: references[def.id].start,
6038 };
6039 while (def.id in merged) def = merged[def.id];
6040 head_refs.end = references[def.id].end;
6041 var skipped = [];
6042 do {
6043 var tail = last.pop();
6044 if (!tail) continue;
6045 if (tail.index > head.index) continue;
6046 var id = tail.definition.id;
6047 var tail_refs = references[id];
6048 if (!tail_refs) continue;
6049 if (head_refs.start.block !== tail_refs.start.block
6050 || !mergeable(head_refs, tail_refs)
6051 || (head_refs.start.loop || !same_scope(def)) && !mergeable(tail_refs, head_refs)
6052 || compressor.option("webkit") && is_funarg(def) !== is_funarg(tail.definition)
6053 || !all(tail_refs, function(sym) {
6054 return sym.scope.find_variable(def.name) === def;
6055 })) {
6056 skipped.unshift(tail);
6057 continue;
6058 }
6059 var orig = [], refs = [];
6060 tail_refs.forEach(function(sym) {
6061 sym.thedef = def;
6062 sym.name = def.name;
6063 if (sym instanceof AST_SymbolRef) {
6064 refs.push(sym);
6065 } else {
6066 orig.push(sym);
6067 }
6068 });
6069 def.orig = orig.concat(def.orig);
6070 def.references = refs.concat(def.references);
6071 def.fixed = tail.definition.fixed && def.fixed;
6072 merged[id] = def;
6073 changed = true;
6074 break;
6075 } while (last.length);
6076 if (skipped.length) last = last.concat(skipped);
6077 }
6078 return changed;
6079
6080 function push() {
6081 segment = Object.create(segment);
6082 }
6083
6084 function pop() {
6085 segment = Object.getPrototypeOf(segment);
6086 }
6087
6088 function mark(sym, read) {
6089 var def = sym.definition(), ldef, seg = segment;
6090 if (in_try) {
6091 push();
6092 seg = segment;
6093 pop();
6094 }
6095 if (def.id in references) {
6096 var refs = references[def.id];
6097 if (!refs) return;
6098 if (refs.start.block !== seg.block) return references[def.id] = false;
6099 refs.push(sym);
6100 refs.end = seg;
6101 if (def.id in prev) {
6102 last[prev[def.id]] = null;
6103 } else if (!read) {
6104 return;
6105 }
6106 } else if ((ldef = self.variables.get(def.name)) !== def) {
6107 if (ldef && root === seg) references[ldef.id] = false;
6108 return references[def.id] = false;
6109 } else if (compressor.exposed(def) || NO_MERGE[sym.name]) {
6110 return references[def.id] = false;
6111 } else {
6112 var refs = declarations.get(def.id) || [];
6113 refs.push(sym);
6114 references[def.id] = refs;
6115 if (!read) {
6116 refs.start = seg;
6117 return first.push({
6118 index: index++,
6119 definition: def,
6120 });
6121 }
6122 if (seg.block !== self) return references[def.id] = false;
6123 refs.start = root;
6124 }
6125 prev[def.id] = last.length;
6126 last.push({
6127 index: index++,
6128 definition: def,
6129 });
6130 }
6131
6132 function insert(target) {
6133 var stack = [];
6134 while (true) {
6135 if (HOP(segment, "block")) {
6136 var block = segment.block;
6137 if (block instanceof AST_LabeledStatement) block = block.body;
6138 if (block === target) break;
6139 }
6140 stack.push(segment);
6141 pop();
6142 }
6143 segment.inserted = segment.block;
6144 push();
6145 while (stack.length) {
6146 var seg = stack.pop();
6147 push();
6148 if (HOP(seg, "block")) segment.block = seg.block;
6149 if (HOP(seg, "loop")) segment.loop = seg.loop;
6150 }
6151 }
6152
6153 function must_visit(base, segment) {
6154 return base === segment || base.isPrototypeOf(segment);
6155 }
6156
6157 function mergeable(head, tail) {
6158 return must_visit(head.start, head.end) || must_visit(head.start, tail.start);
6159 }
6160 });
6161
6162 function fill_holes(orig, elements) {
6163 for (var i = elements.length; --i >= 0;) {
6164 if (!elements[i]) elements[i] = make_node(AST_Hole, orig);
6165 }
6166 }
6167
6168 function to_class_expr(defcl, drop_name) {
6169 var cl = make_node(AST_ClassExpression, defcl, defcl);
6170 cl.name = drop_name ? null : make_node(AST_SymbolClass, defcl.name, defcl.name);
6171 return cl;
6172 }
6173
6174 function to_func_expr(defun, drop_name) {
6175 var ctor;
6176 switch (defun.CTOR) {
6177 case AST_AsyncDefun:
6178 ctor = AST_AsyncFunction;
6179 break;
6180 case AST_AsyncGeneratorDefun:
6181 ctor = AST_AsyncGeneratorFunction;
6182 break;
6183 case AST_Defun:
6184 ctor = AST_Function;
6185 break;
6186 case AST_GeneratorDefun:
6187 ctor = AST_GeneratorFunction;
6188 break;
6189 }
6190 var fn = make_node(ctor, defun, defun);
6191 fn.name = drop_name ? null : make_node(AST_SymbolLambda, defun.name, defun.name);
6192 return fn;
6193 }
6194
6195 AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
6196 if (!compressor.option("unused")) return;
6197 var self = this;
6198 var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs;
6199 var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars;
6200 var assign_as_unused = /keep_assign/.test(compressor.option("unused")) ? return_false : function(node, props) {
6201 var sym, nested = false;
6202 if (node instanceof AST_Assign) {
6203 if (node.write_only || node.operator == "=") sym = extract_reference(node.left, props);
6204 } else if (node instanceof AST_Unary) {
6205 if (node.write_only) sym = extract_reference(node.expression, props);
6206 }
6207 if (!(sym instanceof AST_SymbolRef)) return;
6208 var def = sym.definition();
6209 if (export_defaults[def.id]) return;
6210 if (compressor.exposed(def)) return;
6211 if (!can_drop_symbol(sym, compressor, nested)) return;
6212 return sym;
6213
6214 function extract_reference(node, props) {
6215 if (node instanceof AST_PropAccess) {
6216 var expr = node.expression;
6217 if (!expr.may_throw_on_access(compressor, true)) {
6218 nested = true;
6219 if (props && node instanceof AST_Sub) props.unshift(node.property);
6220 return extract_reference(expr, props);
6221 }
6222 } else if (node instanceof AST_Assign && node.operator == "=") {
6223 node.write_only = "p";
6224 var ref = extract_reference(node.right);
6225 if (!props) return ref;
6226 props.assign = node;
6227 return ref instanceof AST_SymbolRef ? ref : node.left;
6228 }
6229 return node;
6230 }
6231 };
6232 var assign_in_use = Object.create(null);
6233 var export_defaults = Object.create(null);
6234 var find_variable = function(name) {
6235 find_variable = compose(self, 0, noop);
6236 return find_variable(name);
6237
6238 function compose(child, level, find) {
6239 var parent = compressor.parent(level);
6240 if (!parent) return find;
6241 var in_arg = parent instanceof AST_Lambda && member(child, parent.argnames);
6242 return compose(parent, level + 1, in_arg ? function(name) {
6243 var def = find(name);
6244 if (def) return def;
6245 def = parent.variables.get(name);
6246 if (def) {
6247 var sym = def.orig[0];
6248 if (sym instanceof AST_SymbolFunarg || sym instanceof AST_SymbolLambda) return def;
6249 }
6250 } : parent.variables ? function(name) {
6251 return find(name) || parent.variables.get(name);
6252 } : find);
6253 }
6254 };
6255 var for_ins = Object.create(null);
6256 var in_use = [];
6257 var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use
6258 var value_read = Object.create(null);
6259 var value_modified = Object.create(null);
6260 var var_defs = Object.create(null);
6261 if (self instanceof AST_Toplevel && compressor.top_retain) {
6262 self.variables.each(function(def) {
6263 if (compressor.top_retain(def) && !(def.id in in_use_ids)) {
6264 in_use_ids[def.id] = true;
6265 in_use.push(def);
6266 }
6267 });
6268 }
6269 var assignments = new Dictionary();
6270 var initializations = new Dictionary();
6271 // pass 1: find out which symbols are directly used in
6272 // this scope (not in nested scopes).
6273 var scope = this;
6274 var tw = new TreeWalker(function(node, descend) {
6275 if (node instanceof AST_Lambda && node.uses_arguments && !tw.has_directive("use strict")) {
6276 node.each_argname(function(argname) {
6277 var def = argname.definition();
6278 if (!(def.id in in_use_ids)) {
6279 in_use_ids[def.id] = true;
6280 in_use.push(def);
6281 }
6282 });
6283 }
6284 if (node === self) return;
6285 if (scope === self) {
6286 if (node instanceof AST_DefClass) {
6287 var def = node.name.definition();
6288 if ((!drop_funcs || def.exported) && !(def.id in in_use_ids)) {
6289 in_use_ids[def.id] = true;
6290 in_use.push(def);
6291 }
6292 if (node.extends) node.extends.walk(tw);
6293 var is_export = false;
6294 if (tw.parent() instanceof AST_ExportDefault) {
6295 is_export = true;
6296 export_defaults[def.id] = true;
6297 }
6298 node.properties.forEach(function(prop) {
6299 if (prop.key instanceof AST_Node) prop.key.walk(tw);
6300 if (!prop.value) return;
6301 if (is_export || prop instanceof AST_ClassField && prop.static) {
6302 var save_scope = scope;
6303 scope = node;
6304 prop.value.walk(tw);
6305 scope = save_scope;
6306 } else {
6307 initializations.add(def.id, prop.value);
6308 }
6309 });
6310 return true;
6311 }
6312 if (node instanceof AST_LambdaDefinition) {
6313 var def = node.name.definition();
6314 if ((!drop_funcs || def.exported) && !(def.id in in_use_ids)) {
6315 in_use_ids[def.id] = true;
6316 in_use.push(def);
6317 }
6318 initializations.add(def.id, node);
6319 if (tw.parent() instanceof AST_ExportDefault) {
6320 export_defaults[def.id] = true;
6321 } else {
6322 return true;
6323 }
6324 }
6325 if (node instanceof AST_Definitions) {
6326 node.definitions.forEach(function(defn) {
6327 var value = defn.value;
6328 var side_effects = value
6329 && (defn.name instanceof AST_Destructured || value.has_side_effects(compressor));
6330 var shared = side_effects && value.tail_node().operator == "=";
6331 defn.name.mark_symbol(function(name) {
6332 if (!(name instanceof AST_SymbolDeclaration)) return;
6333 var def = name.definition();
6334 var_defs[def.id] = (var_defs[def.id] || 0) + 1;
6335 if (node instanceof AST_Var && def.orig[0] instanceof AST_SymbolCatch) {
6336 var redef = def.redefined();
6337 if (redef) var_defs[redef.id] = (var_defs[redef.id] || 0) + 1;
6338 }
6339 if (!(def.id in in_use_ids) && (!drop_vars || def.exported
6340 || (node instanceof AST_Const ? def.redefined() : def.const_redefs)
6341 || !(node instanceof AST_Var || is_safe_lexical(def)))) {
6342 in_use_ids[def.id] = true;
6343 in_use.push(def);
6344 }
6345 if (value) {
6346 if (!side_effects) {
6347 initializations.add(def.id, value);
6348 } else if (shared) {
6349 verify_safe_usage(def, name, value_modified[def.id]);
6350 }
6351 assignments.add(def.id, defn);
6352 }
6353 return true;
6354 }, tw);
6355 if (side_effects) value.walk(tw);
6356 });
6357 return true;
6358 }
6359 if (node instanceof AST_SymbolFunarg) {
6360 var def = node.definition();
6361 var_defs[def.id] = (var_defs[def.id] || 0) + 1;
6362 assignments.add(def.id, node);
6363 return true;
6364 }
6365 if (node instanceof AST_SymbolImport) {
6366 var def = node.definition();
6367 if (!(def.id in in_use_ids) && (!drop_vars || !is_safe_lexical(def))) {
6368 in_use_ids[def.id] = true;
6369 in_use.push(def);
6370 }
6371 return true;
6372 }
6373 }
6374 return scan_ref_scoped(node, descend, true);
6375 });
6376 tw.directives = Object.create(compressor.directives);
6377 self.walk(tw);
6378 var drop_fn_name = compressor.option("keep_fnames") ? return_false : compressor.option("ie") ? function(def) {
6379 return !compressor.exposed(def) && def.references.length == def.replaced;
6380 } : function(def) {
6381 if (!(def.id in in_use_ids)) return true;
6382 if (def.orig.length - def.eliminated < 2) return false;
6383 // function argument will always overshadow its name
6384 if (def.orig[1] instanceof AST_SymbolFunarg) return true;
6385 // retain if referenced within destructured object of argument
6386 return all(def.references, function(ref) {
6387 return !ref.in_arg;
6388 });
6389 };
6390 if (compressor.option("ie")) initializations.each(function(init, id) {
6391 if (id in in_use_ids) return;
6392 init.forEach(function(init) {
6393 init.walk(new TreeWalker(function(node) {
6394 if (node instanceof AST_Function && node.name && !drop_fn_name(node.name.definition())) {
6395 node.walk(tw);
6396 return true;
6397 }
6398 if (node instanceof AST_Scope) return true;
6399 }));
6400 });
6401 });
6402 // pass 2: for every used symbol we need to walk its
6403 // initialization code to figure out if it uses other
6404 // symbols (that may not be in_use).
6405 tw = new TreeWalker(scan_ref_scoped);
6406 for (var i = 0; i < in_use.length; i++) {
6407 var init = initializations.get(in_use[i].id);
6408 if (init) init.forEach(function(init) {
6409 init.walk(tw);
6410 });
6411 }
6412 Object.keys(assign_in_use).forEach(function(id) {
6413 var assigns = assign_in_use[id];
6414 if (!assigns) {
6415 delete assign_in_use[id];
6416 return;
6417 }
6418 assigns = assigns.reduce(function(in_use, assigns) {
6419 assigns.forEach(function(assign) {
6420 push_uniq(in_use, assign);
6421 });
6422 return in_use;
6423 }, []);
6424 var in_use = (assignments.get(id) || []).filter(function(node) {
6425 return find_if(node instanceof AST_Unary ? function(assign) {
6426 return assign === node;
6427 } : function(assign) {
6428 if (assign === node) return true;
6429 if (assign instanceof AST_Unary) return false;
6430 return get_rvalue(assign) === get_rvalue(node);
6431 }, assigns);
6432 });
6433 if (assigns.length == in_use.length) {
6434 assign_in_use[id] = in_use;
6435 } else {
6436 delete assign_in_use[id];
6437 }
6438 });
6439 // pass 3: we should drop declarations not in_use
6440 var trim_defns = [];
6441 var unused_fn_names = [];
6442 var calls_to_drop_args = [];
6443 var fns_with_marked_args = [];
6444 var trimmer = new TreeTransformer(function(node) {
6445 if (node instanceof AST_DefaultValue) return trim_default(trimmer, node);
6446 if (node instanceof AST_Destructured && node.rest) node.rest = node.rest.transform(trimmer);
6447 if (node instanceof AST_DestructuredArray) {
6448 var trim = !node.rest;
6449 for (var i = node.elements.length; --i >= 0;) {
6450 var element = node.elements[i].transform(trimmer);
6451 if (element) {
6452 node.elements[i] = element;
6453 trim = false;
6454 } else if (trim) {
6455 node.elements.pop();
6456 } else {
6457 node.elements[i] = make_node(AST_Hole, node.elements[i]);
6458 }
6459 }
6460 return node;
6461 }
6462 if (node instanceof AST_DestructuredObject) {
6463 var properties = [];
6464 node.properties.forEach(function(prop) {
6465 var retain = false;
6466 if (prop.key instanceof AST_Node) {
6467 prop.key = prop.key.transform(tt);
6468 retain = prop.key.has_side_effects(compressor);
6469 }
6470 if ((retain || node.rest) && is_decl(prop.value)) {
6471 prop.value = prop.value.transform(tt);
6472 properties.push(prop);
6473 } else {
6474 var value = prop.value.transform(trimmer);
6475 if (!value && node.rest) {
6476 if (prop.value instanceof AST_DestructuredArray) {
6477 value = make_node(AST_DestructuredArray, prop.value, { elements: [] });
6478 } else {
6479 value = make_node(AST_DestructuredObject, prop.value, { properties: [] });
6480 }
6481 }
6482 if (value) {
6483 prop.value = value;
6484 properties.push(prop);
6485 }
6486 }
6487 });
6488 node.properties = properties;
6489 return node;
6490 }
6491 if (node instanceof AST_SymbolDeclaration) return node.definition().id in in_use_ids ? node : null;
6492 });
6493 var tt = new TreeTransformer(function(node, descend, in_list) {
6494 var parent = tt.parent();
6495 if (drop_vars) {
6496 var props = [], sym = assign_as_unused(node, props);
6497 if (sym) {
6498 var value;
6499 if (can_drop_lhs(sym, node)) {
6500 if (node instanceof AST_Assign) {
6501 value = get_rhs(node);
6502 if (node.write_only === true) value = value.drop_side_effect_free(compressor);
6503 }
6504 if (!value) value = make_node(AST_Number, node, { value: 0 });
6505 }
6506 if (value) {
6507 if (props.assign) {
6508 var assign = props.assign.drop_side_effect_free(compressor);
6509 if (assign) {
6510 assign.write_only = true;
6511 props.unshift(assign);
6512 }
6513 }
6514 if (!(parent instanceof AST_Sequence)
6515 || parent.tail_node() === node
6516 || value.has_side_effects(compressor)) {
6517 props.push(value);
6518 }
6519 switch (props.length) {
6520 case 0:
6521 return List.skip;
6522 case 1:
6523 return maintain_this_binding(compressor, parent, node, props[0].transform(tt));
6524 default:
6525 return make_sequence(node, props.map(function(prop) {
6526 return prop.transform(tt);
6527 }));
6528 }
6529 }
6530 } else if (node instanceof AST_UnaryPostfix
6531 && node.expression instanceof AST_SymbolRef
6532 && indexOf_assign(node.expression.definition(), node) < 0) {
6533 return make_node(AST_UnaryPrefix, node, {
6534 operator: "+",
6535 expression: node.expression
6536 });
6537 }
6538 }
6539 if (node instanceof AST_Call) calls_to_drop_args.push(node);
6540 if (scope !== self) return;
6541 if (drop_funcs && node !== self && node instanceof AST_DefClass) {
6542 var def = node.name.definition();
6543 if (!(def.id in in_use_ids)) {
6544 log(node.name, "Dropping unused class {name}");
6545 def.eliminated++;
6546 descend(node, tt);
6547 if (parent instanceof AST_ExportDefault) return to_class_expr(node, true);
6548 var trimmed = node.drop_side_effect_free(compressor, true);
6549 if (trimmed === node) trimmed = to_class_expr(node, true);
6550 if (trimmed) return make_node(AST_SimpleStatement, node, { body: trimmed });
6551 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
6552 }
6553 }
6554 if (node instanceof AST_ClassExpression && node.name && drop_fn_name(node.name.definition())) {
6555 unused_fn_names.push(node);
6556 }
6557 if (node instanceof AST_Lambda) {
6558 if (drop_funcs && node !== self && node instanceof AST_LambdaDefinition) {
6559 var def = node.name.definition();
6560 if (!(def.id in in_use_ids)) {
6561 log(node.name, "Dropping unused function {name}");
6562 def.eliminated++;
6563 if (parent instanceof AST_ExportDefault) {
6564 descend_scope();
6565 return to_func_expr(node, true);
6566 }
6567 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
6568 }
6569 }
6570 if (node instanceof AST_LambdaExpression && node.name && drop_fn_name(node.name.definition())) {
6571 unused_fn_names.push(node);
6572 }
6573 if (!(node instanceof AST_Accessor)) {
6574 if (node.rest) {
6575 var rest = node.rest.transform(trimmer);
6576 if (rest instanceof AST_Destructured && !rest.rest
6577 && (!node.uses_arguments || tt.has_directive("use strict"))) {
6578 if (rest instanceof AST_DestructuredArray) {
6579 if (rest.elements.length == 0) rest = null;
6580 } else if (rest.properties.length == 0) {
6581 rest = null;
6582 }
6583 }
6584 node.rest = rest;
6585 }
6586 var argnames = node.argnames;
6587 var trim = compressor.drop_fargs(node, parent) && !node.rest;
6588 var default_length = trim ? -1 : node.length();
6589 for (var i = argnames.length; --i >= 0;) {
6590 var sym = argnames[i];
6591 if (!(sym instanceof AST_SymbolFunarg)) {
6592 var arg = sym.transform(trimmer);
6593 if (arg) {
6594 trim = false;
6595 } else if (trim) {
6596 log(sym.name, "Dropping unused default argument {name}");
6597 argnames.pop();
6598 } else if (i > default_length) {
6599 log(sym.name, "Dropping unused default argument assignment {name}");
6600 sym.name.unused = true;
6601 argnames[i] = sym.name;
6602 } else {
6603 log(sym.name, "Dropping unused default argument value {name}");
6604 sym.value = make_node(AST_Number, sym, { value: 0 });
6605 }
6606 continue;
6607 }
6608 var def = sym.definition();
6609 if (def.id in in_use_ids) {
6610 trim = false;
6611 if (indexOf_assign(def, sym) < 0) sym.unused = null;
6612 } else if (trim) {
6613 log(sym, "Dropping unused function argument {name}");
6614 argnames.pop();
6615 } else {
6616 sym.unused = true;
6617 }
6618 }
6619 fns_with_marked_args.push(node);
6620 }
6621 }
6622 if (node instanceof AST_Catch && node.argname instanceof AST_Destructured) {
6623 node.argname.transform(trimmer);
6624 }
6625 if (node instanceof AST_Definitions && !(parent instanceof AST_ForEnumeration && parent.init === node)) {
6626 // place uninitialized names at the start
6627 var body = [], head = [], tail = [];
6628 // for unused names whose initialization has
6629 // side effects, we can cascade the init. code
6630 // into the next one, or next statement.
6631 var side_effects = [];
6632 var duplicated = 0;
6633 var is_var = node instanceof AST_Var;
6634 node.definitions.forEach(function(def) {
6635 if (def.value) def.value = def.value.transform(tt);
6636 var value = def.value;
6637 if (def.name instanceof AST_Destructured) {
6638 var trimmed = trim_destructured(def.name, value, function(node) {
6639 if (!drop_vars) return node;
6640 if (node.definition().id in in_use_ids) return node;
6641 if (is_catch(node)) return node;
6642 if (is_var && !can_drop_symbol(node)) return node;
6643 return null;
6644 }, true);
6645 if (trimmed.name) {
6646 def = make_node(AST_VarDef, def, {
6647 name: trimmed.name,
6648 value: value = trimmed.value,
6649 });
6650 flush();
6651 } else if (trimmed.value) {
6652 side_effects.push(trimmed.value);
6653 }
6654 return;
6655 }
6656 var sym = def.name.definition();
6657 var drop_sym = is_var ? can_drop_symbol(def.name) : is_safe_lexical(sym);
6658 if (!drop_sym || !drop_vars || sym.id in in_use_ids) {
6659 if (value && indexOf_assign(sym, def) < 0) {
6660 value = value.drop_side_effect_free(compressor);
6661 if (value) {
6662 AST_Node.warn("Side effects in definition of variable {name} [{file}:{line},{col}]", template(def.name));
6663 side_effects.push(value);
6664 }
6665 value = null;
6666 trim_defns.push(def);
6667 }
6668 var old_def;
6669 if (!value && !(node instanceof AST_Let)) {
6670 if (parent instanceof AST_ExportDeclaration) {
6671 flush();
6672 } else if (drop_sym && var_defs[sym.id] > 1) {
6673 AST_Node.info("Dropping declaration of variable {name} [{file}:{line},{col}]", template(def.name));
6674 var_defs[sym.id]--;
6675 sym.eliminated++;
6676 } else {
6677 head.push(def);
6678 }
6679 } else if (compressor.option("functions")
6680 && !compressor.option("ie")
6681 && drop_sym
6682 && var_defs[sym.id] == 1
6683 && sym.assignments == 0
6684 && value instanceof AST_LambdaExpression
6685 && !is_arguments(sym)
6686 && !is_arrow(value)
6687 && assigned_once(value, sym.references)
6688 && can_declare_defun(value)
6689 && (old_def = rename_def(value, def.name.name)) !== false) {
6690 AST_Node.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name));
6691 var ctor;
6692 switch (value.CTOR) {
6693 case AST_AsyncFunction:
6694 ctor = AST_AsyncDefun;
6695 break;
6696 case AST_AsyncGeneratorFunction:
6697 ctor = AST_AsyncGeneratorDefun;
6698 break;
6699 case AST_Function:
6700 ctor = AST_Defun;
6701 break;
6702 case AST_GeneratorFunction:
6703 ctor = AST_GeneratorDefun;
6704 break;
6705 }
6706 var defun = make_node(ctor, def, value);
6707 defun.name = make_node(AST_SymbolDefun, def.name, def.name);
6708 var name_def = def.name.scope.resolve().def_function(defun.name);
6709 if (old_def) old_def.forEach(function(node) {
6710 node.name = name_def.name;
6711 node.thedef = name_def;
6712 node.reference();
6713 });
6714 body.push(defun);
6715 } else {
6716 if (drop_sym
6717 && var_defs[sym.id] > 1
6718 && !(parent instanceof AST_ExportDeclaration)
6719 && sym.orig.indexOf(def.name) > sym.eliminated) {
6720 var_defs[sym.id]--;
6721 duplicated++;
6722 }
6723 flush();
6724 }
6725 } else if (is_catch(def.name)) {
6726 value = value && value.drop_side_effect_free(compressor);
6727 if (value) side_effects.push(value);
6728 if (var_defs[sym.id] > 1) {
6729 AST_Node.warn("Dropping duplicated declaration of variable {name} [{file}:{line},{col}]", template(def.name));
6730 var_defs[sym.id]--;
6731 sym.eliminated++;
6732 } else {
6733 def.value = null;
6734 head.push(def);
6735 }
6736 } else {
6737 value = value && !value.single_use && value.drop_side_effect_free(compressor);
6738 if (value) {
6739 AST_Node.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
6740 side_effects.push(value);
6741 } else {
6742 log(def.name, "Dropping unused variable {name}");
6743 }
6744 sym.eliminated++;
6745 }
6746
6747 function assigned_once(fn, refs) {
6748 if (refs.length == 0) return fn === def.name.fixed_value();
6749 return all(refs, function(ref) {
6750 return fn === ref.fixed_value();
6751 });
6752 }
6753
6754 function can_declare_defun(fn) {
6755 if (!is_var || compressor.has_directive("use strict") || !(fn instanceof AST_Function)) {
6756 return parent instanceof AST_Scope;
6757 }
6758 return parent instanceof AST_Block
6759 || parent instanceof AST_For && parent.init === node
6760 || parent instanceof AST_If;
6761 }
6762
6763 function rename_def(fn, name) {
6764 if (!fn.name) return null;
6765 var def = fn.name.definition();
6766 if (def.orig.length > 1) return null;
6767 if (def.assignments > 0) return false;
6768 if (def.name == name) return def;
6769 var forbidden;
6770 switch (name) {
6771 case "await":
6772 forbidden = is_async;
6773 break;
6774 case "yield":
6775 forbidden = is_generator;
6776 break;
6777 }
6778 return all(def.references, function(ref) {
6779 var scope = ref.scope;
6780 if (scope.find_variable(name) !== sym) return false;
6781 if (forbidden) do {
6782 scope = scope.resolve();
6783 if (forbidden(scope)) return false;
6784 } while (scope !== fn && (scope = scope.parent_scope));
6785 return true;
6786 }) && def;
6787 }
6788
6789 function is_catch(node) {
6790 var sym = node.definition();
6791 return sym.orig[0] instanceof AST_SymbolCatch && sym.scope.resolve() === node.scope.resolve();
6792 }
6793
6794 function flush() {
6795 if (side_effects.length > 0) {
6796 if (tail.length == 0) {
6797 body.push(make_node(AST_SimpleStatement, node, {
6798 body: make_sequence(node, side_effects),
6799 }));
6800 } else if (value) {
6801 side_effects.push(value);
6802 def.value = make_sequence(value, side_effects);
6803 } else {
6804 def.value = make_node(AST_UnaryPrefix, def, {
6805 operator: "void",
6806 expression: make_sequence(def, side_effects),
6807 });
6808 }
6809 side_effects = [];
6810 }
6811 tail.push(def);
6812 }
6813 });
6814 switch (head.length) {
6815 case 0:
6816 if (tail.length == 0) break;
6817 if (tail.length == duplicated) {
6818 [].unshift.apply(side_effects, tail.map(function(def) {
6819 AST_Node.info("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
6820 var sym = def.name.definition();
6821 var ref = make_node(AST_SymbolRef, def.name, def.name);
6822 sym.references.push(ref);
6823 var assign = make_node(AST_Assign, def, {
6824 operator: "=",
6825 left: ref,
6826 right: def.value,
6827 });
6828 var index = indexOf_assign(sym, def);
6829 if (index >= 0) assign_in_use[sym.id][index] = assign;
6830 sym.eliminated++;
6831 return assign;
6832 }));
6833 break;
6834 }
6835 case 1:
6836 if (tail.length == 0) {
6837 var id = head[0].name.definition().id;
6838 if (id in for_ins) {
6839 node.definitions = head;
6840 for_ins[id].init = node;
6841 break;
6842 }
6843 }
6844 default:
6845 node.definitions = head.concat(tail);
6846 body.push(node);
6847 }
6848 if (side_effects.length > 0) {
6849 body.push(make_node(AST_SimpleStatement, node, {
6850 body: make_sequence(node, side_effects)
6851 }));
6852 }
6853 return insert_statements(body, node, in_list);
6854 }
6855 if (node instanceof AST_Assign) {
6856 descend(node, tt);
6857 if (!(node.left instanceof AST_Destructured)) return node;
6858 var trimmed = trim_destructured(node.left, node.right, function(node) {
6859 return node;
6860 }, node.write_only === true);
6861 if (trimmed.name) return make_node(AST_Assign, node, {
6862 operator: node.operator,
6863 left: trimmed.name,
6864 right: trimmed.value,
6865 });
6866 if (trimmed.value) return trimmed.value;
6867 if (parent instanceof AST_Sequence && parent.tail_node() !== node) return List.skip;
6868 return make_node(AST_Number, node, { value: 0 });
6869 }
6870 if (node instanceof AST_LabeledStatement && node.body instanceof AST_For) {
6871 // Certain combination of unused name + side effect leads to invalid AST:
6872 // https://github.com/mishoo/UglifyJS/issues/1830
6873 // We fix it at this stage by moving the label inwards, back to the `for`.
6874 descend(node, tt);
6875 if (node.body instanceof AST_BlockStatement) {
6876 var block = node.body;
6877 node.body = block.body.pop();
6878 block.body.push(node);
6879 return in_list ? List.splice(block.body) : block;
6880 }
6881 return node;
6882 }
6883 if (node instanceof AST_Scope) {
6884 descend_scope();
6885 return node;
6886 }
6887 if (node instanceof AST_SymbolImport) {
6888 if (!compressor.option("imports") || node.definition().id in in_use_ids) return node;
6889 return in_list ? List.skip : null;
6890 }
6891
6892 function descend_scope() {
6893 var save_scope = scope;
6894 scope = node;
6895 descend(node, tt);
6896 scope = save_scope;
6897 }
6898 }, function(node, in_list) {
6899 if (node instanceof AST_BlockStatement) return trim_block(node, tt.parent(), in_list);
6900 // Certain combination of unused name + side effect leads to invalid AST:
6901 // https://github.com/mishoo/UglifyJS/issues/44
6902 // https://github.com/mishoo/UglifyJS/issues/1838
6903 // https://github.com/mishoo/UglifyJS/issues/3371
6904 // We fix it at this stage by moving the `var` outside the `for`.
6905 if (node instanceof AST_For) {
6906 var block;
6907 if (node.init instanceof AST_BlockStatement) {
6908 block = node.init;
6909 node.init = block.body.pop();
6910 block.body.push(node);
6911 }
6912 if (node.init instanceof AST_Defun) {
6913 if (!block) {
6914 block = make_node(AST_BlockStatement, node, {
6915 body: [ node ]
6916 });
6917 }
6918 block.body.splice(-1, 0, node.init);
6919 node.init = null;
6920 } else if (node.init instanceof AST_SimpleStatement) {
6921 node.init = node.init.body;
6922 } else if (is_empty(node.init)) {
6923 node.init = null;
6924 }
6925 return !block ? node : in_list ? List.splice(block.body) : block;
6926 }
6927 if (node instanceof AST_ForIn) {
6928 if (!drop_vars || !compressor.option("loops")) return;
6929 if (!is_empty(node.body)) return;
6930 var sym = get_init_symbol(node);
6931 if (!sym) return;
6932 var def = sym.definition();
6933 if (def.id in in_use_ids) return;
6934 log(sym, "Dropping unused loop variable {name}");
6935 if (for_ins[def.id] === node) delete for_ins[def.id];
6936 var body = [];
6937 var value = node.object.drop_side_effect_free(compressor);
6938 if (value) {
6939 AST_Node.warn("Side effects in object of for-in loop [{file}:{line},{col}]", value.start);
6940 body.push(make_node(AST_SimpleStatement, node, {
6941 body: value
6942 }));
6943 }
6944 if (node.init instanceof AST_Definitions && def.orig[0] instanceof AST_SymbolCatch) {
6945 body.push(node.init);
6946 }
6947 return insert_statements(body, node, in_list);
6948 }
6949 if (node instanceof AST_Import) {
6950 if (node.properties && node.properties.length == 0) node.properties = null;
6951 return node;
6952 }
6953 if (node instanceof AST_Sequence) {
6954 if (node.expressions.length > 1) return;
6955 return maintain_this_binding(compressor, tt.parent(), node, node.expressions[0]);
6956 }
6957 });
6958 tt.push(compressor.parent());
6959 self.transform(tt);
6960 if (self instanceof AST_Lambda
6961 && self.body.length == 1
6962 && self.body[0] instanceof AST_Directive
6963 && self.body[0].value == "use strict") {
6964 self.body.length = 0;
6965 }
6966 trim_defns.forEach(function(def) {
6967 def.value = null;
6968 });
6969 unused_fn_names.forEach(function(fn) {
6970 fn.name = null;
6971 });
6972 calls_to_drop_args.forEach(function(call) {
6973 drop_unused_call_args(call, compressor, fns_with_marked_args);
6974 });
6975
6976 function log(sym, text) {
6977 AST_Node[sym.definition().references.length > 0 ? "info" : "warn"](text + " [{file}:{line},{col}]", template(sym));
6978 }
6979
6980 function template(sym) {
6981 return {
6982 name: sym.name,
6983 file: sym.start.file,
6984 line: sym.start.line,
6985 col : sym.start.col,
6986 };
6987 }
6988
6989 function get_rvalue(expr) {
6990 return expr[expr instanceof AST_Assign ? "right" : "value"];
6991 }
6992
6993 function insert_statements(body, orig, in_list) {
6994 switch (body.length) {
6995 case 0:
6996 return in_list ? List.skip : make_node(AST_EmptyStatement, orig);
6997 case 1:
6998 return body[0];
6999 default:
7000 return in_list ? List.splice(body) : make_node(AST_BlockStatement, orig, {
7001 body: body
7002 });
7003 }
7004 }
7005
7006 function track_assigns(def, node) {
7007 if (def.scope.resolve() !== self) return false;
7008 if (!def.fixed || !node.fixed) assign_in_use[def.id] = false;
7009 return assign_in_use[def.id] !== false;
7010 }
7011
7012 function add_assigns(def, node) {
7013 if (!assign_in_use[def.id]) assign_in_use[def.id] = [];
7014 if (node.fixed.assigns) push_uniq(assign_in_use[def.id], node.fixed.assigns);
7015 }
7016
7017 function indexOf_assign(def, node) {
7018 var nodes = assign_in_use[def.id];
7019 return nodes && nodes.indexOf(node);
7020 }
7021
7022 function verify_safe_usage(def, read, modified) {
7023 if (def.id in in_use_ids) return;
7024 if (read && modified) {
7025 in_use_ids[def.id] = read;
7026 in_use.push(def);
7027 } else {
7028 value_read[def.id] = read;
7029 value_modified[def.id] = modified;
7030 }
7031 }
7032
7033 function can_drop_lhs(sym, node) {
7034 var def = sym.definition();
7035 var in_use = in_use_ids[def.id];
7036 if (!in_use) return true;
7037 if (node[node instanceof AST_Assign ? "left" : "expression"] !== sym) return false;
7038 return in_use === sym && def.references.length - def.replaced == 1 || indexOf_assign(def, node) < 0;
7039 }
7040
7041 function get_rhs(assign) {
7042 var rhs = assign.right;
7043 if (!assign.write_only) return rhs;
7044 if (!(rhs instanceof AST_Binary && lazy_op[rhs.operator])) return rhs;
7045 if (!(rhs.left instanceof AST_SymbolRef)) return rhs;
7046 if (!(assign.left instanceof AST_SymbolRef)) return rhs;
7047 var def = assign.left.definition();
7048 if (rhs.left.definition() !== def) return rhs;
7049 if (rhs.right.has_side_effects(compressor)) return rhs;
7050 if (track_assigns(def, rhs.left)) add_assigns(def, rhs.left);
7051 return rhs.right;
7052 }
7053
7054 function get_init_symbol(for_in) {
7055 var init = for_in.init;
7056 if (init instanceof AST_Definitions) {
7057 init = init.definitions[0].name;
7058 return init instanceof AST_SymbolDeclaration && init;
7059 }
7060 while (init instanceof AST_PropAccess) init = init.expression.tail_node();
7061 if (init instanceof AST_SymbolRef) return init;
7062 }
7063
7064 function scan_ref_scoped(node, descend, init) {
7065 if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef) {
7066 var def = node.left.definition();
7067 if (def.scope.resolve() === self) assignments.add(def.id, node);
7068 }
7069 if (node instanceof AST_Unary && node.expression instanceof AST_SymbolRef) {
7070 var def = node.expression.definition();
7071 if (def.scope.resolve() === self) assignments.add(def.id, node);
7072 }
7073 var node_def, props = [], sym = assign_as_unused(node, props);
7074 if (sym && ((node_def = sym.definition()).scope.resolve() === self
7075 || self.variables.get(sym.name) === node_def)
7076 && !(is_arguments(node_def) && !all(self.argnames, function(argname) {
7077 return !argname.match_symbol(function(node) {
7078 if (node instanceof AST_SymbolFunarg) {
7079 var def = node.definition();
7080 return def.references.length > def.replaced;
7081 }
7082 }, true);
7083 }))) {
7084 if (node.write_only === "p" && node.right.may_throw_on_access(compressor, true)) return;
7085 var assign = props.assign;
7086 if (assign) {
7087 assign.write_only = true;
7088 assign.walk(tw);
7089 }
7090 props.forEach(function(prop) {
7091 prop.walk(tw);
7092 });
7093 if (node instanceof AST_Assign) {
7094 var right = get_rhs(node), shared = false;
7095 if (init && node.write_only === true && !right.has_side_effects(compressor)) {
7096 initializations.add(node_def.id, right);
7097 } else {
7098 right.walk(tw);
7099 shared = right.tail_node().operator == "=";
7100 }
7101 if (node.left === sym) {
7102 if (!node.write_only || shared) {
7103 verify_safe_usage(node_def, sym, value_modified[node_def.id]);
7104 }
7105 } else {
7106 var fixed = sym.fixed_value();
7107 if (!fixed || !fixed.is_constant()) {
7108 verify_safe_usage(node_def, value_read[node_def.id], true);
7109 }
7110 }
7111 }
7112 if (track_assigns(node_def, sym) && is_lhs(sym, node) !== sym) add_assigns(node_def, sym);
7113 return true;
7114 }
7115 if (node instanceof AST_ForIn) {
7116 if (node.init instanceof AST_SymbolRef && scope === self) {
7117 var id = node.init.definition().id;
7118 if (!(id in for_ins)) for_ins[id] = node;
7119 }
7120 if (!drop_vars || !compressor.option("loops")) return;
7121 if (!is_empty(node.body)) return;
7122 if (node.init.has_side_effects(compressor)) return;
7123 var sym = get_init_symbol(node);
7124 if (!sym) return;
7125 var def = sym.definition();
7126 if (def.scope.resolve() !== self) {
7127 var d = find_variable(sym.name);
7128 if (d === def || d && d.redefined() === def) return;
7129 }
7130 node.object.walk(tw);
7131 return true;
7132 }
7133 if (node instanceof AST_SymbolRef) {
7134 node_def = node.definition();
7135 if (!(node_def.id in in_use_ids)) {
7136 in_use_ids[node_def.id] = true;
7137 in_use.push(node_def);
7138 }
7139 if (cross_scope(node_def.scope, node.scope)) {
7140 var redef = node_def.redefined();
7141 if (redef && !(redef.id in in_use_ids)) {
7142 in_use_ids[redef.id] = true;
7143 in_use.push(redef);
7144 }
7145 }
7146 if (track_assigns(node_def, node)) add_assigns(node_def, node);
7147 return true;
7148 }
7149 if (node instanceof AST_Scope) {
7150 var save_scope = scope;
7151 scope = node;
7152 descend();
7153 scope = save_scope;
7154 return true;
7155 }
7156 }
7157
7158 function is_decl(node) {
7159 return (node instanceof AST_DefaultValue ? node.name : node) instanceof AST_SymbolDeclaration;
7160 }
7161
7162 function trim_default(trimmer, node) {
7163 node.value = node.value.transform(tt);
7164 var name = node.name.transform(trimmer);
7165 if (!name) {
7166 if (node.name instanceof AST_Destructured) return null;
7167 var value = node.value.drop_side_effect_free(compressor);
7168 if (!value) return null;
7169 log(node.name, "Side effects in default value of unused variable {name}");
7170 node.name.unused = null;
7171 node.value = value;
7172 }
7173 return node;
7174 }
7175
7176 function trim_destructured(node, value, process, drop) {
7177 var trimmer = new TreeTransformer(function(node) {
7178 if (node instanceof AST_DefaultValue) {
7179 if (compressor.option("default_values") && value && value.is_defined(compressor)) {
7180 node = node.name;
7181 } else {
7182 var save_drop = drop;
7183 drop = false;
7184 var trimmed = trim_default(trimmer, node);
7185 drop = save_drop;
7186 if (!trimmed && drop && value) value = value.drop_side_effect_free(compressor);
7187 return trimmed;
7188 }
7189 }
7190 if (node instanceof AST_DestructuredArray) {
7191 var save_drop = drop;
7192 var save_value = value;
7193 if (value instanceof AST_SymbolRef) {
7194 drop = false;
7195 value = value.fixed_value();
7196 }
7197 var values = value instanceof AST_Array && value.elements;
7198 var elements = [], newValues = drop && [], pos = 0;
7199 node.elements.forEach(function(element, index) {
7200 value = values && values[index];
7201 if (value instanceof AST_Hole) {
7202 value = null;
7203 } else if (value instanceof AST_Spread) {
7204 if (drop) {
7205 newValues.length = pos;
7206 fill_holes(save_value, newValues);
7207 [].push.apply(newValues, values.slice(index));
7208 save_value.elements = newValues;
7209 }
7210 value = values = false;
7211 }
7212 element = element.transform(trimmer);
7213 if (element) elements[pos] = element;
7214 if (drop && value) newValues[pos] = value;
7215 if (element || value || !drop || !values) pos++;
7216 });
7217 value = values && make_node(AST_Array, save_value, {
7218 elements: values.slice(node.elements.length),
7219 });
7220 if (node.rest) {
7221 var was_drop = drop;
7222 drop = false;
7223 node.rest = node.rest.transform(compressor.option("rests") ? trimmer : tt);
7224 drop = was_drop;
7225 if (node.rest) elements.length = pos;
7226 }
7227 if (drop) {
7228 if (value && !node.rest) value = value.drop_side_effect_free(compressor);
7229 if (value instanceof AST_Array) {
7230 value = value.elements;
7231 } else if (value instanceof AST_Sequence) {
7232 value = value.expressions;
7233 } else if (value) {
7234 value = [ value ];
7235 }
7236 if (value && value.length) {
7237 newValues.length = pos;
7238 [].push.apply(newValues, value);
7239 }
7240 }
7241 value = save_value;
7242 drop = save_drop;
7243 if (values && newValues) {
7244 fill_holes(value, newValues);
7245 value.elements = newValues;
7246 }
7247 if (!node.rest && (value instanceof AST_Array
7248 || value && value.is_string(compressor))) switch (elements.length) {
7249 case 0:
7250 if (drop) value = value.drop_side_effect_free(compressor);
7251 return null;
7252 case 1:
7253 if (!drop) break;
7254 var sym = elements[0];
7255 if (sym.has_side_effects(compressor)) break;
7256 if (value.has_side_effects(compressor) && sym.match_symbol(function(node) {
7257 return node instanceof AST_PropAccess;
7258 })) break;
7259 value = make_node(AST_Sub, node, {
7260 expression: value,
7261 property: make_node(AST_Number, node, { value: 0 }),
7262 });
7263 return sym;
7264 }
7265 fill_holes(node, elements);
7266 node.elements = elements;
7267 return node;
7268 }
7269 if (node instanceof AST_DestructuredObject) {
7270 var save_drop = drop;
7271 var save_value = value;
7272 if (value instanceof AST_SymbolRef) {
7273 drop = false;
7274 value = value.fixed_value();
7275 }
7276 var prop_keys, prop_map;
7277 if (value instanceof AST_Object) {
7278 prop_keys = [];
7279 prop_map = new Dictionary();
7280 value.properties.forEach(function(prop, index) {
7281 if (prop instanceof AST_Spread) return prop_map = false;
7282 var key = prop.key;
7283 if (key instanceof AST_Node) key = key.evaluate(compressor, true);
7284 if (key instanceof AST_Node) {
7285 prop_map = false;
7286 } else if (prop_map && !(prop instanceof AST_ObjectSetter)) {
7287 prop_map.set(key, prop);
7288 }
7289 prop_keys[index] = key;
7290 });
7291 }
7292 if (node.rest) {
7293 value = false;
7294 node.rest = node.rest.transform(compressor.option("rests") ? trimmer : tt);
7295 }
7296 var can_drop = new Dictionary();
7297 var drop_keys = drop && new Dictionary();
7298 var properties = [];
7299 node.properties.map(function(prop) {
7300 var key = prop.key;
7301 if (key instanceof AST_Node) {
7302 prop.key = key = key.transform(tt);
7303 key = key.evaluate(compressor, true);
7304 }
7305 if (key instanceof AST_Node) {
7306 drop_keys = false;
7307 } else {
7308 can_drop.set(key, !can_drop.has(key));
7309 }
7310 return key;
7311 }).forEach(function(key, index) {
7312 var prop = node.properties[index], trimmed;
7313 if (key instanceof AST_Node) {
7314 drop = false;
7315 value = false;
7316 trimmed = prop.value.transform(trimmer) || retain_lhs(prop.value);
7317 } else {
7318 drop = drop_keys && can_drop.get(key);
7319 var mapped = prop_map && prop_map.get(key);
7320 if (mapped) {
7321 value = mapped.value;
7322 if (value instanceof AST_Accessor) value = false;
7323 } else {
7324 value = false;
7325 }
7326 trimmed = prop.value.transform(trimmer);
7327 if (!trimmed) {
7328 if (node.rest || retain_key(prop)) trimmed = retain_lhs(prop.value);
7329 if (drop_keys && !drop_keys.has(key)) {
7330 if (mapped) {
7331 drop_keys.set(key, mapped);
7332 if (value === null) {
7333 prop_map.set(key, retain_key(mapped) && make_node(AST_ObjectKeyVal, mapped, {
7334 key: mapped.key,
7335 value: make_node(AST_Number, mapped, { value: 0 }),
7336 }));
7337 }
7338 } else {
7339 drop_keys.set(key, true);
7340 }
7341 }
7342 } else if (drop_keys) {
7343 drop_keys.set(key, false);
7344 }
7345 if (value) mapped.value = value;
7346 }
7347 if (trimmed) {
7348 prop.value = trimmed;
7349 properties.push(prop);
7350 }
7351 });
7352 value = save_value;
7353 drop = save_drop;
7354 if (drop_keys && prop_keys) value.properties = List(value.properties, function(prop, index) {
7355 if (prop instanceof AST_Spread) return prop;
7356 var key = prop_keys[index];
7357 if (key instanceof AST_Node) return prop;
7358 if (drop_keys.has(key)) {
7359 var mapped = drop_keys.get(key);
7360 if (!mapped) return prop;
7361 if (mapped === prop) return prop_map.get(key) || List.skip;
7362 } else if (node.rest) {
7363 return prop;
7364 }
7365 var trimmed = prop.value.drop_side_effect_free(compressor);
7366 if (trimmed) {
7367 prop.value = trimmed;
7368 return prop;
7369 }
7370 return retain_key(prop) ? make_node(AST_ObjectKeyVal, prop, {
7371 key: prop.key,
7372 value: make_node(AST_Number, prop, { value: 0 }),
7373 }) : List.skip;
7374 });
7375 if (value && !node.rest) switch (properties.length) {
7376 case 0:
7377 if (value.may_throw_on_access(compressor, true)) break;
7378 if (drop) value = value.drop_side_effect_free(compressor);
7379 return null;
7380 case 1:
7381 if (!drop) break;
7382 var prop = properties[0];
7383 if (prop.key instanceof AST_Node) break;
7384 if (prop.value.has_side_effects(compressor)) break;
7385 if (value.has_side_effects(compressor) && prop.value.match_symbol(function(node) {
7386 return node instanceof AST_PropAccess;
7387 })) break;
7388 value = make_node(AST_Sub, node, {
7389 expression: value,
7390 property: make_node_from_constant(prop.key, prop),
7391 });
7392 return prop.value;
7393 }
7394 node.properties = properties;
7395 return node;
7396 }
7397 if (node instanceof AST_Hole) {
7398 node = null;
7399 } else {
7400 node = process(node);
7401 }
7402 if (!node && drop && value) value = value.drop_side_effect_free(compressor);
7403 return node;
7404 });
7405 return {
7406 name: node.transform(trimmer),
7407 value: value,
7408 };
7409
7410 function retain_key(prop) {
7411 return prop.key instanceof AST_Node && prop.key.has_side_effects(compressor);
7412 }
7413
7414 function retain_lhs(node) {
7415 if (node instanceof AST_DefaultValue) return retain_lhs(node.name);
7416 if (node instanceof AST_Destructured) {
7417 if (value === null) {
7418 value = make_node(AST_Number, node, { value: 0 });
7419 } else if (value && (value.tail_node().write_only === true
7420 || value.may_throw_on_access(compressor, true))) {
7421 value = make_node(AST_Array, node, {
7422 elements: value instanceof AST_Sequence ? value.expressions : [ value ],
7423 });
7424 }
7425 return make_node(AST_DestructuredObject, node, { properties: [] });
7426 }
7427 node.unused = null;
7428 return node;
7429 }
7430 }
7431 });
7432
7433 AST_Scope.DEFMETHOD("hoist_declarations", function(compressor) {
7434 if (compressor.has_directive("use asm")) return;
7435 var hoist_funs = compressor.option("hoist_funs");
7436 var hoist_vars = compressor.option("hoist_vars");
7437 var self = this;
7438 if (hoist_vars) {
7439 // let's count var_decl first, we seem to waste a lot of
7440 // space if we hoist `var` when there's only one.
7441 var var_decl = 0;
7442 self.walk(new TreeWalker(function(node) {
7443 if (var_decl > 1) return true;
7444 if (node instanceof AST_ExportDeclaration) return true;
7445 if (node instanceof AST_Scope && node !== self) return true;
7446 if (node instanceof AST_Var) {
7447 var_decl++;
7448 return true;
7449 }
7450 }));
7451 if (var_decl <= 1) hoist_vars = false;
7452 }
7453 if (!hoist_funs && !hoist_vars) return;
7454 var consts = new Dictionary();
7455 var dirs = [];
7456 var hoisted = [];
7457 var vars = new Dictionary(), vars_found = 0;
7458 var tt = new TreeTransformer(function(node, descend, in_list) {
7459 if (node === self) return;
7460 if (node instanceof AST_Directive) {
7461 dirs.push(node);
7462 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
7463 }
7464 if (node instanceof AST_LambdaDefinition) {
7465 if (!hoist_funs) return node;
7466 var p = tt.parent();
7467 if (p instanceof AST_ExportDeclaration) return node;
7468 if (p instanceof AST_ExportDefault) return node;
7469 if (p !== self && compressor.has_directive("use strict")) return node;
7470 hoisted.push(node);
7471 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
7472 }
7473 if (node instanceof AST_Var) {
7474 if (!hoist_vars) return node;
7475 var p = tt.parent();
7476 if (p instanceof AST_ExportDeclaration) return node;
7477 if (!all(node.definitions, function(defn) {
7478 var sym = defn.name;
7479 return sym instanceof AST_SymbolVar
7480 && !consts.has(sym.name)
7481 && self.find_variable(sym.name) === sym.definition();
7482 })) return node;
7483 node.definitions.forEach(function(def) {
7484 vars.set(def.name.name, def);
7485 ++vars_found;
7486 });
7487 var seq = node.to_assignments();
7488 if (p instanceof AST_ForEnumeration && p.init === node) {
7489 if (seq) return seq;
7490 var def = node.definitions[0].name;
7491 return make_node(AST_SymbolRef, def, def);
7492 }
7493 if (p instanceof AST_For && p.init === node) return seq;
7494 if (!seq) return in_list ? List.skip : make_node(AST_EmptyStatement, node);
7495 return make_node(AST_SimpleStatement, node, { body: seq });
7496 }
7497 if (node instanceof AST_Scope) return node;
7498 if (node instanceof AST_SymbolConst) {
7499 consts.set(node.name, true);
7500 return node;
7501 }
7502 });
7503 self.transform(tt);
7504 if (vars_found > 0) {
7505 // collect only vars which don't show up in self's arguments list
7506 var defs = [];
7507 if (self instanceof AST_Lambda) self.each_argname(function(argname) {
7508 vars.del(argname.name);
7509 });
7510 vars.each(function(def, name) {
7511 def = def.clone();
7512 def.value = null;
7513 defs.push(def);
7514 vars.set(name, def);
7515 });
7516 if (defs.length > 0) {
7517 // try to merge in assignments
7518 insert_vars(self.body);
7519 defs = make_node(AST_Var, self, { definitions: defs });
7520 hoisted.push(defs);
7521 }
7522 }
7523 self.body = dirs.concat(hoisted, self.body);
7524
7525 function insert_vars(body) {
7526 while (body.length) {
7527 var stat = body[0];
7528 if (stat instanceof AST_SimpleStatement) {
7529 var expr = stat.body, sym, assign;
7530 if (expr instanceof AST_Assign
7531 && expr.operator == "="
7532 && (sym = expr.left) instanceof AST_Symbol
7533 && vars.has(sym.name)) {
7534 var def = vars.get(sym.name);
7535 if (def.value) break;
7536 var value = expr.right;
7537 if (value instanceof AST_Sequence) value = value.clone();
7538 def.value = value;
7539 remove(defs, def);
7540 defs.push(def);
7541 body.shift();
7542 continue;
7543 }
7544 if (expr instanceof AST_Sequence
7545 && (assign = expr.expressions[0]) instanceof AST_Assign
7546 && assign.operator == "="
7547 && (sym = assign.left) instanceof AST_Symbol
7548 && vars.has(sym.name)) {
7549 var def = vars.get(sym.name);
7550 if (def.value) break;
7551 def.value = assign.right;
7552 remove(defs, def);
7553 defs.push(def);
7554 stat.body = make_sequence(expr, expr.expressions.slice(1));
7555 continue;
7556 }
7557 }
7558 if (stat instanceof AST_EmptyStatement) {
7559 body.shift();
7560 continue;
7561 }
7562 if (stat instanceof AST_BlockStatement && !insert_vars(stat.body)) {
7563 body.shift();
7564 continue;
7565 }
7566 break;
7567 }
7568 return body.length;
7569 }
7570 });
7571
7572 function scan_local_returns(fn, transform) {
7573 fn.walk(new TreeWalker(function(node) {
7574 if (node instanceof AST_Return) {
7575 transform(node);
7576 return true;
7577 }
7578 if (node instanceof AST_Scope && node !== fn) return true;
7579 }));
7580 }
7581
7582 function map_bool_returns(fn) {
7583 var map = Object.create(null);
7584 scan_local_returns(fn, function(node) {
7585 var value = node.value;
7586 if (value) value = value.tail_node();
7587 if (value instanceof AST_SymbolRef) {
7588 var id = value.definition().id;
7589 map[id] = (map[id] || 0) + 1;
7590 }
7591 });
7592 return map;
7593 }
7594
7595 function all_bool(def, bool_returns, compressor) {
7596 return def.bool_fn + (bool_returns[def.id] || 0) === def.references.length - def.replaced
7597 && !compressor.exposed(def);
7598 }
7599
7600 function process_boolean_returns(fn, compressor) {
7601 scan_local_returns(fn, function(node) {
7602 node.in_bool = true;
7603 var value = node.value;
7604 if (value) {
7605 var ev = fuzzy_eval(compressor, value);
7606 if (!ev) {
7607 value = value.drop_side_effect_free(compressor);
7608 node.value = value ? make_sequence(node.value, [
7609 value,
7610 make_node(AST_Number, node.value, { value: 0 }),
7611 ]) : null;
7612 } else if (!(ev instanceof AST_Node)) {
7613 value = value.drop_side_effect_free(compressor);
7614 node.value = value ? make_sequence(node.value, [
7615 value,
7616 make_node(AST_Number, node.value, { value: 1 }),
7617 ]) : make_node(AST_Number, node.value, { value: 1 });
7618 }
7619 }
7620 });
7621 }
7622
7623 AST_Scope.DEFMETHOD("process_boolean_returns", noop);
7624 AST_Defun.DEFMETHOD("process_boolean_returns", function(compressor) {
7625 if (!compressor.option("booleans")) return;
7626 var bool_returns = map_bool_returns(this);
7627 if (!all_bool(this.name.definition(), bool_returns, compressor)) return;
7628 if (compressor.parent() instanceof AST_ExportDefault) return;
7629 process_boolean_returns(this, compressor);
7630 });
7631 AST_Function.DEFMETHOD("process_boolean_returns", function(compressor) {
7632 if (!compressor.option("booleans")) return;
7633 var bool_returns = map_bool_returns(this);
7634 if (this.name && !all_bool(this.name.definition(), bool_returns, compressor)) return;
7635 var parent = compressor.parent();
7636 if (parent instanceof AST_Assign) {
7637 if (parent.operator != "=") return;
7638 var sym = parent.left;
7639 if (!(sym instanceof AST_SymbolRef)) return;
7640 if (!all_bool(sym.definition(), bool_returns, compressor)) return;
7641 } else if (parent instanceof AST_Call && parent.expression !== this) {
7642 var exp = parent.expression;
7643 if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
7644 if (!(exp instanceof AST_Lambda)) return;
7645 if (exp.uses_arguments || exp.pinned()) return;
7646 var sym = exp.argnames[parent.args.indexOf(this)];
7647 if (sym instanceof AST_DefaultValue) sym = sym.name;
7648 if (sym instanceof AST_SymbolFunarg && !all_bool(sym.definition(), bool_returns, compressor)) return;
7649 } else if (parent.TYPE == "Call") {
7650 compressor.pop();
7651 var in_bool = compressor.in_boolean_context();
7652 compressor.push(this);
7653 if (!in_bool) return;
7654 } else return;
7655 process_boolean_returns(this, compressor);
7656 });
7657
7658 AST_BlockScope.DEFMETHOD("var_names", function() {
7659 var var_names = this._var_names;
7660 if (!var_names) {
7661 this._var_names = var_names = new Dictionary();
7662 this.enclosed.forEach(function(def) {
7663 var_names.set(def.name, true);
7664 });
7665 this.variables.each(function(def, name) {
7666 var_names.set(name, true);
7667 });
7668 }
7669 return var_names;
7670 });
7671
7672 AST_Scope.DEFMETHOD("make_var", function(type, orig, prefix) {
7673 var scopes = [ this ];
7674 if (orig instanceof AST_SymbolDeclaration) orig.definition().references.forEach(function(ref) {
7675 var s = ref.scope;
7676 if (member(s, scopes)) return;
7677 do {
7678 push_uniq(scopes, s);
7679 s = s.parent_scope;
7680 } while (s && s !== this);
7681 });
7682 prefix = prefix.replace(/(?:^[^a-z_$]|[^a-z0-9_$])/ig, "_");
7683 var name = prefix;
7684 for (var i = 0; !all(scopes, function(scope) {
7685 return !scope.var_names().has(name);
7686 }); i++) name = prefix + "$" + i;
7687 var sym = make_node(type, orig, {
7688 name: name,
7689 scope: this,
7690 });
7691 var def = this.def_variable(sym);
7692 scopes.forEach(function(scope) {
7693 scope.enclosed.push(def);
7694 scope.var_names().set(name, true);
7695 });
7696 return sym;
7697 });
7698
7699 AST_Scope.DEFMETHOD("hoist_properties", function(compressor) {
7700 if (!compressor.option("hoist_props") || compressor.has_directive("use asm")) return;
7701 var self = this;
7702 var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false;
7703 var defs_by_id = Object.create(null);
7704 self.transform(new TreeTransformer(function(node, descend) {
7705 if (node instanceof AST_Assign) {
7706 if (node.operator != "=") return;
7707 if (!node.write_only) return;
7708 if (!can_hoist(node.left, node.right, 1)) return;
7709 descend(node, this);
7710 var defs = new Dictionary();
7711 var assignments = [];
7712 var decls = [];
7713 node.right.properties.forEach(function(prop) {
7714 var decl = make_sym(AST_SymbolVar, node.left, prop.key);
7715 decls.push(make_node(AST_VarDef, node, {
7716 name: decl,
7717 value: null,
7718 }));
7719 var sym = make_node(AST_SymbolRef, node, {
7720 name: decl.name,
7721 scope: self,
7722 thedef: decl.definition(),
7723 });
7724 sym.reference();
7725 assignments.push(make_node(AST_Assign, node, {
7726 operator: "=",
7727 left: sym,
7728 right: prop.value,
7729 }));
7730 });
7731 defs.value = node.right;
7732 defs_by_id[node.left.definition().id] = defs;
7733 self.body.splice(self.body.indexOf(this.stack[1]) + 1, 0, make_node(AST_Var, node, {
7734 definitions: decls,
7735 }));
7736 return make_sequence(node, assignments);
7737 }
7738 if (node instanceof AST_Scope) return node === self ? undefined : node;
7739 if (node instanceof AST_VarDef) {
7740 if (!can_hoist(node.name, node.value, 0)) return;
7741 descend(node, this);
7742 var defs = new Dictionary();
7743 var var_defs = [];
7744 var decl = node.clone();
7745 decl.value = node.name instanceof AST_SymbolConst ? make_node(AST_Number, node, { value: 0 }) : null;
7746 var_defs.push(decl);
7747 node.value.properties.forEach(function(prop) {
7748 var_defs.push(make_node(AST_VarDef, node, {
7749 name: make_sym(node.name.CTOR, node.name, prop.key),
7750 value: prop.value,
7751 }));
7752 });
7753 defs.value = node.value;
7754 defs_by_id[node.name.definition().id] = defs;
7755 return List.splice(var_defs);
7756 }
7757
7758 function make_sym(type, sym, key) {
7759 var new_var = self.make_var(type, sym, sym.name + "_" + key);
7760 defs.set(key, new_var.definition());
7761 return new_var;
7762 }
7763 }));
7764 self.transform(new TreeTransformer(function(node, descend) {
7765 if (node instanceof AST_PropAccess) {
7766 if (!(node.expression instanceof AST_SymbolRef)) return;
7767 var defs = defs_by_id[node.expression.definition().id];
7768 if (!defs) return;
7769 if (node.expression.fixed_value() !== defs.value) return;
7770 var def = defs.get(node.get_property());
7771 var sym = make_node(AST_SymbolRef, node, {
7772 name: def.name,
7773 scope: node.expression.scope,
7774 thedef: def,
7775 });
7776 sym.reference();
7777 return sym;
7778 }
7779 if (node instanceof AST_SymbolRef) {
7780 var defs = defs_by_id[node.definition().id];
7781 if (!defs) return;
7782 if (node.fixed_value() !== defs.value) return;
7783 return make_node(AST_Object, node, { properties: [] });
7784 }
7785 }));
7786
7787 function can_hoist(sym, right, count) {
7788 if (!(sym instanceof AST_Symbol)) return;
7789 var def = sym.definition();
7790 if (def.assignments != count) return;
7791 if (def.references.length - def.replaced == count) return;
7792 if (def.single_use) return;
7793 if (top_retain(def)) return;
7794 if (sym.fixed_value() !== right) return;
7795 var fixed = sym.fixed || def.fixed;
7796 if (fixed.direct_access) return;
7797 if (fixed.escaped && fixed.escaped.depth == 1) return;
7798 return right instanceof AST_Object
7799 && right.properties.length > 0
7800 && all(right.properties, can_hoist_property)
7801 && can_drop_symbol(sym, compressor);
7802 }
7803 });
7804
7805 function fn_name_unused(fn, compressor) {
7806 if (!fn.name || !compressor.option("ie")) return true;
7807 var def = fn.name.definition();
7808 if (compressor.exposed(def)) return false;
7809 return all(def.references, function(sym) {
7810 return !(sym instanceof AST_SymbolRef);
7811 });
7812 }
7813
7814 // drop_side_effect_free()
7815 // remove side-effect-free parts which only affects return value
7816 (function(def) {
7817 // Drop side-effect-free elements from an array of expressions.
7818 // Returns an array of expressions with side-effects or null
7819 // if all elements were dropped. Note: original array may be
7820 // returned if nothing changed.
7821 function trim(nodes, compressor, first_in_statement, spread) {
7822 var len = nodes.length;
7823 var ret = [], changed = false;
7824 for (var i = 0; i < len; i++) {
7825 var node = nodes[i];
7826 var trimmed;
7827 if (spread && node instanceof AST_Spread) {
7828 trimmed = spread(node, compressor, first_in_statement);
7829 } else {
7830 trimmed = node.drop_side_effect_free(compressor, first_in_statement);
7831 }
7832 if (trimmed !== node) changed = true;
7833 if (trimmed) {
7834 ret.push(trimmed);
7835 first_in_statement = false;
7836 }
7837 }
7838 return ret.length ? changed ? ret : nodes : null;
7839 }
7840 function array_spread(node, compressor, first_in_statement) {
7841 var exp = node.expression;
7842 if (!exp.is_string(compressor)) return node;
7843 return exp.drop_side_effect_free(compressor, first_in_statement);
7844 }
7845 function convert_spread(node) {
7846 return node instanceof AST_Spread ? make_node(AST_Array, node, {
7847 elements: [ node ]
7848 }) : node;
7849 }
7850 def(AST_Node, return_this);
7851 def(AST_Accessor, return_null);
7852 def(AST_Array, function(compressor, first_in_statement) {
7853 var values = trim(this.elements, compressor, first_in_statement, array_spread);
7854 if (!values) return null;
7855 if (values === this.elements && all(values, function(node) {
7856 return node instanceof AST_Spread;
7857 })) return this;
7858 return make_sequence(this, values.map(convert_spread));
7859 });
7860 def(AST_Assign, function(compressor) {
7861 var left = this.left;
7862 if (left instanceof AST_PropAccess) {
7863 var expr = left.expression;
7864 if (expr.may_throw_on_access(compressor, true)) return this;
7865 if (compressor.has_directive("use strict") && expr.is_constant()) return this;
7866 }
7867 if (left.has_side_effects(compressor)) return this;
7868 if (lazy_op[this.operator.slice(0, -1)]) return this;
7869 this.write_only = true;
7870 if (!root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) return this;
7871 return this.right.drop_side_effect_free(compressor);
7872 });
7873 def(AST_Await, function(compressor) {
7874 if (!compressor.option("awaits")) return this;
7875 var exp = this.expression;
7876 if (!is_primitive(compressor, exp)) return this;
7877 var node = this.clone();
7878 node.expression = exp.drop_side_effect_free(compressor) || make_node(AST_Number, this, { value: 0 });
7879 return node;
7880 });
7881 def(AST_Binary, function(compressor, first_in_statement) {
7882 var left = this.left;
7883 var right = this.right;
7884 var op = this.operator;
7885 if (op == "in" && !is_object(right)) {
7886 var lhs = left.drop_side_effect_free(compressor, first_in_statement);
7887 if (lhs === left) return this;
7888 var node = this.clone();
7889 node.left = lhs || make_node(AST_Number, left, { value: 0 });
7890 return node;
7891 }
7892 var rhs = right.drop_side_effect_free(compressor, first_in_statement);
7893 if (!rhs) return left.drop_side_effect_free(compressor, first_in_statement);
7894 if (lazy_op[op] && rhs.has_side_effects(compressor)) {
7895 var node = this;
7896 if (rhs !== right) {
7897 node = node.clone();
7898 node.right = rhs.drop_side_effect_free(compressor);
7899 }
7900 if (op == "??") return node;
7901 var negated = make_node(AST_Binary, this, {
7902 operator: op == "&&" ? "||" : "&&",
7903 left: left.negate(compressor, first_in_statement),
7904 right: node.right,
7905 });
7906 return first_in_statement ? best_of_statement(node, negated) : best_of_expression(node, negated);
7907 }
7908 var lhs = left.drop_side_effect_free(compressor, first_in_statement);
7909 if (!lhs) return rhs;
7910 rhs = rhs.drop_side_effect_free(compressor);
7911 if (!rhs) return lhs;
7912 return make_sequence(this, [ lhs, rhs ]);
7913 });
7914 function drop_returns(compressor, exp) {
7915 var arrow = is_arrow(exp);
7916 var async = is_async(exp);
7917 var drop_body = false;
7918 if (arrow && compressor.option("arrows")) {
7919 if (!exp.value) {
7920 drop_body = true;
7921 } else if (!async || is_primitive(compressor, exp.value)) {
7922 exp.value = exp.value.drop_side_effect_free(compressor);
7923 }
7924 } else if (exp instanceof AST_AsyncFunction || exp instanceof AST_Function) {
7925 if (exp.name) {
7926 var def = exp.name.definition();
7927 drop_body = def.references.length == def.replaced;
7928 } else {
7929 drop_body = true;
7930 }
7931 }
7932 if (drop_body) {
7933 exp.process_expression(false, function(node) {
7934 var value = node.value;
7935 if (value) {
7936 if (async && !is_primitive(compressor, value)) return node;
7937 value = value.drop_side_effect_free(compressor, true);
7938 }
7939 if (!value) return make_node(AST_EmptyStatement, node);
7940 return make_node(AST_SimpleStatement, node, { body: value });
7941 });
7942 scan_local_returns(exp, function(node) {
7943 var value = node.value;
7944 if (value) {
7945 if (async && !is_primitive(compressor, value)) return;
7946 node.value = value.drop_side_effect_free(compressor);
7947 }
7948 });
7949 }
7950 if (async && compressor.option("awaits")) {
7951 if (drop_body) exp.process_expression("awaits", function(node) {
7952 var body = node.body;
7953 if (body instanceof AST_Await) {
7954 if (is_primitive(compressor, body.expression)) {
7955 body = body.expression.drop_side_effect_free(compressor, true);
7956 if (!body) return make_node(AST_EmptyStatement, node);
7957 node.body = body;
7958 }
7959 } else if (body instanceof AST_Sequence) {
7960 var exprs = body.expressions;
7961 for (var i = exprs.length; --i >= 0;) {
7962 var tail = exprs[i];
7963 if (!(tail instanceof AST_Await)) break;
7964 if (!is_primitive(compressor, tail.expression)) break;
7965 if (exprs[i] = tail.expression.drop_side_effect_free(compressor)) break;
7966 }
7967 switch (i) {
7968 case -1:
7969 return make_node(AST_EmptyStatement, node);
7970 case 0:
7971 node.body = exprs[0];
7972 break;
7973 default:
7974 exprs.length = i + 1;
7975 break;
7976 }
7977 }
7978 return node;
7979 });
7980 var abort = !drop_body && exp.name || arrow && exp.value && !is_primitive(compressor, exp.value);
7981 var tw = new TreeWalker(function(node) {
7982 if (abort) return true;
7983 if (tw.parent() === exp && node.may_throw(compressor)) return abort = true;
7984 if (node instanceof AST_Await) return abort = true;
7985 if (node instanceof AST_ForAwaitOf) return abort = true;
7986 if (node instanceof AST_Return) {
7987 if (node.value && !is_primitive(compressor, node.value)) return abort = true;
7988 return;
7989 }
7990 if (node instanceof AST_Scope && node !== exp) return true;
7991 });
7992 exp.walk(tw);
7993 if (!abort) {
7994 var ctor;
7995 switch (exp.CTOR) {
7996 case AST_AsyncArrow:
7997 ctor = AST_Arrow;
7998 break;
7999 case AST_AsyncFunction:
8000 ctor = AST_Function;
8001 break;
8002 case AST_AsyncGeneratorFunction:
8003 ctor = AST_GeneratorFunction;
8004 break;
8005 }
8006 return make_node(ctor, exp, exp);
8007 }
8008 }
8009 return drop_body && exp.clone();
8010 }
8011 def(AST_Call, function(compressor, first_in_statement) {
8012 var self = this;
8013 if (self.is_expr_pure(compressor)) {
8014 if (self.pure) AST_Node.warn("Dropping __PURE__ call [{file}:{line},{col}]", self.start);
8015 var args = trim(self.args, compressor, first_in_statement, array_spread);
8016 return args && make_sequence(self, args.map(convert_spread));
8017 }
8018 var exp = self.expression;
8019 if (self.is_call_pure(compressor)) {
8020 var exprs = self.args.slice();
8021 exprs.unshift(exp.expression);
8022 exprs = trim(exprs, compressor, first_in_statement, array_spread);
8023 return exprs && make_sequence(self, exprs.map(convert_spread));
8024 }
8025 if (compressor.option("yields") && is_generator(exp)) {
8026 var call = self.clone();
8027 call.expression = make_node(AST_Function, exp, exp);
8028 call.expression.body = [];
8029 var opt = call.transform(compressor);
8030 if (opt !== call) return opt.drop_side_effect_free(compressor, first_in_statement);
8031 }
8032 var dropped = drop_returns(compressor, exp);
8033 if (dropped) {
8034 // always shallow clone to ensure stripping of negated IIFEs
8035 self = self.clone();
8036 self.expression = dropped;
8037 // avoid extraneous traversal
8038 if (exp._squeezed) self.expression._squeezed = true;
8039 }
8040 if (self instanceof AST_New) {
8041 var fn = exp;
8042 if (fn instanceof AST_SymbolRef) fn = fn.fixed_value();
8043 if (fn instanceof AST_Lambda) {
8044 if (assign_this_only(fn, compressor)) {
8045 var exprs = self.args.slice();
8046 exprs.unshift(exp);
8047 exprs = trim(exprs, compressor, first_in_statement, array_spread);
8048 return exprs && make_sequence(self, exprs.map(convert_spread));
8049 }
8050 if (!fn.contains_this()) self = make_node(AST_Call, self, self);
8051 }
8052 }
8053 self.call_only = true;
8054 return self;
8055 });
8056 function assign_this_only(fn, compressor) {
8057 fn.new = true;
8058 var result = all(fn.body, function(stat) {
8059 return !stat.has_side_effects(compressor);
8060 }) && all(fn.argnames, function(argname) {
8061 return !argname.match_symbol(return_false);
8062 }) && !(fn.rest && fn.rest.match_symbol(return_false));
8063 fn.new = false;
8064 return result;
8065 }
8066 function drop_class(self, compressor, first_in_statement) {
8067 var exprs = [], values = [];
8068 var props = self.properties;
8069 for (var i = 0; i < props.length; i++) {
8070 var prop = props[i];
8071 if (prop.key instanceof AST_Node) exprs.push(prop.key);
8072 if (prop.static && prop.value
8073 && prop instanceof AST_ClassField
8074 && prop.value.has_side_effects(compressor)) {
8075 if (prop.value.contains_this()) return self;
8076 values.push(prop.value);
8077 }
8078 }
8079 var base = self.extends;
8080 if (base) {
8081 if (base instanceof AST_SymbolRef) base = base.fixed_value();
8082 base = !safe_for_extends(base);
8083 if (!base) exprs.unshift(self.extends);
8084 }
8085 exprs = trim(exprs, compressor, first_in_statement);
8086 if (exprs) first_in_statement = false;
8087 values = trim(values, compressor, first_in_statement);
8088 if (!exprs) {
8089 if (!base && !values) return null;
8090 exprs = [];
8091 }
8092 if (base) {
8093 var node = to_class_expr(self, true);
8094 node.properties = [];
8095 if (exprs.length) node.properties.push(make_node(AST_ClassMethod, self, {
8096 key: make_sequence(self, exprs),
8097 value: make_node(AST_Function, self, {
8098 argnames: [],
8099 body: [],
8100 }).init_vars(node),
8101 }));
8102 exprs = [ node ];
8103 }
8104 if (values) exprs.push(make_node(AST_Call, self, {
8105 expression: make_node(AST_Arrow, self, {
8106 argnames: [],
8107 body: [],
8108 value: make_sequence(self, values),
8109 }).init_vars(self.parent_scope),
8110 args: [],
8111 }));
8112 return make_sequence(self, exprs);
8113 }
8114 def(AST_ClassExpression, function(compressor, first_in_statement) {
8115 var self = this;
8116 var name = self.name;
8117 if (name && name.fixed_value() !== self && name.definition().references.length > 0) return self;
8118 return drop_class(self, compressor, first_in_statement);
8119 });
8120 def(AST_Conditional, function(compressor) {
8121 var consequent = this.consequent.drop_side_effect_free(compressor);
8122 var alternative = this.alternative.drop_side_effect_free(compressor);
8123 if (consequent === this.consequent && alternative === this.alternative) return this;
8124 var exprs;
8125 if (compressor.option("ie")) {
8126 exprs = [];
8127 if (consequent instanceof AST_Function) {
8128 exprs.push(consequent);
8129 consequent = null;
8130 }
8131 if (alternative instanceof AST_Function) {
8132 exprs.push(alternative);
8133 alternative = null;
8134 }
8135 }
8136 var node;
8137 if (!consequent) {
8138 node = alternative ? make_node(AST_Binary, this, {
8139 operator: "||",
8140 left: this.condition,
8141 right: alternative
8142 }) : this.condition.drop_side_effect_free(compressor);
8143 } else if (!alternative) {
8144 node = make_node(AST_Binary, this, {
8145 operator: "&&",
8146 left: this.condition,
8147 right: consequent
8148 });
8149 } else {
8150 node = this.clone();
8151 node.consequent = consequent;
8152 node.alternative = alternative;
8153 }
8154 if (!compressor.option("ie")) return node;
8155 if (node) exprs.push(node);
8156 return exprs.length == 0 ? null : make_sequence(this, exprs);
8157 });
8158 def(AST_Constant, return_null);
8159 def(AST_DefClass, function(compressor, first_in_statement) {
8160 return drop_class(this, compressor, first_in_statement);
8161 });
8162 def(AST_Dot, function(compressor, first_in_statement) {
8163 var expr = this.expression;
8164 if (!this.optional && expr.may_throw_on_access(compressor)) return this;
8165 return expr.drop_side_effect_free(compressor, first_in_statement);
8166 });
8167 def(AST_Function, function(compressor) {
8168 return fn_name_unused(this, compressor) ? null : this;
8169 });
8170 def(AST_LambdaExpression, return_null);
8171 def(AST_Object, function(compressor, first_in_statement) {
8172 var exprs = [];
8173 this.properties.forEach(function(prop) {
8174 if (prop instanceof AST_Spread) {
8175 exprs.push(prop);
8176 } else {
8177 if (prop.key instanceof AST_Node) exprs.push(prop.key);
8178 exprs.push(prop.value);
8179 }
8180 });
8181 var values = trim(exprs, compressor, first_in_statement, function(node, compressor, first_in_statement) {
8182 var exp = node.expression;
8183 return spread_side_effects(exp) ? node : exp.drop_side_effect_free(compressor, first_in_statement);
8184 });
8185 if (!values) return null;
8186 if (values === exprs && !all(values, function(node) {
8187 return !(node instanceof AST_Spread);
8188 })) return this;
8189 return make_sequence(this, values.map(function(node) {
8190 return node instanceof AST_Spread ? make_node(AST_Object, node, {
8191 properties: [ node ],
8192 }) : node;
8193 }));
8194 });
8195 def(AST_ObjectIdentity, return_null);
8196 def(AST_Sequence, function(compressor, first_in_statement) {
8197 var expressions = trim(this.expressions, compressor, first_in_statement);
8198 if (!expressions) return null;
8199 var end = expressions.length - 1;
8200 var last = expressions[end];
8201 if (compressor.option("awaits") && end > 0 && last instanceof AST_Await && last.expression.is_constant()) {
8202 expressions = expressions.slice(0, -1);
8203 end--;
8204 last.expression = expressions[end];
8205 expressions[end] = last;
8206 }
8207 var assign, cond, lhs;
8208 if (compressor.option("conditionals")
8209 && end > 0
8210 && (assign = expressions[end - 1]) instanceof AST_Assign
8211 && assign.operator == "="
8212 && (lhs = assign.left) instanceof AST_SymbolRef
8213 && (cond = to_conditional_assignment(compressor, lhs.definition(), assign.right, last))) {
8214 assign = assign.clone();
8215 assign.right = cond;
8216 expressions = expressions.slice(0, -2);
8217 expressions.push(assign.drop_side_effect_free(compressor, first_in_statement));
8218 }
8219 return expressions === this.expressions ? this : make_sequence(this, expressions);
8220 });
8221 def(AST_Sub, function(compressor, first_in_statement) {
8222 var expr = this.expression;
8223 var prop = this.property;
8224 if (expr.may_throw_on_access(compressor)) {
8225 if (!this.optional) return this;
8226 if (prop.has_side_effects(compressor)) {
8227 prop = prop.drop_side_effect_free(compressor);
8228 if (!prop) return expr.drop_side_effect_free(compressor, first_in_statement);
8229 var node = this.clone();
8230 node.property = prop;
8231 return node;
8232 }
8233 }
8234 expr = expr.drop_side_effect_free(compressor, first_in_statement);
8235 if (!expr) return prop.drop_side_effect_free(compressor, first_in_statement);
8236 prop = prop.drop_side_effect_free(compressor);
8237 if (!prop) return expr;
8238 return make_sequence(this, [ expr, prop ]);
8239 });
8240 def(AST_SymbolRef, function(compressor) {
8241 return this.is_declared(compressor) && can_drop_symbol(this, compressor) ? null : this;
8242 });
8243 def(AST_Template, function(compressor, first_in_statement) {
8244 var self = this;
8245 if (self.is_expr_pure(compressor)) {
8246 var expressions = self.expressions;
8247 if (expressions.length == 0) return null;
8248 return make_sequence(self, expressions).drop_side_effect_free(compressor, first_in_statement);
8249 }
8250 var tag = self.tag;
8251 var dropped = drop_returns(compressor, tag);
8252 if (dropped) {
8253 // always shallow clone to signal internal changes
8254 self = self.clone();
8255 self.tag = dropped;
8256 // avoid extraneous traversal
8257 if (tag._squeezed) self.tag._squeezed = true;
8258 }
8259 return self;
8260 });
8261 def(AST_Unary, function(compressor, first_in_statement) {
8262 var exp = this.expression;
8263 if (unary_side_effects[this.operator]) {
8264 this.write_only = !exp.has_side_effects(compressor);
8265 return this;
8266 }
8267 if (this.operator == "typeof" && exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor)) {
8268 return null;
8269 }
8270 var node = exp.drop_side_effect_free(compressor, first_in_statement);
8271 if (first_in_statement && node && is_iife_call(node)) {
8272 if (node === exp && this.operator == "!") return this;
8273 return node.negate(compressor, first_in_statement);
8274 }
8275 return node;
8276 });
8277 })(function(node, func) {
8278 node.DEFMETHOD("drop_side_effect_free", func);
8279 });
8280
8281 OPT(AST_SimpleStatement, function(self, compressor) {
8282 if (compressor.option("side_effects")) {
8283 var body = self.body;
8284 var node = body.drop_side_effect_free(compressor, true);
8285 if (!node) {
8286 AST_Node.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
8287 return make_node(AST_EmptyStatement, self);
8288 }
8289 if (node !== body) {
8290 return make_node(AST_SimpleStatement, self, { body: node });
8291 }
8292 }
8293 return self;
8294 });
8295
8296 OPT(AST_While, function(self, compressor) {
8297 return compressor.option("loops") ? make_node(AST_For, self, self).optimize(compressor) : self;
8298 });
8299
8300 function has_loop_control(loop, parent, type) {
8301 if (!type) type = AST_LoopControl;
8302 var found = false;
8303 var tw = new TreeWalker(function(node) {
8304 if (found || node instanceof AST_Scope) return true;
8305 if (node instanceof type && tw.loopcontrol_target(node) === loop) {
8306 return found = true;
8307 }
8308 });
8309 if (parent instanceof AST_LabeledStatement) tw.push(parent);
8310 tw.push(loop);
8311 loop.body.walk(tw);
8312 return found;
8313 }
8314
8315 OPT(AST_Do, function(self, compressor) {
8316 if (!compressor.option("loops")) return self;
8317 var cond = fuzzy_eval(compressor, self.condition);
8318 if (!(cond instanceof AST_Node)) {
8319 if (cond && !has_loop_control(self, compressor.parent(), AST_Continue)) return make_node(AST_For, self, {
8320 body: make_node(AST_BlockStatement, self.body, {
8321 body: [
8322 self.body,
8323 make_node(AST_SimpleStatement, self.condition, {
8324 body: self.condition
8325 }),
8326 ]
8327 })
8328 }).optimize(compressor);
8329 if (!has_loop_control(self, compressor.parent())) return make_node(AST_BlockStatement, self.body, {
8330 body: [
8331 self.body,
8332 make_node(AST_SimpleStatement, self.condition, {
8333 body: self.condition
8334 }),
8335 ]
8336 }).optimize(compressor);
8337 }
8338 if (self.body instanceof AST_BlockStatement && !has_loop_control(self, compressor.parent(), AST_Continue)) {
8339 var body = self.body.body;
8340 for (var i = body.length; --i >= 0;) {
8341 var stat = body[i];
8342 if (stat instanceof AST_If
8343 && !stat.alternative
8344 && stat.body instanceof AST_Break
8345 && compressor.loopcontrol_target(stat.body) === self) {
8346 if (has_block_scope_refs(stat.condition)) break;
8347 self.condition = make_node(AST_Binary, self, {
8348 operator: "&&",
8349 left: stat.condition.negate(compressor),
8350 right: self.condition,
8351 });
8352 body.splice(i, 1);
8353 } else if (stat instanceof AST_SimpleStatement) {
8354 if (has_block_scope_refs(stat.body)) break;
8355 self.condition = make_sequence(self, [
8356 stat.body,
8357 self.condition,
8358 ]);
8359 body.splice(i, 1);
8360 } else if (!is_declaration(stat, true)) {
8361 break;
8362 }
8363 }
8364 self.body = trim_block(self.body, compressor.parent());
8365 }
8366 if (self.body instanceof AST_EmptyStatement) return make_node(AST_For, self, self).optimize(compressor);
8367 if (self.body instanceof AST_SimpleStatement) return make_node(AST_For, self, {
8368 condition: make_sequence(self.condition, [
8369 self.body.body,
8370 self.condition
8371 ]),
8372 body: make_node(AST_EmptyStatement, self)
8373 }).optimize(compressor);
8374 return self;
8375
8376 function has_block_scope_refs(node) {
8377 var found = false;
8378 node.walk(new TreeWalker(function(node) {
8379 if (found) return true;
8380 if (node instanceof AST_SymbolRef) {
8381 if (!member(node.definition(), self.enclosed)) found = true;
8382 return true;
8383 }
8384 }));
8385 return found;
8386 }
8387 });
8388
8389 function if_break_in_loop(self, compressor) {
8390 var first = first_statement(self.body);
8391 if (compressor.option("dead_code")
8392 && (first instanceof AST_Break
8393 || first instanceof AST_Continue && external_target(first)
8394 || first instanceof AST_Exit)) {
8395 var body = [];
8396 if (is_statement(self.init)) {
8397 body.push(self.init);
8398 } else if (self.init) {
8399 body.push(make_node(AST_SimpleStatement, self.init, {
8400 body: self.init
8401 }));
8402 }
8403 var retain = external_target(first) || first instanceof AST_Exit;
8404 if (self.condition && retain) {
8405 body.push(make_node(AST_If, self, {
8406 condition: self.condition,
8407 body: first,
8408 alternative: null
8409 }));
8410 } else if (self.condition) {
8411 body.push(make_node(AST_SimpleStatement, self.condition, {
8412 body: self.condition
8413 }));
8414 } else if (retain) {
8415 body.push(first);
8416 }
8417 extract_declarations_from_unreachable_code(compressor, self.body, body);
8418 return make_node(AST_BlockStatement, self, {
8419 body: body
8420 });
8421 }
8422 if (first instanceof AST_If) {
8423 var ab = first_statement(first.body);
8424 if (ab instanceof AST_Break && !external_target(ab)) {
8425 if (self.condition) {
8426 self.condition = make_node(AST_Binary, self.condition, {
8427 left: self.condition,
8428 operator: "&&",
8429 right: first.condition.negate(compressor),
8430 });
8431 } else {
8432 self.condition = first.condition.negate(compressor);
8433 }
8434 var body = as_statement_array(first.alternative);
8435 extract_declarations_from_unreachable_code(compressor, first.body, body);
8436 return drop_it(body);
8437 }
8438 ab = first_statement(first.alternative);
8439 if (ab instanceof AST_Break && !external_target(ab)) {
8440 if (self.condition) {
8441 self.condition = make_node(AST_Binary, self.condition, {
8442 left: self.condition,
8443 operator: "&&",
8444 right: first.condition,
8445 });
8446 } else {
8447 self.condition = first.condition;
8448 }
8449 var body = as_statement_array(first.body);
8450 extract_declarations_from_unreachable_code(compressor, first.alternative, body);
8451 return drop_it(body);
8452 }
8453 }
8454 return self;
8455
8456 function first_statement(body) {
8457 return body instanceof AST_BlockStatement ? body.body[0] : body;
8458 }
8459
8460 function external_target(node) {
8461 return compressor.loopcontrol_target(node) !== compressor.self();
8462 }
8463
8464 function drop_it(rest) {
8465 if (self.body instanceof AST_BlockStatement) {
8466 self.body = self.body.clone();
8467 self.body.body = rest.concat(self.body.body.slice(1));
8468 self.body = self.body.transform(compressor);
8469 } else {
8470 self.body = make_node(AST_BlockStatement, self.body, {
8471 body: rest
8472 }).transform(compressor);
8473 }
8474 return if_break_in_loop(self, compressor);
8475 }
8476 }
8477
8478 OPT(AST_For, function(self, compressor) {
8479 if (!compressor.option("loops")) return self;
8480 if (compressor.option("side_effects")) {
8481 if (self.init) self.init = self.init.drop_side_effect_free(compressor);
8482 if (self.step) self.step = self.step.drop_side_effect_free(compressor);
8483 }
8484 if (self.condition) {
8485 var cond = fuzzy_eval(compressor, self.condition);
8486 if (!cond) {
8487 if (compressor.option("dead_code")) {
8488 var body = [];
8489 if (is_statement(self.init)) {
8490 body.push(self.init);
8491 } else if (self.init) {
8492 body.push(make_node(AST_SimpleStatement, self.init, { body: self.init }));
8493 }
8494 body.push(make_node(AST_SimpleStatement, self.condition, { body: self.condition }));
8495 extract_declarations_from_unreachable_code(compressor, self.body, body);
8496 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
8497 }
8498 } else if (!(cond instanceof AST_Node)) {
8499 self.body = make_node(AST_BlockStatement, self.body, {
8500 body: [
8501 make_node(AST_SimpleStatement, self.condition, { body: self.condition }),
8502 self.body,
8503 ],
8504 });
8505 self.condition = null;
8506 }
8507 }
8508 return if_break_in_loop(self, compressor);
8509 });
8510
8511 OPT(AST_ForEnumeration, function(self, compressor) {
8512 if (compressor.option("varify") && is_lexical_definition(self.init)) {
8513 var name = self.init.definitions[0].name;
8514 if ((name instanceof AST_Destructured || name instanceof AST_SymbolLet)
8515 && !name.match_symbol(function(node) {
8516 if (node instanceof AST_SymbolDeclaration) {
8517 var def = node.definition();
8518 return !same_scope(def) || may_overlap(compressor, def);
8519 }
8520 }, true)) {
8521 self.init = to_var(self.init);
8522 }
8523 }
8524 return self;
8525 });
8526
8527 function mark_locally_defined(condition, consequent, alternative) {
8528 if (!(condition instanceof AST_Binary)) return;
8529 if (!(condition.left instanceof AST_String)) {
8530 switch (condition.operator) {
8531 case "&&":
8532 mark_locally_defined(condition.left, consequent);
8533 mark_locally_defined(condition.right, consequent);
8534 break;
8535 case "||":
8536 mark_locally_defined(negate(condition.left), alternative);
8537 mark_locally_defined(negate(condition.right), alternative);
8538 break;
8539 }
8540 return;
8541 }
8542 if (!(condition.right instanceof AST_UnaryPrefix)) return;
8543 if (condition.right.operator != "typeof") return;
8544 var sym = condition.right.expression;
8545 if (!is_undeclared_ref(sym)) return;
8546 var body;
8547 var undef = condition.left.value == "undefined";
8548 switch (condition.operator) {
8549 case "==":
8550 body = undef ? alternative : consequent;
8551 break;
8552 case "!=":
8553 body = undef ? consequent : alternative;
8554 break;
8555 default:
8556 return;
8557 }
8558 if (!body) return;
8559 var def = sym.definition();
8560 var tw = new TreeWalker(function(node) {
8561 if (node instanceof AST_Scope) {
8562 var parent = tw.parent();
8563 if (parent instanceof AST_Call && parent.expression === node) return;
8564 return true;
8565 }
8566 if (node instanceof AST_SymbolRef && node.definition() === def) node.defined = true;
8567 });
8568 body.walk(tw);
8569
8570 function negate(node) {
8571 if (!(node instanceof AST_Binary)) return;
8572 switch (node.operator) {
8573 case "==":
8574 node = node.clone();
8575 node.operator = "!=";
8576 return node;
8577 case "!=":
8578 node = node.clone();
8579 node.operator = "==";
8580 return node;
8581 }
8582 }
8583 }
8584
8585 function fuzzy_eval(compressor, node, nullish) {
8586 if (node.truthy) return true;
8587 if (node.falsy && !nullish) return false;
8588 if (node.is_truthy()) return true;
8589 return node.evaluate(compressor, true);
8590 }
8591
8592 function mark_duplicate_condition(compressor, node) {
8593 var child;
8594 var level = 0;
8595 var negated = false;
8596 var parent = compressor.self();
8597 if (!is_statement(parent)) while (true) {
8598 child = parent;
8599 parent = compressor.parent(level++);
8600 if (parent instanceof AST_Binary) {
8601 var op = parent.operator;
8602 if (!lazy_op[op]) return;
8603 var left = parent.left;
8604 if (left === child) continue;
8605 if (match(left)) switch (op) {
8606 case "&&":
8607 node[negated ? "falsy" : "truthy"] = true;
8608 break;
8609 case "||":
8610 case "??":
8611 node[negated ? "truthy" : "falsy"] = true;
8612 break;
8613 }
8614 } else if (parent instanceof AST_Conditional) {
8615 var cond = parent.condition;
8616 if (cond === child) continue;
8617 if (match(cond)) switch (child) {
8618 case parent.consequent:
8619 node[negated ? "falsy" : "truthy"] = true;
8620 break;
8621 case parent.alternative:
8622 node[negated ? "truthy" : "falsy"] = true;
8623 break;
8624 }
8625 } else if (parent instanceof AST_Exit) {
8626 break;
8627 } else if (parent instanceof AST_If) {
8628 break;
8629 } else if (parent instanceof AST_Sequence) {
8630 if (parent.expressions[0] === child) continue;
8631 } else if (parent instanceof AST_SimpleStatement) {
8632 break;
8633 }
8634 return;
8635 }
8636 while (true) {
8637 child = parent;
8638 parent = compressor.parent(level++);
8639 if (parent instanceof AST_BlockStatement) {
8640 if (parent.body[0] === child) continue;
8641 } else if (parent instanceof AST_If) {
8642 if (match(parent.condition)) switch (child) {
8643 case parent.body:
8644 node[negated ? "falsy" : "truthy"] = true;
8645 break;
8646 case parent.alternative:
8647 node[negated ? "truthy" : "falsy"] = true;
8648 break;
8649 }
8650 }
8651 return;
8652 }
8653
8654 function match(cond) {
8655 if (node.equivalent_to(cond)) return true;
8656 if (!(cond instanceof AST_UnaryPrefix)) return false;
8657 if (cond.operator != "!") return false;
8658 if (!node.equivalent_to(cond.expression)) return false;
8659 negated = true;
8660 return true;
8661 }
8662 }
8663
8664 OPT(AST_If, function(self, compressor) {
8665 if (is_empty(self.alternative)) self.alternative = null;
8666
8667 if (!compressor.option("conditionals")) return self;
8668 if (compressor.option("booleans") && !self.condition.has_side_effects(compressor)) {
8669 mark_duplicate_condition(compressor, self.condition);
8670 }
8671 // if condition can be statically determined, warn and drop
8672 // one of the blocks. note, statically determined implies
8673 // “has no side effects”; also it doesn't work for cases like
8674 // `x && true`, though it probably should.
8675 if (compressor.option("dead_code")) {
8676 var cond = fuzzy_eval(compressor, self.condition);
8677 if (!cond) {
8678 AST_Node.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
8679 var body = [ make_node(AST_SimpleStatement, self.condition, { body: self.condition }) ];
8680 extract_declarations_from_unreachable_code(compressor, self.body, body);
8681 if (self.alternative) body.push(self.alternative);
8682 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
8683 } else if (!(cond instanceof AST_Node)) {
8684 AST_Node.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
8685 var body = [
8686 make_node(AST_SimpleStatement, self.condition, { body: self.condition }),
8687 self.body,
8688 ];
8689 if (self.alternative) extract_declarations_from_unreachable_code(compressor, self.alternative, body);
8690 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
8691 }
8692 }
8693 var negated = self.condition.negate(compressor);
8694 var self_condition_length = self.condition.print_to_string().length;
8695 var negated_length = negated.print_to_string().length;
8696 var negated_is_best = negated_length < self_condition_length;
8697 if (self.alternative && negated_is_best) {
8698 negated_is_best = false; // because we already do the switch here.
8699 // no need to swap values of self_condition_length and negated_length
8700 // here because they are only used in an equality comparison later on.
8701 self.condition = negated;
8702 var tmp = self.body;
8703 self.body = self.alternative || make_node(AST_EmptyStatement, self);
8704 self.alternative = tmp;
8705 }
8706 var body = [], var_defs = [], refs = [];
8707 var body_exprs = sequencesize(self.body, body, var_defs, refs);
8708 var alt_exprs = sequencesize(self.alternative, body, var_defs, refs);
8709 if (body_exprs && alt_exprs) {
8710 if (var_defs.length > 0) body.push(make_node(AST_Var, self, { definitions: var_defs }));
8711 if (body_exprs.length == 0) {
8712 body.push(make_node(AST_SimpleStatement, self.condition, {
8713 body: alt_exprs.length > 0 ? make_node(AST_Binary, self, {
8714 operator : "||",
8715 left : self.condition,
8716 right : make_sequence(self.alternative, alt_exprs)
8717 }).transform(compressor) : self.condition.clone()
8718 }).optimize(compressor));
8719 } else if (alt_exprs.length == 0) {
8720 if (self_condition_length === negated_length && !negated_is_best
8721 && self.condition instanceof AST_Binary && self.condition.operator == "||") {
8722 // although the code length of self.condition and negated are the same,
8723 // negated does not require additional surrounding parentheses.
8724 // see https://github.com/mishoo/UglifyJS/issues/979
8725 negated_is_best = true;
8726 }
8727 body.push(make_node(AST_SimpleStatement, self, {
8728 body: make_node(AST_Binary, self, {
8729 operator : negated_is_best ? "||" : "&&",
8730 left : negated_is_best ? negated : self.condition,
8731 right : make_sequence(self.body, body_exprs)
8732 }).transform(compressor)
8733 }).optimize(compressor));
8734 } else {
8735 body.push(make_node(AST_SimpleStatement, self, {
8736 body: make_node(AST_Conditional, self, {
8737 condition : self.condition,
8738 consequent : make_sequence(self.body, body_exprs),
8739 alternative : make_sequence(self.alternative, alt_exprs)
8740 })
8741 }).optimize(compressor));
8742 }
8743 refs.forEach(function(ref) {
8744 ref.definition().references.push(ref);
8745 });
8746 return make_node(AST_BlockStatement, self, {
8747 body: body
8748 }).optimize(compressor);
8749 }
8750 if (is_empty(self.body)) {
8751 self = make_node(AST_If, self, {
8752 condition: negated,
8753 body: self.alternative,
8754 alternative: null
8755 });
8756 }
8757 if (self.body instanceof AST_Exit
8758 && self.alternative instanceof AST_Exit
8759 && self.body.TYPE == self.alternative.TYPE) {
8760 var exit = make_node(self.body.CTOR, self, {
8761 value: make_node(AST_Conditional, self, {
8762 condition : self.condition,
8763 consequent : self.body.value || make_node(AST_Undefined, self.body).transform(compressor),
8764 alternative : self.alternative.value || make_node(AST_Undefined, self.alternative).transform(compressor)
8765 })
8766 });
8767 if (exit instanceof AST_Return) {
8768 exit.in_bool = self.body.in_bool || self.alternative.in_bool;
8769 }
8770 return exit;
8771 }
8772 if (self.body instanceof AST_If
8773 && !self.body.alternative
8774 && !self.alternative) {
8775 self = make_node(AST_If, self, {
8776 condition: make_node(AST_Binary, self.condition, {
8777 operator: "&&",
8778 left: self.condition,
8779 right: self.body.condition
8780 }),
8781 body: self.body.body,
8782 alternative: null
8783 });
8784 }
8785 if (aborts(self.body)) {
8786 if (self.alternative) {
8787 var alt = self.alternative;
8788 self.alternative = null;
8789 return make_node(AST_BlockStatement, self, {
8790 body: [ self, alt ]
8791 }).optimize(compressor);
8792 }
8793 }
8794 if (aborts(self.alternative)) {
8795 var body = self.body;
8796 self.body = self.alternative;
8797 self.condition = negated_is_best ? negated : self.condition.negate(compressor);
8798 self.alternative = null;
8799 return make_node(AST_BlockStatement, self, {
8800 body: [ self, body ]
8801 }).optimize(compressor);
8802 }
8803 if (compressor.option("typeofs")) mark_locally_defined(self.condition, self.body, self.alternative);
8804 return self;
8805
8806 function sequencesize(stat, defuns, var_defs, refs) {
8807 if (stat == null) return [];
8808 if (stat instanceof AST_BlockStatement) {
8809 var exprs = [];
8810 for (var i = 0; i < stat.body.length; i++) {
8811 var line = stat.body[i];
8812 if (line instanceof AST_LambdaDefinition) {
8813 defuns.push(line);
8814 } else if (line instanceof AST_EmptyStatement) {
8815 continue;
8816 } else if (line instanceof AST_SimpleStatement) {
8817 if (!compressor.option("sequences") && exprs.length > 0) return;
8818 exprs.push(line.body);
8819 } else if (line instanceof AST_Var) {
8820 if (!compressor.option("sequences") && exprs.length > 0) return;
8821 line.remove_initializers(compressor, var_defs);
8822 line.definitions.forEach(process_var_def);
8823 } else {
8824 return;
8825 }
8826 }
8827 return exprs;
8828 }
8829 if (stat instanceof AST_LambdaDefinition) {
8830 defuns.push(stat);
8831 return [];
8832 }
8833 if (stat instanceof AST_EmptyStatement) return [];
8834 if (stat instanceof AST_SimpleStatement) return [ stat.body ];
8835 if (stat instanceof AST_Var) {
8836 var exprs = [];
8837 stat.remove_initializers(compressor, var_defs);
8838 stat.definitions.forEach(process_var_def);
8839 return exprs;
8840 }
8841
8842 function process_var_def(var_def) {
8843 if (!var_def.value) return;
8844 exprs.push(make_node(AST_Assign, var_def, {
8845 operator: "=",
8846 left: var_def.name.convert_symbol(AST_SymbolRef, function(ref) {
8847 refs.push(ref);
8848 }),
8849 right: var_def.value
8850 }));
8851 }
8852 }
8853 });
8854
8855 OPT(AST_Switch, function(self, compressor) {
8856 if (!compressor.option("switches")) return self;
8857 if (!compressor.option("dead_code")) return self;
8858 var body = [];
8859 var branch;
8860 var decl = [];
8861 var default_branch;
8862 var exact_match;
8863 var side_effects = [];
8864 for (var i = 0, len = self.body.length; i < len; i++) {
8865 branch = self.body[i];
8866 if (branch instanceof AST_Default) {
8867 var prev = body[body.length - 1];
8868 if (default_branch || is_break(branch.body[0], compressor) && (!prev || aborts(prev))) {
8869 eliminate_branch(branch, prev);
8870 continue;
8871 } else {
8872 default_branch = branch;
8873 }
8874 } else {
8875 var exp = branch.expression;
8876 var equals = make_node(AST_Binary, self, {
8877 operator: "===",
8878 left: self.expression,
8879 right: exp,
8880 }).evaluate(compressor, true);
8881 if (!equals) {
8882 if (exp.has_side_effects(compressor)) side_effects.push(exp);
8883 eliminate_branch(branch, body[body.length - 1]);
8884 continue;
8885 }
8886 if (!(equals instanceof AST_Node)) {
8887 if (default_branch) {
8888 var default_index = body.indexOf(default_branch);
8889 body.splice(default_index, 1);
8890 eliminate_branch(default_branch, body[default_index - 1]);
8891 default_branch = null;
8892 }
8893 if (exp.has_side_effects(compressor)) {
8894 exact_match = branch;
8895 } else {
8896 default_branch = branch = make_node(AST_Default, branch, branch);
8897 }
8898 while (++i < len) eliminate_branch(self.body[i], branch);
8899 }
8900 }
8901 if (i + 1 >= len || aborts(branch)) {
8902 var prev = body[body.length - 1];
8903 var statements = branch.body;
8904 if (aborts(prev)) switch (prev.body.length - statements.length) {
8905 case 1:
8906 var stat = prev.body[prev.body.length - 1];
8907 if (!is_break(stat, compressor)) break;
8908 statements = statements.concat(stat);
8909 case 0:
8910 var prev_block = make_node(AST_BlockStatement, prev, prev);
8911 var next_block = make_node(AST_BlockStatement, branch, { body: statements });
8912 if (prev_block.equivalent_to(next_block)) prev.body = [];
8913 }
8914 }
8915 if (side_effects.length) {
8916 if (branch instanceof AST_Default) {
8917 body.push(make_node(AST_Case, self, { expression: make_sequence(self, side_effects), body: [] }));
8918 } else {
8919 side_effects.push(branch.expression);
8920 branch.expression = make_sequence(self, side_effects);
8921 }
8922 side_effects = [];
8923 }
8924 body.push(branch);
8925 }
8926 if (side_effects.length && !exact_match) {
8927 body.push(make_node(AST_Case, self, { expression: make_sequence(self, side_effects), body: [] }));
8928 }
8929 while (branch = body[body.length - 1]) {
8930 var stat = branch.body[branch.body.length - 1];
8931 if (is_break(stat, compressor)) branch.body.pop();
8932 if (branch === default_branch) {
8933 if (!has_declarations_only(branch)) break;
8934 } else if (branch.expression.has_side_effects(compressor)) {
8935 break;
8936 } else if (default_branch) {
8937 if (!has_declarations_only(default_branch)) break;
8938 if (body[body.length - 2] !== default_branch) break;
8939 default_branch.body = default_branch.body.concat(branch.body);
8940 branch.body = [];
8941 } else if (!has_declarations_only(branch)) break;
8942 eliminate_branch(branch);
8943 if (body.pop() === default_branch) default_branch = null;
8944 }
8945 if (!branch) {
8946 decl.push(make_node(AST_SimpleStatement, self.expression, { body: self.expression }));
8947 if (side_effects.length) decl.push(make_node(AST_SimpleStatement, self, {
8948 body: make_sequence(self, side_effects),
8949 }));
8950 return make_node(AST_BlockStatement, self, { body: decl }).optimize(compressor);
8951 }
8952 if (branch === default_branch) while (branch = body[body.length - 2]) {
8953 if (branch instanceof AST_Default) break;
8954 if (!has_declarations_only(branch)) break;
8955 var exp = branch.expression;
8956 if (exp.has_side_effects(compressor)) {
8957 var prev = body[body.length - 3];
8958 if (prev && !aborts(prev)) break;
8959 default_branch.body.unshift(make_node(AST_SimpleStatement, self, { body: exp }));
8960 }
8961 eliminate_branch(branch);
8962 body.splice(-2, 1);
8963 }
8964 body[0].body = decl.concat(body[0].body);
8965 self.body = body;
8966 if (compressor.option("conditionals")) switch (body.length) {
8967 case 1:
8968 if (!no_break(body[0])) break;
8969 var exp = body[0].expression;
8970 var statements = body[0].body.slice();
8971 if (body[0] !== default_branch && body[0] !== exact_match) return make_node(AST_If, self, {
8972 condition: make_node(AST_Binary, self, {
8973 operator: "===",
8974 left: self.expression,
8975 right: exp,
8976 }),
8977 body: make_node(AST_BlockStatement, self, {
8978 body: statements,
8979 }),
8980 alternative: null,
8981 }).optimize(compressor);
8982 if (exp) statements.unshift(make_node(AST_SimpleStatement, exp, {
8983 body: exp,
8984 }));
8985 statements.unshift(make_node(AST_SimpleStatement, self.expression, {
8986 body:self.expression,
8987 }));
8988 return make_node(AST_BlockStatement, self, {
8989 body: statements,
8990 }).optimize(compressor);
8991 case 2:
8992 if (!member(default_branch, body) || !no_break(body[1])) break;
8993 var statements = body[0].body.slice();
8994 var exclusive = statements.length && is_break(statements[statements.length - 1], compressor);
8995 if (exclusive) statements.pop();
8996 if (!all(statements, no_break)) break;
8997 var alternative = body[1].body.length && make_node(AST_BlockStatement, body[1], body[1]);
8998 var node = make_node(AST_If, self, {
8999 condition: make_node(AST_Binary, self, body[0] === default_branch ? {
9000 operator: "!==",
9001 left: self.expression,
9002 right: body[1].expression,
9003 } : {
9004 operator: "===",
9005 left: self.expression,
9006 right: body[0].expression,
9007 }),
9008 body: make_node(AST_BlockStatement, body[0], {
9009 body: statements,
9010 }),
9011 alternative: exclusive && alternative || null,
9012 });
9013 if (!exclusive && alternative) node = make_node(AST_BlockStatement, self, {
9014 body: [ node, alternative ],
9015 });
9016 return node.optimize(compressor);
9017 }
9018 return self;
9019
9020 function is_break(node, tw) {
9021 return node instanceof AST_Break && tw.loopcontrol_target(node) === self;
9022 }
9023
9024 function no_break(node) {
9025 var found = false;
9026 var tw = new TreeWalker(function(node) {
9027 if (found
9028 || node instanceof AST_Lambda
9029 || node instanceof AST_SimpleStatement) return true;
9030 if (is_break(node, tw)) found = true;
9031 });
9032 tw.push(self);
9033 node.walk(tw);
9034 return !found;
9035 }
9036
9037 function eliminate_branch(branch, prev) {
9038 if (prev && !aborts(prev)) {
9039 prev.body = prev.body.concat(branch.body);
9040 } else {
9041 extract_declarations_from_unreachable_code(compressor, branch, decl);
9042 }
9043 }
9044 });
9045
9046 OPT(AST_Try, function(self, compressor) {
9047 self.body = tighten_body(self.body, compressor);
9048 if (compressor.option("dead_code")) {
9049 if (has_declarations_only(self)
9050 && !(self.bcatch && self.bcatch.argname && self.bcatch.argname.match_symbol(function(node) {
9051 return node instanceof AST_SymbolCatch && !can_drop_symbol(node);
9052 }, true))) {
9053 var body = [];
9054 if (self.bcatch) {
9055 extract_declarations_from_unreachable_code(compressor, self.bcatch, body);
9056 body.forEach(function(stat) {
9057 if (!(stat instanceof AST_Var)) return;
9058 stat.definitions.forEach(function(var_def) {
9059 var def = var_def.name.definition().redefined();
9060 if (!def) return;
9061 var_def.name = var_def.name.clone();
9062 var_def.name.thedef = def;
9063 });
9064 });
9065 }
9066 body.unshift(make_node(AST_BlockStatement, self, self).optimize(compressor));
9067 if (self.bfinally) {
9068 body.push(make_node(AST_BlockStatement, self.bfinally, self.bfinally).optimize(compressor));
9069 }
9070 return make_node(AST_BlockStatement, self, {
9071 body: body
9072 }).optimize(compressor);
9073 }
9074 if (self.bfinally && has_declarations_only(self.bfinally)) {
9075 var body = make_node(AST_BlockStatement, self.bfinally, self.bfinally).optimize(compressor);
9076 body = self.body.concat(body);
9077 if (!self.bcatch) return make_node(AST_BlockStatement, self, {
9078 body: body
9079 }).optimize(compressor);
9080 self.body = body;
9081 self.bfinally = null;
9082 }
9083 }
9084 return self;
9085 });
9086
9087 function remove_initializers(make_value) {
9088 return function(compressor, defns) {
9089 var dropped = false;
9090 this.definitions.forEach(function(defn) {
9091 if (defn.value) dropped = true;
9092 defn.name.match_symbol(function(node) {
9093 if (node instanceof AST_SymbolDeclaration) defns.push(make_node(AST_VarDef, node, {
9094 name: node,
9095 value: make_value(compressor, node)
9096 }));
9097 }, true);
9098 });
9099 return dropped;
9100 };
9101 }
9102
9103 AST_Const.DEFMETHOD("remove_initializers", remove_initializers(function(compressor, node) {
9104 return make_node(AST_Undefined, node).optimize(compressor);
9105 }));
9106 AST_Let.DEFMETHOD("remove_initializers", remove_initializers(return_null));
9107 AST_Var.DEFMETHOD("remove_initializers", remove_initializers(return_null));
9108
9109 AST_Definitions.DEFMETHOD("to_assignments", function() {
9110 var assignments = this.definitions.reduce(function(a, defn) {
9111 var def = defn.name.definition();
9112 var value = defn.value;
9113 if (value) {
9114 if (value instanceof AST_Sequence) value = value.clone();
9115 var name = make_node(AST_SymbolRef, defn.name, defn.name);
9116 var assign = make_node(AST_Assign, defn, {
9117 operator: "=",
9118 left: name,
9119 right: value,
9120 });
9121 a.push(assign);
9122 var fixed = function() {
9123 return assign.right;
9124 };
9125 fixed.assigns = [ assign ];
9126 fixed.direct_access = def.direct_access;
9127 fixed.escaped = def.escaped;
9128 name.fixed = fixed;
9129 def.references.forEach(function(ref) {
9130 var assigns = ref.fixed && ref.fixed.assigns;
9131 if (assigns && assigns[0] === defn) assigns[0] = assign;
9132 });
9133 def.references.push(name);
9134 }
9135 def.assignments++;
9136 def.eliminated++;
9137 def.single_use = false;
9138 return a;
9139 }, []);
9140 if (assignments.length == 0) return null;
9141 return make_sequence(this, assignments);
9142 });
9143
9144 function is_safe_lexical(def) {
9145 return def.name != "arguments" && def.orig.length < (def.orig[0] instanceof AST_SymbolLambda ? 3 : 2);
9146 }
9147
9148 function may_overlap(compressor, def) {
9149 if (compressor.exposed(def)) return true;
9150 var scope = def.scope.resolve();
9151 for (var s = def.scope; s !== scope;) {
9152 s = s.parent_scope;
9153 if (s.var_names().has(def.name)) return true;
9154 }
9155 }
9156
9157 function to_var(stat) {
9158 return make_node(AST_Var, stat, {
9159 definitions: stat.definitions.map(function(defn) {
9160 return make_node(AST_VarDef, defn, {
9161 name: defn.name.convert_symbol(AST_SymbolVar, function(name, node) {
9162 var def = name.definition();
9163 def.orig[def.orig.indexOf(node)] = name;
9164 var scope = def.scope.resolve();
9165 if (def.scope === scope) return;
9166 def.scope = scope;
9167 scope.variables.set(def.name, def);
9168 scope.enclosed.push(def);
9169 scope.var_names().set(def.name, true);
9170 }),
9171 value: defn.value,
9172 });
9173 })
9174 });
9175 }
9176
9177 function can_varify(compressor, sym) {
9178 if (!sym.fixed_value()) return false;
9179 var def = sym.definition();
9180 return is_safe_lexical(def) && same_scope(def) && !may_overlap(compressor, def);
9181 }
9182
9183 function varify(self, compressor) {
9184 return compressor.option("varify") && all(self.definitions, function(defn) {
9185 return !defn.name.match_symbol(function(node) {
9186 if (node instanceof AST_SymbolDeclaration) return !can_varify(compressor, node);
9187 }, true);
9188 }) ? to_var(self) : self;
9189 }
9190
9191 OPT(AST_Const, varify);
9192 OPT(AST_Let, varify);
9193
9194 function trim_optional_chain(node, compressor) {
9195 if (!compressor.option("optional_chains")) return;
9196 if (node.terminal) do {
9197 var expr = node.expression;
9198 if (node.optional) {
9199 var ev = fuzzy_eval(compressor, expr, true);
9200 if (ev == null) return make_node(AST_UnaryPrefix, node, {
9201 operator: "void",
9202 expression: expr,
9203 }).optimize(compressor);
9204 if (!(ev instanceof AST_Node)) node.optional = false;
9205 }
9206 node = expr;
9207 } while ((node.TYPE == "Call" || node instanceof AST_PropAccess) && !node.terminal);
9208 }
9209
9210 function lift_sequence_in_expression(node, compressor) {
9211 var exp = node.expression;
9212 if (!(exp instanceof AST_Sequence)) return node;
9213 var x = exp.expressions.slice();
9214 var e = node.clone();
9215 e.expression = x.pop();
9216 x.push(e);
9217 return make_sequence(node, x);
9218 }
9219
9220 function drop_unused_call_args(call, compressor, fns_with_marked_args) {
9221 var exp = call.expression;
9222 var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
9223 if (!(fn instanceof AST_Lambda)) return;
9224 if (fn.uses_arguments) return;
9225 if (fn.pinned()) return;
9226 if (fns_with_marked_args && fns_with_marked_args.indexOf(fn) < 0) return;
9227 var args = call.args;
9228 if (!all(args, function(arg) {
9229 return !(arg instanceof AST_Spread);
9230 })) return;
9231 var argnames = fn.argnames;
9232 var is_iife = fn === exp && !fn.name;
9233 if (fn.rest) {
9234 if (!(is_iife && compressor.option("rests"))) return;
9235 var insert = argnames.length;
9236 args = args.slice(0, insert);
9237 while (args.length < insert) args.push(make_node(AST_Undefined, call).optimize(compressor));
9238 args.push(make_node(AST_Array, call, { elements: call.args.slice(insert) }));
9239 argnames = argnames.concat(fn.rest);
9240 fn.rest = null;
9241 } else {
9242 args = args.slice();
9243 argnames = argnames.slice();
9244 }
9245 var pos = 0, last = 0;
9246 var drop_defaults = is_iife && compressor.option("default_values");
9247 var drop_fargs = is_iife && compressor.drop_fargs(fn, call) ? function(argname, arg) {
9248 if (!argname) return true;
9249 if (argname instanceof AST_DestructuredArray) {
9250 return argname.elements.length == 0 && !argname.rest && arg instanceof AST_Array;
9251 }
9252 if (argname instanceof AST_DestructuredObject) {
9253 return argname.properties.length == 0 && !argname.rest && arg && !arg.may_throw_on_access(compressor);
9254 }
9255 return argname.unused;
9256 } : return_false;
9257 var side_effects = [];
9258 for (var i = 0; i < args.length; i++) {
9259 var argname = argnames[i];
9260 if (drop_defaults && argname instanceof AST_DefaultValue && args[i].is_defined(compressor)) {
9261 argnames[i] = argname = argname.name;
9262 }
9263 if (!argname || argname.unused !== undefined) {
9264 var node = args[i].drop_side_effect_free(compressor);
9265 if (drop_fargs(argname)) {
9266 if (argname) argnames.splice(i, 1);
9267 args.splice(i, 1);
9268 if (node) side_effects.push(node);
9269 i--;
9270 continue;
9271 } else if (node) {
9272 side_effects.push(node);
9273 args[pos++] = make_sequence(call, side_effects);
9274 side_effects = [];
9275 } else if (argname) {
9276 if (side_effects.length) {
9277 args[pos++] = make_sequence(call, side_effects);
9278 side_effects = [];
9279 } else {
9280 args[pos++] = make_node(AST_Number, args[i], {
9281 value: 0
9282 });
9283 continue;
9284 }
9285 }
9286 } else if (drop_fargs(argname, args[i])) {
9287 var node = args[i].drop_side_effect_free(compressor);
9288 argnames.splice(i, 1);
9289 args.splice(i, 1);
9290 if (node) side_effects.push(node);
9291 i--;
9292 continue;
9293 } else {
9294 side_effects.push(args[i]);
9295 args[pos++] = make_sequence(call, side_effects);
9296 side_effects = [];
9297 }
9298 last = pos;
9299 }
9300 for (; i < argnames.length; i++) {
9301 if (drop_fargs(argnames[i])) argnames.splice(i--, 1);
9302 }
9303 fn.argnames = argnames;
9304 args.length = last;
9305 call.args = args;
9306 if (!side_effects.length) return;
9307 var arg = make_sequence(call, side_effects);
9308 args.push(args.length < argnames.length ? make_node(AST_UnaryPrefix, call, {
9309 operator: "void",
9310 expression: arg,
9311 }) : arg);
9312 }
9313
9314 function avoid_await_yield(parent_scope) {
9315 var avoid = [];
9316 if (is_async(parent_scope)) avoid.push("await");
9317 if (is_generator(parent_scope)) avoid.push("yield");
9318 return avoid.length && makePredicate(avoid);
9319 }
9320
9321 OPT(AST_Call, function(self, compressor) {
9322 var exp = self.expression;
9323 var terminated = trim_optional_chain(self, compressor);
9324 if (terminated) return terminated;
9325 if (compressor.option("sequences")) {
9326 if (exp instanceof AST_PropAccess) {
9327 var seq = lift_sequence_in_expression(exp, compressor);
9328 if (seq !== exp) {
9329 var call = self.clone();
9330 call.expression = seq.expressions.pop();
9331 seq.expressions.push(call);
9332 return seq.optimize(compressor);
9333 }
9334 } else if (!needs_unbinding(compressor, exp.tail_node())) {
9335 var seq = lift_sequence_in_expression(self, compressor);
9336 if (seq !== self) return seq.optimize(compressor);
9337 }
9338 }
9339 if (compressor.option("unused")) drop_unused_call_args(self, compressor);
9340 if (compressor.option("unsafe")) {
9341 if (is_undeclared_ref(exp)) switch (exp.name) {
9342 case "Array":
9343 // Array(n) ---> [ , , ... , ]
9344 if (self.args.length == 1) {
9345 var first = self.args[0];
9346 if (first instanceof AST_Number) try {
9347 var length = first.value;
9348 if (length > 6) break;
9349 var elements = Array(length);
9350 for (var i = 0; i < length; i++) elements[i] = make_node(AST_Hole, self);
9351 return make_node(AST_Array, self, { elements: elements });
9352 } catch (ex) {
9353 AST_Node.warn("Invalid array length: {length} [{file}:{line},{col}]", {
9354 length: length,
9355 file: self.start.file,
9356 line: self.start.line,
9357 col: self.start.col,
9358 });
9359 break;
9360 }
9361 if (!first.is_boolean(compressor) && !first.is_string(compressor)) break;
9362 }
9363 // Array(...) ---> [ ... ]
9364 return make_node(AST_Array, self, { elements: self.args });
9365 case "Object":
9366 // Object() ---> {}
9367 if (self.args.length == 0) return make_node(AST_Object, self, { properties: [] });
9368 break;
9369 case "String":
9370 // String() ---> ""
9371 if (self.args.length == 0) return make_node(AST_String, self, { value: "" });
9372 // String(x) ---> "" + x
9373 if (self.args.length == 1) return make_node(AST_Binary, self, {
9374 operator: "+",
9375 left: make_node(AST_String, self, { value: "" }),
9376 right: self.args[0],
9377 }).optimize(compressor);
9378 break;
9379 case "Number":
9380 // Number() ---> 0
9381 if (self.args.length == 0) return make_node(AST_Number, self, { value: 0 });
9382 // Number(x) ---> +("" + x)
9383 if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
9384 operator: "+",
9385 expression: make_node(AST_Binary, self, {
9386 operator: "+",
9387 left: make_node(AST_String, self, { value: "" }),
9388 right: self.args[0],
9389 }),
9390 }).optimize(compressor);
9391 break;
9392 case "Boolean":
9393 // Boolean() ---> false
9394 if (self.args.length == 0) return make_node(AST_False, self).optimize(compressor);
9395 // Boolean(x) ---> !!x
9396 if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
9397 operator: "!",
9398 expression: make_node(AST_UnaryPrefix, self, {
9399 operator: "!",
9400 expression: self.args[0],
9401 }),
9402 }).optimize(compressor);
9403 break;
9404 case "RegExp":
9405 // attempt to convert RegExp(...) to literal
9406 var params = [];
9407 if (all(self.args, function(arg) {
9408 var value = arg.evaluate(compressor);
9409 params.unshift(value);
9410 return arg !== value;
9411 })) try {
9412 return best_of(compressor, self, make_node(AST_RegExp, self, {
9413 value: RegExp.apply(RegExp, params),
9414 }));
9415 } catch (ex) {
9416 AST_Node.warn("Error converting {expr} [{file}:{line},{col}]", {
9417 expr: self,
9418 file: self.start.file,
9419 line: self.start.line,
9420 col: self.start.col,
9421 });
9422 }
9423 break;
9424 } else if (exp instanceof AST_Dot) switch (exp.property) {
9425 case "toString":
9426 // x.toString() ---> "" + x
9427 var expr = exp.expression;
9428 if (self.args.length == 0 && !(expr.may_throw_on_access(compressor) || expr instanceof AST_Super)) {
9429 return make_node(AST_Binary, self, {
9430 operator: "+",
9431 left: make_node(AST_String, self, { value: "" }),
9432 right: expr,
9433 }).optimize(compressor);
9434 }
9435 break;
9436 case "join":
9437 if (exp.expression instanceof AST_Array && self.args.length < 2) EXIT: {
9438 var separator = self.args[0];
9439 // [].join() ---> ""
9440 // [].join(x) ---> (x, "")
9441 if (exp.expression.elements.length == 0 && !(separator instanceof AST_Spread)) {
9442 return separator ? make_sequence(self, [
9443 separator,
9444 make_node(AST_String, self, { value: "" }),
9445 ]).optimize(compressor) : make_node(AST_String, self, { value: "" });
9446 }
9447 if (separator) {
9448 separator = separator.evaluate(compressor);
9449 if (separator instanceof AST_Node) break EXIT; // not a constant
9450 }
9451 var elements = [];
9452 var consts = [];
9453 for (var i = 0; i < exp.expression.elements.length; i++) {
9454 var el = exp.expression.elements[i];
9455 var value = el.evaluate(compressor);
9456 if (value !== el) {
9457 consts.push(value);
9458 } else if (el instanceof AST_Spread) {
9459 break EXIT;
9460 } else {
9461 if (consts.length > 0) {
9462 elements.push(make_node(AST_String, self, {
9463 value: consts.join(separator),
9464 }));
9465 consts.length = 0;
9466 }
9467 elements.push(el);
9468 }
9469 }
9470 if (consts.length > 0) elements.push(make_node(AST_String, self, {
9471 value: consts.join(separator),
9472 }));
9473 // [ x ].join() ---> "" + x
9474 // [ x ].join(".") ---> "" + x
9475 // [ 1, 2, 3 ].join() ---> "1,2,3"
9476 // [ 1, 2, 3 ].join(".") ---> "1.2.3"
9477 if (elements.length == 1) {
9478 if (elements[0].is_string(compressor)) return elements[0];
9479 return make_node(AST_Binary, elements[0], {
9480 operator: "+",
9481 left: make_node(AST_String, self, { value: "" }),
9482 right: elements[0],
9483 });
9484 }
9485 // [ 1, 2, a, 3 ].join("") ---> "12" + a + "3"
9486 if (separator == "") {
9487 var first;
9488 if (elements[0].is_string(compressor) || elements[1].is_string(compressor)) {
9489 first = elements.shift();
9490 } else {
9491 first = make_node(AST_String, self, { value: "" });
9492 }
9493 return elements.reduce(function(prev, el) {
9494 return make_node(AST_Binary, el, {
9495 operator: "+",
9496 left: prev,
9497 right: el,
9498 });
9499 }, first).optimize(compressor);
9500 }
9501 // [ x, "foo", "bar", y ].join() ---> [ x, "foo,bar", y ].join()
9502 // [ x, "foo", "bar", y ].join("-") ---> [ x, "foo-bar", y ].join("-")
9503 // need this awkward cloning to not affect original element
9504 // best_of will decide which one to get through.
9505 var node = self.clone();
9506 node.expression = node.expression.clone();
9507 node.expression.expression = node.expression.expression.clone();
9508 node.expression.expression.elements = elements;
9509 return best_of(compressor, self, node);
9510 }
9511 break;
9512 case "charAt":
9513 if (self.args.length < 2) {
9514 var node = make_node(AST_Binary, self, {
9515 operator: "||",
9516 left: make_node(AST_Sub, self, {
9517 expression: exp.expression,
9518 property: self.args.length ? make_node(AST_Binary, self.args[0], {
9519 operator: "|",
9520 left: make_node(AST_Number, self, { value: 0 }),
9521 right: self.args[0],
9522 }) : make_node(AST_Number, self, { value: 0 }),
9523 }).optimize(compressor),
9524 right: make_node(AST_String, self, { value: "" }),
9525 });
9526 node.is_string = return_true;
9527 return node.optimize(compressor);
9528 }
9529 break;
9530 case "apply":
9531 if (self.args.length == 2 && self.args[1] instanceof AST_Array) {
9532 var args = self.args[1].elements.slice();
9533 args.unshift(self.args[0]);
9534 return make_node(AST_Call, self, {
9535 expression: make_node(AST_Dot, exp, {
9536 expression: exp.expression,
9537 property: "call",
9538 }),
9539 args: args
9540 }).optimize(compressor);
9541 }
9542 break;
9543 case "call":
9544 var func = exp.expression;
9545 if (func instanceof AST_SymbolRef) {
9546 func = func.fixed_value();
9547 }
9548 if (func instanceof AST_Lambda && !func.contains_this()) {
9549 return (self.args.length ? make_sequence(this, [
9550 self.args[0],
9551 make_node(AST_Call, self, {
9552 expression: exp.expression,
9553 args: self.args.slice(1)
9554 })
9555 ]) : make_node(AST_Call, self, {
9556 expression: exp.expression,
9557 args: []
9558 })).optimize(compressor);
9559 }
9560 break;
9561 }
9562 }
9563 if (compressor.option("unsafe_Function")
9564 && is_undeclared_ref(exp)
9565 && exp.name == "Function") {
9566 // new Function() ---> function(){}
9567 if (self.args.length == 0) return make_node(AST_Function, self, {
9568 argnames: [],
9569 body: []
9570 }).init_vars(exp.scope);
9571 if (all(self.args, function(x) {
9572 return x instanceof AST_String;
9573 })) {
9574 // quite a corner-case, but we can handle it:
9575 // https://github.com/mishoo/UglifyJS/issues/203
9576 // if the code argument is a constant, then we can minify it.
9577 try {
9578 var code = "n(function(" + self.args.slice(0, -1).map(function(arg) {
9579 return arg.value;
9580 }).join() + "){" + self.args[self.args.length - 1].value + "})";
9581 var ast = parse(code);
9582 var mangle = { ie: compressor.option("ie") };
9583 ast.figure_out_scope(mangle);
9584 var comp = new Compressor(compressor.options);
9585 ast = ast.transform(comp);
9586 ast.figure_out_scope(mangle);
9587 ast.compute_char_frequency(mangle);
9588 ast.mangle_names(mangle);
9589 var fun;
9590 ast.walk(new TreeWalker(function(node) {
9591 if (fun) return true;
9592 if (node instanceof AST_Lambda) {
9593 fun = node;
9594 return true;
9595 }
9596 }));
9597 var code = OutputStream();
9598 AST_BlockStatement.prototype._codegen.call(fun, code);
9599 self.args = [
9600 make_node(AST_String, self, {
9601 value: fun.argnames.map(function(arg) {
9602 return arg.print_to_string();
9603 }).join(),
9604 }),
9605 make_node(AST_String, self.args[self.args.length - 1], {
9606 value: code.get().replace(/^\{|\}$/g, "")
9607 })
9608 ];
9609 return self;
9610 } catch (ex) {
9611 if (ex instanceof JS_Parse_Error) {
9612 AST_Node.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
9613 AST_Node.warn(ex.toString());
9614 } else {
9615 throw ex;
9616 }
9617 }
9618 }
9619 }
9620 var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
9621 var parent = compressor.parent(), current = compressor.self();
9622 var is_func = fn instanceof AST_Lambda
9623 && (!is_async(fn) || compressor.option("awaits") && parent instanceof AST_Await)
9624 && (!is_generator(fn) || compressor.option("yields") && current instanceof AST_Yield && current.nested);
9625 var stat = is_func && fn.first_statement();
9626 var has_default = 0, has_destructured = false;
9627 var has_spread = !all(self.args, function(arg) {
9628 return !(arg instanceof AST_Spread);
9629 });
9630 var can_drop = is_func && all(fn.argnames, function(argname, index) {
9631 if (has_default == 1 && self.args[index] instanceof AST_Spread) has_default = 2;
9632 if (argname instanceof AST_DefaultValue) {
9633 if (!has_default) has_default = 1;
9634 var arg = has_default == 1 && self.args[index];
9635 if (arg && !is_undefined(arg)) has_default = 2;
9636 if (has_arg_refs(argname.value)) return false;
9637 argname = argname.name;
9638 }
9639 if (argname instanceof AST_Destructured) {
9640 has_destructured = true;
9641 if (has_arg_refs(argname)) return false;
9642 }
9643 return true;
9644 }) && !(fn.rest instanceof AST_Destructured && has_arg_refs(fn.rest));
9645 var can_inline = can_drop && compressor.option("inline") && !self.is_expr_pure(compressor);
9646 if (can_inline && stat instanceof AST_Return) {
9647 var value = stat.value;
9648 if (exp === fn
9649 && !fn.name
9650 && (!value || value.is_constant_expression())
9651 && safe_from_await_yield(fn, compressor.find_parent(AST_Scope))) {
9652 return make_sequence(self, convert_args(value)).optimize(compressor);
9653 }
9654 }
9655 if (is_func) {
9656 var def, value, var_assigned = false;
9657 if (can_inline
9658 && !fn.uses_arguments
9659 && !fn.pinned()
9660 && !(fn.name && fn instanceof AST_LambdaExpression)
9661 && (exp === fn || !recursive_ref(compressor, def = exp.definition(), fn)
9662 && fn.is_constant_expression(find_scope(compressor)))
9663 && !has_spread
9664 && (value = can_flatten_body(stat))
9665 && !fn.contains_this()) {
9666 var replacing = exp === fn || def.single_use && def.references.length - def.replaced == 1;
9667 if (can_substitute_directly()) {
9668 var args = self.args.slice();
9669 var refs = [];
9670 var retValue = value.clone(true).transform(new TreeTransformer(function(node) {
9671 if (node instanceof AST_SymbolRef) {
9672 var def = node.definition();
9673 if (fn.variables.get(node.name) !== def) {
9674 refs.push(node);
9675 return node;
9676 }
9677 var index = resolve_index(def);
9678 var arg = args[index];
9679 if (!arg) return make_node(AST_Undefined, self);
9680 args[index] = null;
9681 var parent = this.parent();
9682 return parent ? maintain_this_binding(compressor, parent, node, arg) : arg;
9683 }
9684 }));
9685 var save_inlined = fn.inlined;
9686 if (exp !== fn) fn.inlined = true;
9687 var exprs = [];
9688 args.forEach(function(arg) {
9689 if (!arg) return;
9690 arg = arg.clone(true);
9691 arg.walk(new TreeWalker(function(node) {
9692 if (node instanceof AST_SymbolRef) refs.push(node);
9693 }));
9694 exprs.push(arg);
9695 }, []);
9696 exprs.push(retValue);
9697 var node = make_sequence(self, exprs).optimize(compressor);
9698 fn.inlined = save_inlined;
9699 node = maintain_this_binding(compressor, parent, current, node);
9700 if (replacing || best_of_expression(node, self) === node) {
9701 refs.forEach(function(ref) {
9702 ref.scope = exp === fn ? fn.parent_scope : exp.scope;
9703 ref.reference();
9704 var def = ref.definition();
9705 if (replacing) def.replaced++;
9706 def.single_use = false;
9707 });
9708 return node;
9709 } else if (!node.has_side_effects(compressor)) {
9710 self.drop_side_effect_free = function(compressor, first_in_statement) {
9711 var self = this;
9712 var exprs = self.args.slice();
9713 exprs.unshift(self.expression);
9714 return make_sequence(self, exprs).drop_side_effect_free(compressor, first_in_statement);
9715 };
9716 }
9717 }
9718 var arg_used, insert, in_loop, scope;
9719 if (replacing && can_inject_symbols()) {
9720 fn._squeezed = true;
9721 if (exp !== fn) fn.parent_scope = exp.scope;
9722 var node = make_sequence(self, flatten_fn()).optimize(compressor);
9723 return maintain_this_binding(compressor, parent, current, node);
9724 }
9725 }
9726 if (compressor.option("side_effects")
9727 && can_drop
9728 && all(fn.body, is_empty)
9729 && (fn === exp ? fn_name_unused(fn, compressor) : !has_default && !has_destructured && !fn.rest)
9730 && !(is_arrow(fn) && fn.value)
9731 && safe_from_await_yield(fn, compressor.find_parent(AST_Scope))) {
9732 return make_sequence(self, convert_args()).optimize(compressor);
9733 }
9734 }
9735 if (compressor.option("drop_console")) {
9736 if (exp instanceof AST_PropAccess) {
9737 var name = exp.expression;
9738 while (name.expression) {
9739 name = name.expression;
9740 }
9741 if (is_undeclared_ref(name) && name.name == "console") {
9742 return make_node(AST_Undefined, self).optimize(compressor);
9743 }
9744 }
9745 }
9746 if (compressor.option("negate_iife") && parent instanceof AST_SimpleStatement && is_iife_call(current)) {
9747 return self.negate(compressor, true);
9748 }
9749 return try_evaluate(compressor, self);
9750
9751 function has_arg_refs(node) {
9752 var found = false;
9753 node.walk(new TreeWalker(function(node) {
9754 if (found) return true;
9755 if (node instanceof AST_SymbolRef && fn.variables.get(node.name) === node.definition()) {
9756 return found = true;
9757 }
9758 }));
9759 return found;
9760 }
9761
9762 function make_void_lhs(orig) {
9763 return make_node(AST_Dot, orig, {
9764 expression: make_node(AST_Array, orig, { elements: [] }),
9765 property: "e",
9766 });
9767 }
9768
9769 function convert_args(value) {
9770 var args = self.args.slice();
9771 var destructured = has_default > 1 || has_destructured || fn.rest;
9772 if (destructured || has_spread) args = [ make_node(AST_Array, self, { elements: args }) ];
9773 if (destructured) {
9774 var tt = new TreeTransformer(function(node, descend) {
9775 if (node instanceof AST_DefaultValue) return make_node(AST_DefaultValue, node, {
9776 name: node.name.transform(tt) || make_void_lhs(node),
9777 value: node.value,
9778 });
9779 if (node instanceof AST_DestructuredArray) {
9780 var elements = [];
9781 node.elements.forEach(function(node, index) {
9782 node = node.transform(tt);
9783 if (node) elements[index] = node;
9784 });
9785 fill_holes(node, elements);
9786 return make_node(AST_DestructuredArray, node, { elements: elements });
9787 }
9788 if (node instanceof AST_DestructuredObject) {
9789 var properties = [], side_effects = [];
9790 node.properties.forEach(function(prop) {
9791 var key = prop.key;
9792 var value = prop.value.transform(tt);
9793 if (value) {
9794 if (side_effects.length) {
9795 if (!(key instanceof AST_Node)) key = make_node_from_constant(key, prop);
9796 side_effects.push(key);
9797 key = make_sequence(node, side_effects);
9798 side_effects = [];
9799 }
9800 properties.push(make_node(AST_DestructuredKeyVal, prop, {
9801 key: key,
9802 value: value,
9803 }));
9804 } else if (key instanceof AST_Node) {
9805 side_effects.push(key);
9806 }
9807 });
9808 if (side_effects.length) properties.push(make_node(AST_DestructuredKeyVal, node, {
9809 key: make_sequence(node, side_effects),
9810 value: make_void_lhs(node),
9811 }));
9812 return make_node(AST_DestructuredObject, node, { properties: properties });
9813 }
9814 if (node instanceof AST_SymbolFunarg) return null;
9815 });
9816 var lhs = [];
9817 fn.argnames.forEach(function(argname, index) {
9818 argname = argname.transform(tt);
9819 if (argname) lhs[index] = argname;
9820 });
9821 var rest = fn.rest && fn.rest.transform(tt);
9822 if (rest) lhs.length = fn.argnames.length;
9823 fill_holes(fn, lhs);
9824 args[0] = make_node(AST_Assign, self, {
9825 operator: "=",
9826 left: make_node(AST_DestructuredArray, fn, {
9827 elements: lhs,
9828 rest: rest,
9829 }),
9830 right: args[0],
9831 });
9832 } else fn.argnames.forEach(function(argname) {
9833 if (argname instanceof AST_DefaultValue) args.push(argname.value);
9834 });
9835 args.push(value || make_node(AST_Undefined, self));
9836 return args;
9837 }
9838
9839 function safe_from_await_yield(node, scope) {
9840 var avoid = avoid_await_yield(scope);
9841 if (!avoid) return true;
9842 var safe = true;
9843 var tw = new TreeWalker(function(node) {
9844 if (!safe) return true;
9845 if (node instanceof AST_Scope) {
9846 if (node === fn) return;
9847 if (is_arrow(node)) {
9848 for (var i = 0; safe && i < node.argnames.length; i++) node.argnames[i].walk(tw);
9849 } else if (node instanceof AST_LambdaDefinition && avoid[node.name.name]) {
9850 safe = false;
9851 }
9852 return true;
9853 }
9854 if (node instanceof AST_Symbol && avoid[node.name] && node !== fn.name) safe = false;
9855 });
9856 node.walk(tw);
9857 return safe;
9858 }
9859
9860 function noop_value() {
9861 return self.call_only ? make_node(AST_Number, self, { value: 0 }) : make_node(AST_Undefined, self);
9862 }
9863
9864 function return_value(stat) {
9865 if (!stat) return noop_value();
9866 if (stat instanceof AST_Return) return stat.value || noop_value();
9867 if (stat instanceof AST_SimpleStatement) {
9868 return self.call_only ? stat.body : make_node(AST_UnaryPrefix, stat, {
9869 operator: "void",
9870 expression: stat.body,
9871 });
9872 }
9873 }
9874
9875 function can_flatten_body(stat) {
9876 var len = fn.body.length;
9877 if (len < 2) {
9878 stat = return_value(stat);
9879 if (stat) return stat;
9880 }
9881 if (compressor.option("inline") < 3) return false;
9882 stat = null;
9883 for (var i = 0; i < len; i++) {
9884 var line = fn.body[i];
9885 if (line instanceof AST_Var) {
9886 var assigned = var_assigned || !declarations_only(line);
9887 if (assigned) {
9888 var_assigned = true;
9889 if (stat) return false;
9890 }
9891 } else if (line instanceof AST_AsyncDefun
9892 || line instanceof AST_Defun
9893 || line instanceof AST_EmptyStatement) {
9894 continue;
9895 } else if (stat) {
9896 return false;
9897 } else {
9898 stat = line;
9899 }
9900 }
9901 return return_value(stat);
9902 }
9903
9904 function resolve_index(def) {
9905 for (var i = fn.argnames.length; --i >= 0;) {
9906 if (fn.argnames[i].definition() === def) return i;
9907 }
9908 }
9909
9910 function can_substitute_directly() {
9911 if (has_default || has_destructured || var_assigned || fn.rest) return;
9912 if (compressor.option("inline") < 2 && fn.argnames.length) return;
9913 if (!fn.variables.all(function(def) {
9914 return def.references.length - def.replaced < 2 && def.orig[0] instanceof AST_SymbolFunarg;
9915 })) return;
9916 var scope = compressor.find_parent(AST_Scope);
9917 var abort = false;
9918 var avoid = avoid_await_yield(scope);
9919 var begin;
9920 var in_order = [];
9921 var side_effects = false;
9922 value.walk(new TreeWalker(function(node, descend) {
9923 if (abort) return true;
9924 if (node instanceof AST_Binary && lazy_op[node.operator]
9925 || node instanceof AST_Conditional) {
9926 in_order = null;
9927 return;
9928 }
9929 if (node instanceof AST_Scope) return abort = true;
9930 if (avoid && node instanceof AST_Symbol && avoid[node.name]) return abort = true;
9931 if (node instanceof AST_SymbolRef) {
9932 var def = node.definition();
9933 if (fn.variables.get(node.name) !== def) {
9934 in_order = null;
9935 return;
9936 }
9937 if (def.init instanceof AST_LambdaDefinition) return abort = true;
9938 if (is_lhs(node, this.parent())) return abort = true;
9939 var index = resolve_index(def);
9940 if (!(begin < index)) begin = index;
9941 if (!in_order) return;
9942 if (side_effects) {
9943 in_order = null;
9944 } else {
9945 in_order.push(fn.argnames[index]);
9946 }
9947 return;
9948 }
9949 if (node.has_side_effects(compressor)) {
9950 descend();
9951 side_effects = true;
9952 return true;
9953 }
9954 }));
9955 if (abort) return;
9956 var end = self.args.length;
9957 if (in_order && fn.argnames.length >= end) {
9958 end = fn.argnames.length;
9959 while (end-- > begin && fn.argnames[end] === in_order.pop());
9960 end++;
9961 }
9962 return end <= begin || all(self.args.slice(begin, end), side_effects && !in_order ? function(funarg) {
9963 return funarg.is_constant_expression(scope);
9964 } : function(funarg) {
9965 return !funarg.has_side_effects(compressor);
9966 });
9967 }
9968
9969 function var_exists(defined, name) {
9970 return defined.has(name) || identifier_atom[name] || scope.var_names().has(name);
9971 }
9972
9973 function can_inject_args(defined, safe_to_inject) {
9974 var abort = false;
9975 fn.each_argname(function(arg) {
9976 if (abort) return;
9977 if (arg.unused) return;
9978 if (!safe_to_inject || var_exists(defined, arg.name)) return abort = true;
9979 arg_used.set(arg.name, true);
9980 if (in_loop) in_loop.push(arg.definition());
9981 });
9982 return !abort;
9983 }
9984
9985 function can_inject_vars(defined, safe_to_inject) {
9986 for (var i = 0; i < fn.body.length; i++) {
9987 var stat = fn.body[i];
9988 if (stat instanceof AST_LambdaDefinition) {
9989 var name = stat.name;
9990 if (!safe_to_inject) return false;
9991 if (arg_used.has(name.name)) return false;
9992 if (var_exists(defined, name.name)) return false;
9993 if (!all(stat.enclosed, function(def) {
9994 return def.scope === stat || !defined.has(def.name);
9995 })) return false;
9996 if (in_loop) in_loop.push(name.definition());
9997 continue;
9998 }
9999 if (!(stat instanceof AST_Var)) continue;
10000 if (!safe_to_inject) return false;
10001 for (var j = stat.definitions.length; --j >= 0;) {
10002 var name = stat.definitions[j].name;
10003 if (var_exists(defined, name.name)) return false;
10004 if (in_loop) in_loop.push(name.definition());
10005 }
10006 }
10007 return true;
10008 }
10009
10010 function can_inject_symbols() {
10011 var defined = new Dictionary();
10012 var level = 0, child;
10013 scope = current;
10014 do {
10015 if (scope.variables) scope.variables.each(function(def) {
10016 defined.set(def.name, true);
10017 });
10018 child = scope;
10019 scope = compressor.parent(level++);
10020 if (scope instanceof AST_DWLoop) {
10021 in_loop = [];
10022 } else if (scope instanceof AST_For) {
10023 if (scope.init === child) continue;
10024 in_loop = [];
10025 } else if (scope instanceof AST_ForEnumeration) {
10026 if (scope.init === child) continue;
10027 if (scope.object === child) continue;
10028 in_loop = [];
10029 } else if (scope instanceof AST_SymbolRef) {
10030 if (scope.fixed_value() instanceof AST_Scope) return false;
10031 }
10032 } while (!(scope instanceof AST_Scope));
10033 insert = scope.body.indexOf(child) + 1;
10034 if (!insert) return false;
10035 if (!safe_from_await_yield(fn, scope)) return false;
10036 var safe_to_inject = exp !== fn || fn.parent_scope.resolve() === scope;
10037 if (scope instanceof AST_Toplevel) {
10038 if (compressor.toplevel.vars) {
10039 defined.set("arguments", true);
10040 } else {
10041 safe_to_inject = false;
10042 }
10043 }
10044 arg_used = new Dictionary();
10045 var inline = compressor.option("inline");
10046 if (!can_inject_args(defined, inline >= 2 && safe_to_inject)) return false;
10047 if (!can_inject_vars(defined, inline >= 3 && safe_to_inject)) return false;
10048 return !in_loop || in_loop.length == 0 || !is_reachable(fn, in_loop);
10049 }
10050
10051 function append_var(decls, expressions, name, value) {
10052 var def = name.definition();
10053 if (!scope.var_names().has(name.name)) {
10054 scope.var_names().set(name.name, true);
10055 decls.push(make_node(AST_VarDef, name, {
10056 name: name,
10057 value: null,
10058 }));
10059 }
10060 scope.variables.set(name.name, def);
10061 scope.enclosed.push(def);
10062 if (!value) return;
10063 var sym = make_node(AST_SymbolRef, name, name);
10064 def.references.push(sym);
10065 expressions.push(make_node(AST_Assign, self, {
10066 operator: "=",
10067 left: sym,
10068 right: value,
10069 }));
10070 }
10071
10072 function flatten_args(decls, expressions) {
10073 var len = fn.argnames.length;
10074 for (var i = self.args.length; --i >= len;) {
10075 expressions.push(self.args[i]);
10076 }
10077 var default_args = [];
10078 for (i = len; --i >= 0;) {
10079 var argname = fn.argnames[i];
10080 var name;
10081 if (argname instanceof AST_DefaultValue) {
10082 default_args.push(argname);
10083 name = argname.name;
10084 } else {
10085 name = argname;
10086 }
10087 var value = self.args[i];
10088 if (name.unused || scope.var_names().has(name.name)) {
10089 if (value) expressions.push(value);
10090 } else {
10091 var symbol = make_node(AST_SymbolVar, name, name);
10092 name.definition().orig.push(symbol);
10093 if (name.unused !== undefined) {
10094 append_var(decls, expressions, symbol);
10095 if (value) expressions.push(value);
10096 } else {
10097 if (!value && in_loop && argname === name) value = make_node(AST_Undefined, self);
10098 append_var(decls, expressions, symbol, value);
10099 }
10100 }
10101 }
10102 decls.reverse();
10103 expressions.reverse();
10104 for (i = default_args.length; --i >= 0;) {
10105 var node = default_args[i];
10106 if (node.name.unused !== undefined) {
10107 expressions.push(node.value);
10108 } else {
10109 var sym = make_node(AST_SymbolRef, node.name, node.name);
10110 node.name.definition().references.push(sym);
10111 expressions.push(make_node(AST_Assign, node, {
10112 operator: "=",
10113 left: sym,
10114 right: node.value,
10115 }));
10116 }
10117 }
10118 }
10119
10120 function flatten_destructured(decls, expressions) {
10121 expressions.push(make_node(AST_Assign, self, {
10122 operator: "=",
10123 left: make_node(AST_DestructuredArray, self, {
10124 elements: fn.argnames.map(function(argname) {
10125 if (argname.unused) return make_node(AST_Hole, argname);
10126 return argname.convert_symbol(AST_SymbolRef, process);
10127 }),
10128 rest: fn.rest && fn.rest.convert_symbol(AST_SymbolRef, process),
10129 }),
10130 right: make_node(AST_Array, self, { elements: self.args.slice() }),
10131 }));
10132
10133 function process(ref, name) {
10134 var def = name.definition();
10135 def.references.push(ref);
10136 var symbol = make_node(AST_SymbolVar, name, name);
10137 def.orig.push(symbol);
10138 append_var(decls, expressions, symbol);
10139 }
10140 }
10141
10142 function flatten_var(name) {
10143 var redef = name.definition().redefined();
10144 if (redef) {
10145 name = name.clone();
10146 name.thedef = redef;
10147 }
10148 return name;
10149 }
10150
10151 function flatten_vars(decls, expressions) {
10152 var args = [ insert, 0 ];
10153 var decl_var = [], expr_var = [], expr_loop = [];
10154 for (var i = 0; i < fn.body.length; i++) {
10155 var stat = fn.body[i];
10156 if (stat instanceof AST_LambdaDefinition) {
10157 if (in_loop) {
10158 var name = make_node(AST_SymbolVar, stat.name, flatten_var(stat.name));
10159 name.definition().orig.push(name);
10160 append_var(decls, expressions, name, to_func_expr(stat, true));
10161 } else {
10162 var def = stat.name.definition();
10163 scope.functions.set(def.name, def);
10164 scope.variables.set(def.name, def);
10165 scope.enclosed.push(def);
10166 scope.var_names().set(def.name, true);
10167 args.push(stat);
10168 }
10169 continue;
10170 }
10171 if (!(stat instanceof AST_Var)) continue;
10172 for (var j = 0; j < stat.definitions.length; j++) {
10173 var var_def = stat.definitions[j];
10174 var name = flatten_var(var_def.name);
10175 append_var(decl_var, expr_var, name, var_def.value);
10176 if (in_loop && !arg_used.has(name.name)) {
10177 var def = fn.variables.get(name.name);
10178 var sym = make_node(AST_SymbolRef, name, name);
10179 def.references.push(sym);
10180 expr_loop.push(make_node(AST_Assign, var_def, {
10181 operator: "=",
10182 left: sym,
10183 right: make_node(AST_Undefined, name),
10184 }));
10185 }
10186 }
10187 }
10188 [].push.apply(decls, decl_var);
10189 [].push.apply(expressions, expr_loop);
10190 [].push.apply(expressions, expr_var);
10191 return args;
10192 }
10193
10194 function flatten_fn() {
10195 var decls = [];
10196 var expressions = [];
10197 if (has_default > 1 || has_destructured || fn.rest) {
10198 flatten_destructured(decls, expressions);
10199 } else {
10200 flatten_args(decls, expressions);
10201 }
10202 var args = flatten_vars(decls, expressions);
10203 expressions.push(value);
10204 if (decls.length) args.push(make_node(AST_Var, fn, {
10205 definitions: decls
10206 }));
10207 [].splice.apply(scope.body, args);
10208 fn.enclosed.forEach(function(def) {
10209 if (scope.var_names().has(def.name)) return;
10210 scope.enclosed.push(def);
10211 scope.var_names().set(def.name, true);
10212 });
10213 return expressions;
10214 }
10215 });
10216
10217 OPT(AST_New, function(self, compressor) {
10218 if (compressor.option("sequences")) {
10219 var seq = lift_sequence_in_expression(self, compressor);
10220 if (seq !== self) return seq.optimize(compressor);
10221 }
10222 if (compressor.option("unused")) drop_unused_call_args(self, compressor);
10223 if (compressor.option("unsafe")) {
10224 var exp = self.expression;
10225 if (is_undeclared_ref(exp)) {
10226 switch (exp.name) {
10227 case "Object":
10228 case "RegExp":
10229 case "Function":
10230 case "Error":
10231 case "Array":
10232 return make_node(AST_Call, self, self).transform(compressor);
10233 }
10234 }
10235 }
10236 return self;
10237 });
10238
10239 // (a = b, x && a = c) ---> a = x ? c : b
10240 // (a = b, x || a = c) ---> a = x ? b : c
10241 function to_conditional_assignment(compressor, def, value, node) {
10242 if (!(node instanceof AST_Binary)) return;
10243 if (!(node.operator == "&&" || node.operator == "||")) return;
10244 if (!(node.right instanceof AST_Assign)) return;
10245 if (node.right.operator != "=") return;
10246 if (!(node.right.left instanceof AST_SymbolRef)) return;
10247 if (node.right.left.definition() !== def) return;
10248 if (value.has_side_effects(compressor)) return;
10249 if (!safe_from_assignment(node.left)) return;
10250 if (!safe_from_assignment(node.right.right)) return;
10251 def.replaced++;
10252 return node.operator == "&&" ? make_node(AST_Conditional, node, {
10253 condition: node.left,
10254 consequent: node.right.right,
10255 alternative: value
10256 }) : make_node(AST_Conditional, node, {
10257 condition: node.left,
10258 consequent: value,
10259 alternative: node.right.right
10260 });
10261
10262 function safe_from_assignment(node) {
10263 if (node.has_side_effects(compressor)) return;
10264 var hit = false;
10265 node.walk(new TreeWalker(function(node) {
10266 if (hit) return true;
10267 if (node instanceof AST_SymbolRef && node.definition() === def) return hit = true;
10268 }));
10269 return !hit;
10270 }
10271 }
10272
10273 OPT(AST_Sequence, function(self, compressor) {
10274 var expressions = filter_for_side_effects();
10275 var end = expressions.length - 1;
10276 merge_assignments();
10277 trim_right_for_undefined();
10278 if (end == 0) {
10279 self = maintain_this_binding(compressor, compressor.parent(), compressor.self(), expressions[0]);
10280 if (!(self instanceof AST_Sequence)) self = self.optimize(compressor);
10281 return self;
10282 }
10283 self.expressions = expressions;
10284 return self;
10285
10286 function filter_for_side_effects() {
10287 if (!compressor.option("side_effects")) return self.expressions;
10288 var expressions = [];
10289 var first = first_in_statement(compressor);
10290 var last = self.expressions.length - 1;
10291 self.expressions.forEach(function(expr, index) {
10292 if (index < last) expr = expr.drop_side_effect_free(compressor, first);
10293 if (expr) {
10294 merge_sequence(expressions, expr);
10295 first = false;
10296 }
10297 });
10298 return expressions;
10299 }
10300
10301 function trim_right_for_undefined() {
10302 if (!compressor.option("side_effects")) return;
10303 while (end > 0 && is_undefined(expressions[end], compressor)) end--;
10304 if (end < expressions.length - 1) {
10305 expressions[end] = make_node(AST_UnaryPrefix, self, {
10306 operator : "void",
10307 expression : expressions[end]
10308 });
10309 expressions.length = end + 1;
10310 }
10311 }
10312
10313 function is_simple_assign(node) {
10314 return node instanceof AST_Assign
10315 && node.operator == "="
10316 && node.left instanceof AST_SymbolRef
10317 && node.left.definition();
10318 }
10319
10320 function merge_assignments() {
10321 for (var i = 1; i < end; i++) {
10322 var prev = expressions[i - 1];
10323 var def = is_simple_assign(prev);
10324 if (!def) continue;
10325 var expr = expressions[i];
10326 if (compressor.option("conditionals")) {
10327 var cond = to_conditional_assignment(compressor, def, prev.right, expr);
10328 if (cond) {
10329 prev.right = cond;
10330 expressions.splice(i--, 1);
10331 end--;
10332 continue;
10333 }
10334 }
10335 if (compressor.option("dead_code")
10336 && is_simple_assign(expr) === def
10337 && expr.right.is_constant_expression(def.scope.resolve())) {
10338 expressions[--i] = prev.right;
10339 }
10340 }
10341 }
10342 });
10343
10344 OPT(AST_UnaryPostfix, function(self, compressor) {
10345 if (compressor.option("sequences")) {
10346 var seq = lift_sequence_in_expression(self, compressor);
10347 if (seq !== self) return seq.optimize(compressor);
10348 }
10349 return try_evaluate(compressor, self);
10350 });
10351
10352 var SIGN_OPS = makePredicate("+ -");
10353 var MULTIPLICATIVE_OPS = makePredicate("* / %");
10354 OPT(AST_UnaryPrefix, function(self, compressor) {
10355 var op = self.operator;
10356 var exp = self.expression;
10357 if (compressor.option("evaluate") && op == "delete" && !may_not_delete(exp)) {
10358 return make_sequence(self, [ exp, make_node(AST_True, self) ]).optimize(compressor);
10359 }
10360 if (compressor.option("sequences") && can_lift()) {
10361 var seq = lift_sequence_in_expression(self, compressor);
10362 if (seq !== self) return seq.optimize(compressor);
10363 }
10364 if (compressor.option("side_effects") && op == "void") {
10365 exp = exp.drop_side_effect_free(compressor);
10366 if (!exp) return make_node(AST_Undefined, self).optimize(compressor);
10367 self.expression = exp;
10368 return self;
10369 }
10370 if (compressor.option("booleans")) {
10371 if (op == "!" && exp.is_truthy()) {
10372 return make_sequence(self, [ exp, make_node(AST_False, self) ]).optimize(compressor);
10373 } else if (compressor.in_boolean_context()) switch (op) {
10374 case "!":
10375 if (exp instanceof AST_UnaryPrefix && exp.operator == "!") {
10376 // !!foo ---> foo, if we're in boolean context
10377 return exp.expression;
10378 }
10379 if (exp instanceof AST_Binary) {
10380 self = best_of(compressor, self, exp.negate(compressor, first_in_statement(compressor)));
10381 }
10382 break;
10383 case "typeof":
10384 // typeof always returns a non-empty string, thus it's
10385 // always true in booleans
10386 AST_Node.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
10387 var exprs = [ make_node(AST_True, self) ];
10388 if (!(exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor))) exprs.unshift(exp);
10389 return make_sequence(self, exprs).optimize(compressor);
10390 }
10391 }
10392 if (op == "-" && exp instanceof AST_Infinity) exp = exp.transform(compressor);
10393 if (compressor.option("evaluate")
10394 && exp instanceof AST_Binary
10395 && SIGN_OPS[op]
10396 && MULTIPLICATIVE_OPS[exp.operator]
10397 && (exp.left.is_constant() || !exp.right.has_side_effects(compressor))) {
10398 return make_node(AST_Binary, self, {
10399 operator: exp.operator,
10400 left: make_node(AST_UnaryPrefix, exp.left, {
10401 operator: op,
10402 expression: exp.left
10403 }),
10404 right: exp.right
10405 });
10406 }
10407 // avoids infinite recursion of numerals
10408 return op == "-" && (exp instanceof AST_Number || exp instanceof AST_Infinity)
10409 ? self : try_evaluate(compressor, self);
10410
10411 function may_not_delete(node) {
10412 return node instanceof AST_Infinity
10413 || node instanceof AST_NaN
10414 || node instanceof AST_NewTarget
10415 || node instanceof AST_PropAccess
10416 || node instanceof AST_SymbolRef
10417 || node instanceof AST_Undefined;
10418 }
10419
10420 function can_lift() {
10421 switch (op) {
10422 case "delete":
10423 return !may_not_delete(exp.tail_node());
10424 case "typeof":
10425 return !is_undeclared_ref(exp.tail_node());
10426 default:
10427 return true;
10428 }
10429 }
10430 });
10431
10432 OPT(AST_Await, function(self, compressor) {
10433 if (!compressor.option("awaits")) return self;
10434 if (compressor.option("sequences")) {
10435 var seq = lift_sequence_in_expression(self, compressor);
10436 if (seq !== self) return seq.optimize(compressor);
10437 }
10438 if (compressor.option("side_effects")) {
10439 var exp = self.expression;
10440 if (exp instanceof AST_Await) return exp.optimize(compressor);
10441 if (exp instanceof AST_UnaryPrefix) {
10442 if (exp.expression instanceof AST_Await) return exp.optimize(compressor);
10443 if (exp.operator == "void") return make_node(AST_UnaryPrefix, self, {
10444 operator: "void",
10445 expression: make_node(AST_Await, self, { expression: exp.expression }),
10446 }).optimize(compressor);
10447 }
10448 for (var level = 0, node = self, parent; parent = compressor.parent(level++); node = parent) {
10449 if (is_arrow(parent)) {
10450 if (parent.value === node) return exp.optimize(compressor);
10451 } else if (parent instanceof AST_Return) {
10452 var drop = true;
10453 do {
10454 node = parent;
10455 parent = compressor.parent(level++);
10456 if (parent instanceof AST_Try && (parent.bfinally || parent.bcatch) !== node) {
10457 drop = false;
10458 break;
10459 }
10460 } while (parent && !(parent instanceof AST_Scope));
10461 if (drop) return exp.optimize(compressor);
10462 } else if (parent instanceof AST_Sequence) {
10463 if (parent.tail_node() === node) continue;
10464 }
10465 break;
10466 }
10467 }
10468 return self;
10469 });
10470
10471 OPT(AST_Yield, function(self, compressor) {
10472 if (!compressor.option("yields")) return self;
10473 if (compressor.option("sequences")) {
10474 var seq = lift_sequence_in_expression(self, compressor);
10475 if (seq !== self) return seq.optimize(compressor);
10476 }
10477 var exp = self.expression;
10478 if (self.nested && exp.TYPE == "Call") {
10479 var inlined = exp.clone().optimize(compressor);
10480 if (inlined.TYPE != "Call") return inlined;
10481 }
10482 return self;
10483 });
10484
10485 AST_Binary.DEFMETHOD("lift_sequences", function(compressor) {
10486 if (this.left instanceof AST_PropAccess) {
10487 if (!(this.left.expression instanceof AST_Sequence)) return this;
10488 var x = this.left.expression.expressions.slice();
10489 var e = this.clone();
10490 e.left = e.left.clone();
10491 e.left.expression = x.pop();
10492 x.push(e);
10493 return make_sequence(this, x);
10494 }
10495 if (this.left instanceof AST_Sequence) {
10496 var x = this.left.expressions.slice();
10497 var e = this.clone();
10498 e.left = x.pop();
10499 x.push(e);
10500 return make_sequence(this, x);
10501 }
10502 if (this.right instanceof AST_Sequence) {
10503 if (this.left.has_side_effects(compressor)) return this;
10504 var assign = this.operator == "=" && this.left instanceof AST_SymbolRef;
10505 var x = this.right.expressions;
10506 var last = x.length - 1;
10507 for (var i = 0; i < last; i++) {
10508 if (!assign && x[i].has_side_effects(compressor)) break;
10509 }
10510 if (i == last) {
10511 x = x.slice();
10512 var e = this.clone();
10513 e.right = x.pop();
10514 x.push(e);
10515 return make_sequence(this, x);
10516 }
10517 if (i > 0) {
10518 var e = this.clone();
10519 e.right = make_sequence(this.right, x.slice(i));
10520 x = x.slice(0, i);
10521 x.push(e);
10522 return make_sequence(this, x);
10523 }
10524 }
10525 return this;
10526 });
10527
10528 var indexFns = makePredicate("indexOf lastIndexOf");
10529 var commutativeOperators = makePredicate("== === != !== * & | ^");
10530 function is_object(node) {
10531 if (node instanceof AST_Assign) return node.operator == "=" && is_object(node.right);
10532 if (node instanceof AST_Sequence) return is_object(node.tail_node());
10533 if (node instanceof AST_SymbolRef) return is_object(node.fixed_value());
10534 return node instanceof AST_Array
10535 || node instanceof AST_Class
10536 || node instanceof AST_Lambda
10537 || node instanceof AST_New
10538 || node instanceof AST_Object;
10539 }
10540
10541 function is_primitive(compressor, node) {
10542 if (node.is_constant()) return true;
10543 if (node instanceof AST_Assign) return node.operator != "=" || is_primitive(compressor, node.right);
10544 if (node instanceof AST_Binary) {
10545 return !lazy_op[node.operator]
10546 || is_primitive(compressor, node.left) && is_primitive(compressor, node.right);
10547 }
10548 if (node instanceof AST_Conditional) {
10549 return is_primitive(compressor, node.consequent) && is_primitive(compressor, node.alternative);
10550 }
10551 if (node instanceof AST_Sequence) return is_primitive(compressor, node.tail_node());
10552 if (node instanceof AST_SymbolRef) {
10553 var fixed = node.fixed_value();
10554 return fixed && is_primitive(compressor, fixed);
10555 }
10556 if (node instanceof AST_Template) return !node.tag || is_raw_tag(compressor, node.tag);
10557 if (node instanceof AST_Unary) return true;
10558 }
10559
10560 function repeatable(compressor, node) {
10561 if (node instanceof AST_Dot) return repeatable(compressor, node.expression);
10562 if (node instanceof AST_Sub) {
10563 return repeatable(compressor, node.expression) && repeatable(compressor, node.property);
10564 }
10565 if (node instanceof AST_Symbol) return true;
10566 return !node.has_side_effects(compressor);
10567 }
10568
10569 OPT(AST_Binary, function(self, compressor) {
10570 function reversible() {
10571 return self.left.is_constant()
10572 || self.right.is_constant()
10573 || !self.left.has_side_effects(compressor)
10574 && !self.right.has_side_effects(compressor);
10575 }
10576 function reverse(op) {
10577 if (reversible()) {
10578 if (op) self.operator = op;
10579 var tmp = self.left;
10580 self.left = self.right;
10581 self.right = tmp;
10582 }
10583 }
10584 function swap_chain() {
10585 var rhs = self.right;
10586 self.left = make_node(AST_Binary, self, {
10587 operator: self.operator,
10588 left: self.left,
10589 right: rhs.left,
10590 start: self.left.start,
10591 end: rhs.left.end
10592 });
10593 self.right = rhs.right;
10594 self.left = self.left.transform(compressor);
10595 }
10596 if (commutativeOperators[self.operator]
10597 && self.right.is_constant()
10598 && !self.left.is_constant()
10599 && !(self.left instanceof AST_Binary
10600 && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
10601 // if right is a constant, whatever side effects the
10602 // left side might have could not influence the
10603 // result. hence, force switch.
10604 reverse();
10605 }
10606 if (compressor.option("sequences")) {
10607 var seq = self.lift_sequences(compressor);
10608 if (seq !== self) return seq.optimize(compressor);
10609 }
10610 if (compressor.option("assignments") && lazy_op[self.operator]) {
10611 var assign = self.right;
10612 // a || (a = x) ---> a = a || x
10613 // a && (a = x) ---> a = a && x
10614 if (self.left instanceof AST_SymbolRef
10615 && assign instanceof AST_Assign
10616 && assign.operator == "="
10617 && self.left.equivalent_to(assign.left)) {
10618 self.right = assign.right;
10619 assign.right = self;
10620 return assign;
10621 }
10622 }
10623 if (compressor.option("comparisons")) switch (self.operator) {
10624 case "===":
10625 case "!==":
10626 if (is_undefined(self.left, compressor) && self.right.is_defined(compressor)) {
10627 AST_Node.warn("Expression always defined [{file}:{line},{col}]", self.start);
10628 return make_sequence(self, [
10629 self.right,
10630 make_node(self.operator == "===" ? AST_False : AST_True, self)
10631 ]).optimize(compressor);
10632 }
10633 var is_strict_comparison = true;
10634 if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
10635 (self.left.is_number(compressor) && self.right.is_number(compressor)) ||
10636 (self.left.is_boolean(compressor) && self.right.is_boolean(compressor)) ||
10637 repeatable(compressor, self.left) && self.left.equivalent_to(self.right)) {
10638 self.operator = self.operator.slice(0, 2);
10639 }
10640 // XXX: intentionally falling down to the next case
10641 case "==":
10642 case "!=":
10643 // void 0 == x ---> null == x
10644 if (!is_strict_comparison && is_undefined(self.left, compressor)) {
10645 self.left = make_node(AST_Null, self.left);
10646 }
10647 // "undefined" == typeof x ---> undefined === x
10648 else if (compressor.option("typeofs")
10649 && self.left instanceof AST_String
10650 && self.left.value == "undefined"
10651 && self.right instanceof AST_UnaryPrefix
10652 && self.right.operator == "typeof") {
10653 var expr = self.right.expression;
10654 if (expr instanceof AST_SymbolRef ? expr.is_declared(compressor)
10655 : !(expr instanceof AST_PropAccess && compressor.option("ie"))) {
10656 self.right = expr;
10657 self.left = make_node(AST_Undefined, self.left).optimize(compressor);
10658 if (self.operator.length == 2) self.operator += "=";
10659 }
10660 }
10661 // obj !== obj ---> false
10662 else if (self.left instanceof AST_SymbolRef
10663 && self.right instanceof AST_SymbolRef
10664 && self.left.definition() === self.right.definition()
10665 && is_object(self.left)) {
10666 return make_node(self.operator[0] == "=" ? AST_True : AST_False, self).optimize(compressor);
10667 }
10668 break;
10669 case "&&":
10670 case "||":
10671 // void 0 !== x && null !== x ---> null != x
10672 // void 0 === x || null === x ---> null == x
10673 var lhs = self.left;
10674 if (lhs.operator == self.operator) {
10675 lhs = lhs.right;
10676 }
10677 if (lhs instanceof AST_Binary
10678 && lhs.operator == (self.operator == "&&" ? "!==" : "===")
10679 && self.right instanceof AST_Binary
10680 && lhs.operator == self.right.operator
10681 && (is_undefined(lhs.left, compressor) && self.right.left instanceof AST_Null
10682 || lhs.left instanceof AST_Null && is_undefined(self.right.left, compressor))
10683 && !lhs.right.has_side_effects(compressor)
10684 && lhs.right.equivalent_to(self.right.right)) {
10685 var combined = make_node(AST_Binary, self, {
10686 operator: lhs.operator.slice(0, -1),
10687 left: make_node(AST_Null, self),
10688 right: lhs.right
10689 });
10690 if (lhs !== self.left) {
10691 combined = make_node(AST_Binary, self, {
10692 operator: self.operator,
10693 left: self.left.left,
10694 right: combined
10695 });
10696 }
10697 return combined;
10698 }
10699 break;
10700 }
10701 var in_bool = false;
10702 var parent = compressor.parent();
10703 if (compressor.option("booleans")) {
10704 var lhs = self.left;
10705 if (lazy_op[self.operator] && !lhs.has_side_effects(compressor)) {
10706 if (lhs.equivalent_to(self.right)) {
10707 return maintain_this_binding(compressor, parent, compressor.self(), lhs).optimize(compressor);
10708 }
10709 mark_duplicate_condition(compressor, lhs);
10710 }
10711 in_bool = compressor.in_boolean_context();
10712 }
10713 if (in_bool) switch (self.operator) {
10714 case "+":
10715 var ll = self.left.evaluate(compressor);
10716 var rr = self.right.evaluate(compressor);
10717 if (ll && typeof ll == "string") {
10718 AST_Node.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
10719 return make_sequence(self, [
10720 self.right,
10721 make_node(AST_True, self)
10722 ]).optimize(compressor);
10723 }
10724 if (rr && typeof rr == "string") {
10725 AST_Node.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
10726 return make_sequence(self, [
10727 self.left,
10728 make_node(AST_True, self)
10729 ]).optimize(compressor);
10730 }
10731 break;
10732 case "==":
10733 if (self.left instanceof AST_String && self.left.value == "" && self.right.is_string(compressor)) {
10734 return make_node(AST_UnaryPrefix, self, {
10735 operator: "!",
10736 expression: self.right
10737 }).optimize(compressor);
10738 }
10739 break;
10740 case "!=":
10741 if (self.left instanceof AST_String && self.left.value == "" && self.right.is_string(compressor)) {
10742 return self.right.optimize(compressor);
10743 }
10744 break;
10745 }
10746 if (compressor.option("comparisons") && self.is_boolean(compressor)) {
10747 if (!(parent instanceof AST_Binary) || parent instanceof AST_Assign) {
10748 var negated = best_of(compressor, self, make_node(AST_UnaryPrefix, self, {
10749 operator: "!",
10750 expression: self.negate(compressor, first_in_statement(compressor))
10751 }));
10752 if (negated !== self) return negated;
10753 }
10754 switch (self.operator) {
10755 case ">": reverse("<"); break;
10756 case ">=": reverse("<="); break;
10757 }
10758 }
10759 // x && (y && z) ---> x && y && z
10760 // x || (y || z) ---> x || y || z
10761 if (compressor.option("conditionals")
10762 && lazy_op[self.operator]
10763 && self.right instanceof AST_Binary
10764 && self.operator == self.right.operator) {
10765 swap_chain();
10766 }
10767 if (compressor.option("strings") && self.operator == "+") {
10768 // "foo" + 42 + "" ---> "foo" + 42
10769 if (self.right instanceof AST_String
10770 && self.right.value == ""
10771 && self.left.is_string(compressor)) {
10772 return self.left.optimize(compressor);
10773 }
10774 // "" + ("foo" + 42) ---> "foo" + 42
10775 if (self.left instanceof AST_String
10776 && self.left.value == ""
10777 && self.right.is_string(compressor)) {
10778 return self.right.optimize(compressor);
10779 }
10780 // "" + 42 + "foo" ---> 42 + "foo"
10781 if (self.left instanceof AST_Binary
10782 && self.left.operator == "+"
10783 && self.left.left instanceof AST_String
10784 && self.left.left.value == ""
10785 && self.right.is_string(compressor)
10786 && (self.left.right.is_constant() || !self.right.has_side_effects(compressor))) {
10787 self.left = self.left.right;
10788 return self.optimize(compressor);
10789 }
10790 // "x" + (y + "z") ---> "x" + y + "z"
10791 // x + ("y" + z) ---> x + "y" + z
10792 if (self.right instanceof AST_Binary
10793 && self.operator == self.right.operator
10794 && (self.left.is_string(compressor) && self.right.is_string(compressor)
10795 || self.right.left.is_string(compressor)
10796 && (self.left.is_constant() || !self.right.right.has_side_effects(compressor)))) {
10797 swap_chain();
10798 }
10799 }
10800 if (compressor.option("evaluate")) {
10801 var associative = true;
10802 switch (self.operator) {
10803 case "&&":
10804 var ll = fuzzy_eval(compressor, self.left);
10805 if (!ll) {
10806 AST_Node.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
10807 return maintain_this_binding(compressor, parent, compressor.self(), self.left).optimize(compressor);
10808 } else if (!(ll instanceof AST_Node)) {
10809 AST_Node.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
10810 return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
10811 }
10812 var rr = self.right.evaluate(compressor);
10813 if (!rr) {
10814 if (in_bool) {
10815 AST_Node.warn("Boolean && always false [{file}:{line},{col}]", self.start);
10816 return make_sequence(self, [
10817 self.left,
10818 make_node(AST_False, self)
10819 ]).optimize(compressor);
10820 } else self.falsy = true;
10821 } else if (!(rr instanceof AST_Node)) {
10822 if (in_bool || parent.operator == "&&" && parent.left === compressor.self()) {
10823 AST_Node.warn("Dropping side-effect-free && [{file}:{line},{col}]", self.start);
10824 return self.left.optimize(compressor);
10825 }
10826 }
10827 // (x || false) && y ---> x ? y : false
10828 if (self.left.operator == "||") {
10829 var lr = fuzzy_eval(compressor, self.left.right);
10830 if (!lr) return make_node(AST_Conditional, self, {
10831 condition: self.left.left,
10832 consequent: self.right,
10833 alternative: self.left.right
10834 }).optimize(compressor);
10835 }
10836 break;
10837 case "??":
10838 var nullish = true;
10839 case "||":
10840 var ll = fuzzy_eval(compressor, self.left, nullish);
10841 if (nullish ? ll == null : !ll) {
10842 AST_Node.warn("Condition left of {operator} always {value} [{file}:{line},{col}]", {
10843 operator: self.operator,
10844 value: nullish ? "nulish" : "false",
10845 file: self.start.file,
10846 line: self.start.line,
10847 col: self.start.col,
10848 });
10849 return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
10850 } else if (!(ll instanceof AST_Node)) {
10851 AST_Node.warn("Condition left of {operator} always {value} [{file}:{line},{col}]", {
10852 operator: self.operator,
10853 value: nullish ? "defined" : "true",
10854 file: self.start.file,
10855 line: self.start.line,
10856 col: self.start.col,
10857 });
10858 return maintain_this_binding(compressor, parent, compressor.self(), self.left).optimize(compressor);
10859 }
10860 var rr = self.right.evaluate(compressor);
10861 if (!rr) {
10862 if (in_bool || parent.operator == "||" && parent.left === compressor.self()) {
10863 AST_Node.warn("Dropping side-effect-free {operator} [{file}:{line},{col}]", {
10864 operator: self.operator,
10865 file: self.start.file,
10866 line: self.start.line,
10867 col: self.start.col,
10868 });
10869 return self.left.optimize(compressor);
10870 }
10871 } else if (!nullish && !(rr instanceof AST_Node)) {
10872 if (in_bool) {
10873 AST_Node.warn("Boolean || always true [{file}:{line},{col}]", self.start);
10874 return make_sequence(self, [
10875 self.left,
10876 make_node(AST_True, self)
10877 ]).optimize(compressor);
10878 } else self.truthy = true;
10879 }
10880 // x && true || y ---> x ? true : y
10881 if (!nullish && self.left.operator == "&&") {
10882 var lr = fuzzy_eval(compressor, self.left.right);
10883 if (lr && !(lr instanceof AST_Node)) return make_node(AST_Conditional, self, {
10884 condition: self.left.left,
10885 consequent: self.left.right,
10886 alternative: self.right
10887 }).optimize(compressor);
10888 }
10889 break;
10890 case "+":
10891 // "foo" + ("bar" + x) ---> "foobar" + x
10892 if (self.left instanceof AST_Constant
10893 && self.right instanceof AST_Binary
10894 && self.right.operator == "+"
10895 && self.right.left instanceof AST_Constant
10896 && self.right.is_string(compressor)) {
10897 self = make_node(AST_Binary, self, {
10898 operator: "+",
10899 left: make_node(AST_String, self.left, {
10900 value: "" + self.left.value + self.right.left.value,
10901 start: self.left.start,
10902 end: self.right.left.end
10903 }),
10904 right: self.right.right
10905 });
10906 }
10907 // (x + "foo") + "bar" ---> x + "foobar"
10908 if (self.right instanceof AST_Constant
10909 && self.left instanceof AST_Binary
10910 && self.left.operator == "+"
10911 && self.left.right instanceof AST_Constant
10912 && self.left.is_string(compressor)) {
10913 self = make_node(AST_Binary, self, {
10914 operator: "+",
10915 left: self.left.left,
10916 right: make_node(AST_String, self.right, {
10917 value: "" + self.left.right.value + self.right.value,
10918 start: self.left.right.start,
10919 end: self.right.end
10920 })
10921 });
10922 }
10923 // a + -b ---> a - b
10924 if (self.right instanceof AST_UnaryPrefix
10925 && self.right.operator == "-"
10926 && self.left.is_number(compressor)) {
10927 self = make_node(AST_Binary, self, {
10928 operator: "-",
10929 left: self.left,
10930 right: self.right.expression
10931 });
10932 break;
10933 }
10934 // -a + b ---> b - a
10935 if (self.left instanceof AST_UnaryPrefix
10936 && self.left.operator == "-"
10937 && reversible()
10938 && self.right.is_number(compressor)) {
10939 self = make_node(AST_Binary, self, {
10940 operator: "-",
10941 left: self.right,
10942 right: self.left.expression
10943 });
10944 break;
10945 }
10946 // (a + b) + 3 ---> 3 + (a + b)
10947 if (compressor.option("unsafe_math")
10948 && self.left instanceof AST_Binary
10949 && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]
10950 && self.right.is_constant()
10951 && (self.right.is_boolean(compressor) || self.right.is_number(compressor))
10952 && self.left.is_number(compressor)
10953 && !self.left.right.is_constant()
10954 && (self.left.left.is_boolean(compressor) || self.left.left.is_number(compressor))) {
10955 self = make_node(AST_Binary, self, {
10956 operator: self.left.operator,
10957 left: make_node(AST_Binary, self, {
10958 operator: self.operator,
10959 left: self.right,
10960 right: self.left.left
10961 }),
10962 right: self.left.right
10963 });
10964 break;
10965 }
10966 case "-":
10967 // a - -b ---> a + b
10968 if (self.right instanceof AST_UnaryPrefix
10969 && self.right.operator == "-"
10970 && self.left.is_number(compressor)
10971 && self.right.expression.is_number(compressor)) {
10972 self = make_node(AST_Binary, self, {
10973 operator: "+",
10974 left: self.left,
10975 right: self.right.expression
10976 });
10977 break;
10978 }
10979 case "*":
10980 case "/":
10981 associative = compressor.option("unsafe_math");
10982 // +a - b ---> a - b
10983 // a - +b ---> a - b
10984 if (self.operator != "+") [ "left", "right" ].forEach(function(operand) {
10985 var node = self[operand];
10986 if (node instanceof AST_UnaryPrefix && node.operator == "+") {
10987 var exp = node.expression;
10988 if (exp.is_boolean(compressor) || exp.is_number(compressor) || exp.is_string(compressor)) {
10989 self[operand] = exp;
10990 }
10991 }
10992 });
10993 case "&":
10994 case "|":
10995 case "^":
10996 // a + +b ---> +b + a
10997 if (self.operator != "-"
10998 && self.operator != "/"
10999 && (self.left.is_boolean(compressor) || self.left.is_number(compressor))
11000 && (self.right.is_boolean(compressor) || self.right.is_number(compressor))
11001 && reversible()
11002 && !(self.left instanceof AST_Binary
11003 && self.left.operator != self.operator
11004 && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
11005 var reversed = make_node(AST_Binary, self, {
11006 operator: self.operator,
11007 left: self.right,
11008 right: self.left
11009 });
11010 if (self.right instanceof AST_Constant
11011 && !(self.left instanceof AST_Constant)) {
11012 self = best_of(compressor, reversed, self);
11013 } else {
11014 self = best_of(compressor, self, reversed);
11015 }
11016 }
11017 if (!associative || !self.is_number(compressor)) break;
11018 // a + (b + c) ---> (a + b) + c
11019 if (self.right instanceof AST_Binary
11020 && self.right.operator != "%"
11021 && PRECEDENCE[self.right.operator] == PRECEDENCE[self.operator]
11022 && self.right.is_number(compressor)
11023 && (self.operator != "+"
11024 || self.right.left.is_boolean(compressor)
11025 || self.right.left.is_number(compressor))
11026 && (self.operator != "-" || !self.left.is_negative_zero())
11027 && (self.right.left.is_constant_expression()
11028 || !self.right.right.has_side_effects(compressor))
11029 && !is_modify_array(self.right.right)) {
11030 self = make_node(AST_Binary, self, {
11031 operator: align(self.operator, self.right.operator),
11032 left: make_node(AST_Binary, self.left, {
11033 operator: self.operator,
11034 left: self.left,
11035 right: self.right.left,
11036 start: self.left.start,
11037 end: self.right.left.end
11038 }),
11039 right: self.right.right
11040 });
11041 if (self.operator == "+"
11042 && !self.right.is_boolean(compressor)
11043 && !self.right.is_number(compressor)) {
11044 self.right = make_node(AST_UnaryPrefix, self.right, {
11045 operator: "+",
11046 expression: self.right
11047 });
11048 }
11049 }
11050 // (2 * n) * 3 ---> 6 * n
11051 // (n + 2) + 3 ---> n + 5
11052 if (self.right instanceof AST_Constant
11053 && self.left instanceof AST_Binary
11054 && self.left.operator != "%"
11055 && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]
11056 && self.left.is_number(compressor)) {
11057 if (self.left.left instanceof AST_Constant) {
11058 var lhs = make_binary(self.left, self.operator, self.left.left, self.right, self.left.left.start, self.right.end);
11059 self = make_binary(self, self.left.operator, try_evaluate(compressor, lhs), self.left.right);
11060 } else if (self.left.right instanceof AST_Constant) {
11061 var op = align(self.left.operator, self.operator);
11062 var rhs = try_evaluate(compressor, make_binary(self.left, op, self.left.right, self.right));
11063 if (rhs.is_constant()
11064 && !(self.left.operator == "-"
11065 && self.right.value != 0
11066 && +rhs.value == 0
11067 && self.left.left.is_negative_zero())) {
11068 self = make_binary(self, self.left.operator, self.left.left, rhs);
11069 }
11070 }
11071 }
11072 break;
11073 }
11074 if (!(parent instanceof AST_UnaryPrefix && parent.operator == "delete")) {
11075 if (self.left instanceof AST_Number && !self.right.is_constant()) switch (self.operator) {
11076 // 0 + n ---> n
11077 case "+":
11078 if (self.left.value == 0) {
11079 if (self.right.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, {
11080 operator: "+",
11081 expression: self.right
11082 }).optimize(compressor);
11083 if (self.right.is_number(compressor) && !self.right.is_negative_zero()) return self.right;
11084 }
11085 break;
11086 // 1 * n ---> n
11087 case "*":
11088 if (self.left.value == 1) {
11089 return self.right.is_number(compressor) ? self.right : make_node(AST_UnaryPrefix, self, {
11090 operator: "+",
11091 expression: self.right
11092 }).optimize(compressor);
11093 }
11094 break;
11095 }
11096 if (self.right instanceof AST_Number && !self.left.is_constant()) switch (self.operator) {
11097 // n + 0 ---> n
11098 case "+":
11099 if (self.right.value == 0) {
11100 if (self.left.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, {
11101 operator: "+",
11102 expression: self.left
11103 }).optimize(compressor);
11104 if (self.left.is_number(compressor) && !self.left.is_negative_zero()) return self.left;
11105 }
11106 break;
11107 // n - 0 ---> n
11108 case "-":
11109 if (self.right.value == 0) {
11110 return self.left.is_number(compressor) ? self.left : make_node(AST_UnaryPrefix, self, {
11111 operator: "+",
11112 expression: self.left
11113 }).optimize(compressor);
11114 }
11115 break;
11116 // n / 1 ---> n
11117 case "/":
11118 if (self.right.value == 1) {
11119 return self.left.is_number(compressor) ? self.left : make_node(AST_UnaryPrefix, self, {
11120 operator: "+",
11121 expression: self.left
11122 }).optimize(compressor);
11123 }
11124 break;
11125 }
11126 }
11127 }
11128 if (compressor.option("typeofs")) switch (self.operator) {
11129 case "&&":
11130 mark_locally_defined(self.left, self.right, null);
11131 break;
11132 case "||":
11133 mark_locally_defined(self.left, null, self.right);
11134 break;
11135 }
11136 if (compressor.option("unsafe")) {
11137 var indexRight = is_indexFn(self.right);
11138 if (in_bool
11139 && indexRight
11140 && (self.operator == "==" || self.operator == "!=")
11141 && self.left instanceof AST_Number
11142 && self.left.value == 0) {
11143 return (self.operator == "==" ? make_node(AST_UnaryPrefix, self, {
11144 operator: "!",
11145 expression: self.right
11146 }) : self.right).optimize(compressor);
11147 }
11148 var indexLeft = is_indexFn(self.left);
11149 if (compressor.option("comparisons") && is_indexOf_match_pattern()) {
11150 var node = make_node(AST_UnaryPrefix, self, {
11151 operator: "!",
11152 expression: make_node(AST_UnaryPrefix, self, {
11153 operator: "~",
11154 expression: indexLeft ? self.left : self.right
11155 })
11156 });
11157 switch (self.operator) {
11158 case "<":
11159 if (indexLeft) break;
11160 case "<=":
11161 case "!=":
11162 node = make_node(AST_UnaryPrefix, self, {
11163 operator: "!",
11164 expression: node
11165 });
11166 break;
11167 }
11168 return node.optimize(compressor);
11169 }
11170 }
11171 return try_evaluate(compressor, self);
11172
11173 function is_modify_array(node) {
11174 var found = false;
11175 node.walk(new TreeWalker(function(node) {
11176 if (found) return true;
11177 if (node instanceof AST_Assign) {
11178 if (node.left instanceof AST_PropAccess) return found = true;
11179 } else if (node instanceof AST_Unary) {
11180 if (unary_side_effects[node.operator] && node.expression instanceof AST_PropAccess) {
11181 return found = true;
11182 }
11183 }
11184 }));
11185 return found;
11186 }
11187
11188 function align(ref, op) {
11189 switch (ref) {
11190 case "-":
11191 return op == "+" ? "-" : "+";
11192 case "/":
11193 return op == "*" ? "/" : "*";
11194 default:
11195 return op;
11196 }
11197 }
11198
11199 function make_binary(orig, op, left, right, start, end) {
11200 if (op == "+") {
11201 if (!left.is_boolean(compressor) && !left.is_number(compressor)) {
11202 left = make_node(AST_UnaryPrefix, left, {
11203 operator: "+",
11204 expression: left
11205 });
11206 }
11207 if (!right.is_boolean(compressor) && !right.is_number(compressor)) {
11208 right = make_node(AST_UnaryPrefix, right, {
11209 operator: "+",
11210 expression: right
11211 });
11212 }
11213 }
11214 return make_node(AST_Binary, orig, {
11215 operator: op,
11216 left: left,
11217 right: right,
11218 start: start,
11219 end: end
11220 });
11221 }
11222
11223 function is_indexFn(node) {
11224 return node.TYPE == "Call"
11225 && node.expression instanceof AST_Dot
11226 && indexFns[node.expression.property];
11227 }
11228
11229 function is_indexOf_match_pattern() {
11230 switch (self.operator) {
11231 case "<=":
11232 // 0 <= array.indexOf(string) ---> !!~array.indexOf(string)
11233 return indexRight && self.left instanceof AST_Number && self.left.value == 0;
11234 case "<":
11235 // array.indexOf(string) < 0 ---> !~array.indexOf(string)
11236 if (indexLeft && self.right instanceof AST_Number && self.right.value == 0) return true;
11237 // -1 < array.indexOf(string) ---> !!~array.indexOf(string)
11238 case "==":
11239 case "!=":
11240 // -1 == array.indexOf(string) ---> !~array.indexOf(string)
11241 // -1 != array.indexOf(string) ---> !!~array.indexOf(string)
11242 if (!indexRight) return false;
11243 return self.left instanceof AST_Number && self.left.value == -1
11244 || self.left instanceof AST_UnaryPrefix && self.left.operator == "-"
11245 && self.left.expression instanceof AST_Number && self.left.expression.value == 1;
11246 }
11247 }
11248 });
11249
11250 OPT(AST_SymbolExport, function(self) {
11251 return self;
11252 });
11253
11254 function recursive_ref(compressor, def, fn) {
11255 var level = 0, node = compressor.self();
11256 do {
11257 if (node === fn) return node;
11258 if (is_lambda(node) && node.name && node.name.definition() === def) return node;
11259 } while (node = compressor.parent(level++));
11260 }
11261
11262 function same_scope(def) {
11263 var scope = def.scope.resolve();
11264 return all(def.references, function(ref) {
11265 return scope === ref.scope.resolve();
11266 });
11267 }
11268
11269 OPT(AST_SymbolRef, function(self, compressor) {
11270 if (!compressor.option("ie")
11271 && is_undeclared_ref(self)
11272 // testing against `self.scope.uses_with` is an optimization
11273 && !(self.scope.resolve().uses_with && compressor.find_parent(AST_With))) {
11274 switch (self.name) {
11275 case "undefined":
11276 return make_node(AST_Undefined, self).optimize(compressor);
11277 case "NaN":
11278 return make_node(AST_NaN, self).optimize(compressor);
11279 case "Infinity":
11280 return make_node(AST_Infinity, self).optimize(compressor);
11281 }
11282 }
11283 var parent = compressor.parent();
11284 if (compressor.option("reduce_vars") && is_lhs(compressor.self(), parent) !== compressor.self()) {
11285 var def = self.definition();
11286 var fixed = self.fixed_value();
11287 var single_use = def.single_use && !(parent instanceof AST_Call && parent.is_expr_pure(compressor));
11288 if (single_use) {
11289 if (is_lambda(fixed)) {
11290 if ((def.scope !== self.scope.resolve() || def.in_loop)
11291 && (!compressor.option("reduce_funcs") || def.escaped.depth == 1 || fixed.inlined)) {
11292 single_use = false;
11293 } else if (recursive_ref(compressor, def, fixed)) {
11294 single_use = false;
11295 } else if (fixed.name && fixed.name.definition() !== def) {
11296 single_use = false;
11297 } else if (fixed.parent_scope !== self.scope || is_funarg(def)) {
11298 single_use = fixed.is_constant_expression(self.scope);
11299 if (single_use == "f") {
11300 var scope = self.scope;
11301 do {
11302 if (scope instanceof AST_LambdaDefinition || scope instanceof AST_LambdaExpression) {
11303 scope.inlined = true;
11304 }
11305 } while (scope = scope.parent_scope);
11306 }
11307 } else if (fixed.name && (fixed.name.name == "await" && is_async(fixed)
11308 || fixed.name.name == "yield" && is_generator(fixed))) {
11309 single_use = false;
11310 } else if (fixed.has_side_effects(compressor)) {
11311 single_use = false;
11312 } else if (compressor.option("ie") && fixed instanceof AST_Class) {
11313 single_use = false;
11314 }
11315 if (single_use) fixed.parent_scope = self.scope;
11316 } else if (!fixed
11317 || def.recursive_refs > 0
11318 || !fixed.is_constant_expression()
11319 || fixed.drop_side_effect_free(compressor)) {
11320 single_use = false;
11321 }
11322 }
11323 if (single_use) {
11324 def.single_use = false;
11325 fixed._squeezed = true;
11326 fixed.single_use = true;
11327 if (fixed instanceof AST_DefClass) fixed = to_class_expr(fixed);
11328 if (fixed instanceof AST_LambdaDefinition) fixed = to_func_expr(fixed);
11329 if (is_lambda(fixed)) {
11330 var scope = self.scope.resolve();
11331 fixed.enclosed.forEach(function(def) {
11332 if (fixed.variables.has(def.name)) return;
11333 if (scope.var_names().has(def.name)) return;
11334 scope.enclosed.push(def);
11335 scope.var_names().set(def.name, true);
11336 });
11337 }
11338 var value;
11339 if (def.recursive_refs > 0) {
11340 value = fixed.clone(true);
11341 var defun_def = value.name.definition();
11342 var lambda_def = value.variables.get(value.name.name);
11343 var name = lambda_def && lambda_def.orig[0];
11344 var def_fn_name, symbol_type;
11345 if (value instanceof AST_Class) {
11346 def_fn_name = "def_function";
11347 symbol_type = AST_SymbolClass;
11348 } else {
11349 def_fn_name = "def_variable";
11350 symbol_type = AST_SymbolLambda;
11351 }
11352 if (!(name instanceof symbol_type)) {
11353 name = make_node(symbol_type, value.name, value.name);
11354 name.scope = value;
11355 value.name = name;
11356 lambda_def = value[def_fn_name](name);
11357 lambda_def.recursive_refs = def.recursive_refs;
11358 }
11359 value.walk(new TreeWalker(function(node) {
11360 if (node instanceof AST_SymbolDeclaration) {
11361 if (node !== name) {
11362 var def = node.definition();
11363 def.orig.push(node);
11364 def.eliminated++;
11365 }
11366 return;
11367 }
11368 if (!(node instanceof AST_SymbolRef)) return;
11369 var def = node.definition();
11370 if (def === defun_def) {
11371 node.thedef = def = lambda_def;
11372 } else {
11373 def.single_use = false;
11374 var fn = node.fixed_value();
11375 if (is_lambda(fn)
11376 && fn.name
11377 && fn.name.definition() === def
11378 && def.scope === fn.name.scope
11379 && fixed.variables.get(fn.name.name) === def) {
11380 fn.name = fn.name.clone();
11381 node.thedef = def = value.variables.get(fn.name.name) || value[def_fn_name](fn.name);
11382 }
11383 }
11384 def.references.push(node);
11385 }));
11386 } else {
11387 if (fixed instanceof AST_Scope) {
11388 compressor.push(fixed);
11389 value = fixed.optimize(compressor);
11390 compressor.pop();
11391 } else {
11392 value = fixed.optimize(compressor);
11393 }
11394 value = value.transform(new TreeTransformer(function(node, descend) {
11395 if (node instanceof AST_Scope) return node;
11396 node = node.clone();
11397 descend(node, this);
11398 return node;
11399 }));
11400 }
11401 def.replaced++;
11402 return value;
11403 }
11404 var local = self.fixed !== def.fixed;
11405 if (fixed && (local || def.should_replace !== false)) {
11406 var ev, init;
11407 if (fixed instanceof AST_This) {
11408 if (!is_funarg(def) && same_scope(def)) init = fixed;
11409 } else if ((ev = fixed.evaluate(compressor, true)) !== fixed
11410 && typeof ev != "function"
11411 && (ev === null
11412 || typeof ev != "object"
11413 || compressor.option("unsafe_regexp")
11414 && ev instanceof RegExp && !def.cross_loop && same_scope(def))) {
11415 init = make_node_from_constant(ev, fixed);
11416 }
11417 if (init) {
11418 if (!local && def.should_replace === undefined) {
11419 var value_length = init.optimize(compressor).print_to_string().length;
11420 if (!has_symbol_ref(fixed)) {
11421 value_length = Math.min(value_length, fixed.print_to_string().length);
11422 }
11423 var name_length = def.name.length;
11424 if (compressor.option("unused") && !compressor.exposed(def)) {
11425 var referenced = def.references.length - def.replaced;
11426 name_length += (name_length + 2 + value_length) / (referenced - def.assignments);
11427 }
11428 var delta = value_length - Math.floor(name_length);
11429 def.should_replace = delta < compressor.eval_threshold;
11430 }
11431 if (local || def.should_replace) {
11432 var value;
11433 if (has_symbol_ref(fixed)) {
11434 value = init.optimize(compressor);
11435 if (value === init) value = value.clone(true);
11436 } else {
11437 value = best_of_expression(init.optimize(compressor), fixed);
11438 if (value === init || value === fixed) value = value.clone(true);
11439 }
11440 def.replaced++;
11441 return value;
11442 }
11443 }
11444 }
11445 }
11446 return self;
11447
11448 function has_symbol_ref(value) {
11449 var found;
11450 value.walk(new TreeWalker(function(node) {
11451 if (node instanceof AST_SymbolRef) found = true;
11452 if (found) return true;
11453 }));
11454 return found;
11455 }
11456 });
11457
11458 function is_raw_tag(compressor, tag) {
11459 return compressor.option("unsafe")
11460 && tag instanceof AST_Dot
11461 && tag.property == "raw"
11462 && is_undeclared_ref(tag.expression)
11463 && tag.expression.name == "String";
11464 }
11465
11466 function decode_template(str) {
11467 var malformed = false;
11468 str = str.replace(/\\(u\{[^{}]*\}?|u[\s\S]{0,4}|x[\s\S]{0,2}|[0-9]+|[\s\S])/g, function(match, seq) {
11469 var ch = decode_escape_sequence(seq);
11470 if (typeof ch == "string") return ch;
11471 malformed = true;
11472 });
11473 if (!malformed) return str;
11474 }
11475
11476 OPT(AST_Template, function(self, compressor) {
11477 if (!compressor.option("templates")) return self;
11478 var tag = self.tag;
11479 if (!tag || is_raw_tag(compressor, tag)) {
11480 var exprs = [];
11481 var strs = [];
11482 for (var i = 0, status; i < self.strings.length; i++) {
11483 var str = self.strings[i];
11484 if (!tag) {
11485 var trimmed = decode_template(str);
11486 if (trimmed) str = escape_literal(trimmed);
11487 }
11488 if (i > 0) {
11489 var node = self.expressions[i - 1];
11490 var value = should_join(node);
11491 if (value) {
11492 var prev = strs[strs.length - 1];
11493 var joined = prev + value + str;
11494 var decoded;
11495 if (tag || typeof (decoded = decode_template(joined)) == status) {
11496 strs[strs.length - 1] = decoded ? escape_literal(decoded) : joined;
11497 continue;
11498 }
11499 }
11500 exprs.push(node);
11501 }
11502 strs.push(str);
11503 if (!tag) status = typeof trimmed;
11504 }
11505 if (!tag && strs.length > 1) {
11506 if (strs[strs.length - 1] == "") return make_node(AST_Binary, self, {
11507 operator: "+",
11508 left: make_node(AST_Template, self, {
11509 expressions: exprs.slice(0, -1),
11510 strings: strs.slice(0, -1),
11511 tag: tag,
11512 }).transform(compressor),
11513 right: exprs[exprs.length - 1],
11514 }).optimize(compressor);
11515 if (strs[0] == "") {
11516 var left = make_node(AST_Binary, self, {
11517 operator: "+",
11518 left: make_node(AST_String, self, { value: "" }),
11519 right: exprs[0],
11520 });
11521 for (var i = 1; strs[i] == "" && i < exprs.length; i++) {
11522 left = make_node(AST_Binary, self, {
11523 operator: "+",
11524 left: left,
11525 right: exprs[i],
11526 });
11527 }
11528 return best_of(compressor, self, make_node(AST_Binary, self, {
11529 operator: "+",
11530 left: left.transform(compressor),
11531 right: make_node(AST_Template, self, {
11532 expressions: exprs.slice(i),
11533 strings: strs.slice(i),
11534 tag: tag,
11535 }).transform(compressor),
11536 }).optimize(compressor));
11537 }
11538 }
11539 self.expressions = exprs;
11540 self.strings = strs;
11541 }
11542 return try_evaluate(compressor, self);
11543
11544 function escape_literal(str) {
11545 return str.replace(/\r|\\|`|\${/g, function(s) {
11546 return "\\" + (s == "\r" ? "r" : s);
11547 });
11548 }
11549
11550 function should_join(node) {
11551 var ev = node.evaluate(compressor);
11552 if (ev === node) return;
11553 if (tag && /\r|\\|`/.test(ev)) return;
11554 ev = escape_literal("" + ev);
11555 if (ev.length > node.print_to_string().length + "${}".length) return;
11556 return ev;
11557 }
11558 });
11559
11560 function is_atomic(lhs, self) {
11561 return lhs instanceof AST_SymbolRef || lhs.TYPE === self.TYPE;
11562 }
11563
11564 OPT(AST_Undefined, function(self, compressor) {
11565 if (compressor.option("unsafe_undefined")) {
11566 var undef = find_scope(compressor).find_variable("undefined");
11567 if (undef) {
11568 var ref = make_node(AST_SymbolRef, self, {
11569 name : "undefined",
11570 scope : undef.scope,
11571 thedef : undef
11572 });
11573 ref.is_undefined = true;
11574 return ref;
11575 }
11576 }
11577 var lhs = is_lhs(compressor.self(), compressor.parent());
11578 if (lhs && is_atomic(lhs, self)) return self;
11579 return make_node(AST_UnaryPrefix, self, {
11580 operator: "void",
11581 expression: make_node(AST_Number, self, {
11582 value: 0
11583 })
11584 });
11585 });
11586
11587 OPT(AST_Infinity, function(self, compressor) {
11588 var lhs = is_lhs(compressor.self(), compressor.parent());
11589 if (lhs && is_atomic(lhs, self)) return self;
11590 if (compressor.option("keep_infinity") && !lhs && !find_scope(compressor).find_variable("Infinity")) {
11591 return self;
11592 }
11593 return make_node(AST_Binary, self, {
11594 operator: "/",
11595 left: make_node(AST_Number, self, {
11596 value: 1
11597 }),
11598 right: make_node(AST_Number, self, {
11599 value: 0
11600 })
11601 });
11602 });
11603
11604 OPT(AST_NaN, function(self, compressor) {
11605 var lhs = is_lhs(compressor.self(), compressor.parent());
11606 if (lhs && is_atomic(lhs, self)) return self;
11607 if (!lhs && !find_scope(compressor).find_variable("NaN")) return self;
11608 return make_node(AST_Binary, self, {
11609 operator: "/",
11610 left: make_node(AST_Number, self, {
11611 value: 0
11612 }),
11613 right: make_node(AST_Number, self, {
11614 value: 0
11615 })
11616 });
11617 });
11618
11619 function is_reachable(self, defs) {
11620 var reachable = false;
11621 var find_ref = new TreeWalker(function(node) {
11622 if (reachable) return true;
11623 if (node instanceof AST_SymbolRef && member(node.definition(), defs)) {
11624 return reachable = true;
11625 }
11626 });
11627 var scan_scope = new TreeWalker(function(node) {
11628 if (reachable) return true;
11629 if (node instanceof AST_Lambda && node !== self) {
11630 if (!(node.name || is_async(node) || is_generator(node))) {
11631 var parent = scan_scope.parent();
11632 if (parent instanceof AST_Call && parent.expression === node) return;
11633 }
11634 node.walk(find_ref);
11635 return true;
11636 }
11637 });
11638 self.walk(scan_scope);
11639 return reachable;
11640 }
11641
11642 var ASSIGN_OPS = makePredicate("+ - * / % >> << >>> | ^ &");
11643 var ASSIGN_OPS_COMMUTATIVE = makePredicate("* | ^ &");
11644 OPT(AST_Assign, function(self, compressor) {
11645 if (compressor.option("dead_code")) {
11646 if (self.left instanceof AST_PropAccess) {
11647 if (self.operator == "=") {
11648 if (self.redundant) {
11649 var exprs = [ self.left.expression ];
11650 if (self.left instanceof AST_Sub) exprs.push(self.left.property);
11651 exprs.push(self.right);
11652 return make_sequence(self, exprs).optimize(compressor);
11653 }
11654 if (self.left.equivalent_to(self.right) && !self.left.has_side_effects(compressor)) {
11655 return self.right;
11656 }
11657 var exp = self.left.expression;
11658 if (exp instanceof AST_Lambda
11659 || !compressor.has_directive("use strict")
11660 && exp instanceof AST_Constant
11661 && !exp.may_throw_on_access(compressor)) {
11662 return self.left instanceof AST_Dot ? self.right : make_sequence(self, [
11663 self.left.property,
11664 self.right
11665 ]).optimize(compressor);
11666 }
11667 }
11668 } else if (self.left instanceof AST_SymbolRef && can_drop_symbol(self.left, compressor)) {
11669 var parent;
11670 if (self.operator == "=" && self.left.equivalent_to(self.right)
11671 && !((parent = compressor.parent()) instanceof AST_UnaryPrefix && parent.operator == "delete")) {
11672 return self.right;
11673 }
11674 if (self.left.is_immutable()) return strip_assignment();
11675 var def = self.left.definition();
11676 var scope = def.scope.resolve();
11677 var local = scope === compressor.find_parent(AST_Lambda);
11678 var level = 0, node;
11679 parent = compressor.self();
11680 if (!(scope.uses_arguments && is_funarg(def)) || compressor.has_directive("use strict")) do {
11681 node = parent;
11682 parent = compressor.parent(level++);
11683 if (parent instanceof AST_Assign) {
11684 if (parent.left instanceof AST_SymbolRef && parent.left.definition() === def) {
11685 if (in_try(level, parent)) break;
11686 return strip_assignment(def);
11687 }
11688 if (parent.left.match_symbol(function(node) {
11689 if (node instanceof AST_PropAccess) return true;
11690 })) break;
11691 continue;
11692 }
11693 if (parent instanceof AST_Exit) {
11694 if (!local) break;
11695 if (in_try(level, parent)) break;
11696 if (is_reachable(scope, [ def ])) break;
11697 return strip_assignment(def);
11698 }
11699 if (parent instanceof AST_SimpleStatement) {
11700 if (!local) break;
11701 if (is_reachable(scope, [ def ])) break;
11702 var stat;
11703 do {
11704 stat = parent;
11705 parent = compressor.parent(level++);
11706 if (parent === scope && is_last_statement(parent.body, stat)) return strip_assignment(def);
11707 } while (is_tail_block(stat, parent));
11708 break;
11709 }
11710 if (parent instanceof AST_VarDef) {
11711 if (!(parent.name instanceof AST_SymbolDeclaration)) continue;
11712 if (parent.name.definition() !== def) continue;
11713 if (in_try(level, parent)) break;
11714 return strip_assignment(def);
11715 }
11716 } while (is_tail(node, parent));
11717 }
11718 }
11719 if (compressor.option("sequences")) {
11720 var seq = self.lift_sequences(compressor);
11721 if (seq !== self) return seq.optimize(compressor);
11722 }
11723 if (compressor.option("assignments")) {
11724 if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) {
11725 // x = expr1 OP expr2
11726 if (self.right.left instanceof AST_SymbolRef
11727 && self.right.left.name == self.left.name
11728 && ASSIGN_OPS[self.right.operator]) {
11729 // x = x - 2 ---> x -= 2
11730 return make_node(AST_Assign, self, {
11731 operator: self.right.operator + "=",
11732 left: self.left,
11733 right: self.right.right,
11734 });
11735 }
11736 if (self.right.right instanceof AST_SymbolRef
11737 && self.right.right.name == self.left.name
11738 && ASSIGN_OPS_COMMUTATIVE[self.right.operator]
11739 && !self.right.left.has_side_effects(compressor)) {
11740 // x = 2 & x ---> x &= 2
11741 return make_node(AST_Assign, self, {
11742 operator: self.right.operator + "=",
11743 left: self.left,
11744 right: self.right.left,
11745 });
11746 }
11747 }
11748 if ((self.operator == "-=" || self.operator == "+="
11749 && (self.left.is_boolean(compressor) || self.left.is_number(compressor)))
11750 && self.right instanceof AST_Number
11751 && self.right.value == 1) {
11752 var op = self.operator.slice(0, -1);
11753 return make_node(AST_UnaryPrefix, self, {
11754 operator: op + op,
11755 expression: self.left
11756 });
11757 }
11758 }
11759 return try_evaluate(compressor, self);
11760
11761 function is_tail(node, parent) {
11762 if (parent instanceof AST_Binary) {
11763 return parent.right === node || parent.right.is_constant_expression(scope);
11764 }
11765 if (parent instanceof AST_Conditional) {
11766 return parent.condition !== node
11767 || parent.consequent.is_constant_expression(scope)
11768 && parent.alternative.is_constant_expression(scope);
11769 }
11770 if (parent instanceof AST_Sequence) {
11771 var exprs = parent.expressions;
11772 var stop = exprs.indexOf(node);
11773 if (stop < 0) return false;
11774 for (var i = exprs.length; --i > stop;) {
11775 if (!exprs[i].is_constant_expression(scope)) return false;
11776 }
11777 return true;
11778 }
11779 if (parent instanceof AST_UnaryPrefix) return true;
11780 }
11781
11782 function is_tail_block(stat, parent) {
11783 if (parent instanceof AST_BlockStatement) return is_last_statement(parent.body, stat);
11784 if (parent instanceof AST_Catch) return is_last_statement(parent.body, stat);
11785 if (parent instanceof AST_Finally) return is_last_statement(parent.body, stat);
11786 if (parent instanceof AST_If) return parent.body === stat || parent.alternative === stat;
11787 if (parent instanceof AST_Try) return parent.bfinally ? parent.bfinally === stat : parent.bcatch === stat;
11788 }
11789
11790 function in_try(level, node) {
11791 var right = self.right;
11792 self.right = make_node(AST_Null, right);
11793 var may_throw = node.may_throw(compressor);
11794 self.right = right;
11795 for (var parent; parent = compressor.parent(level++); node = parent) {
11796 if (parent === scope) return false;
11797 if (parent instanceof AST_Try) {
11798 if (parent.bfinally && parent.bfinally !== node) return true;
11799 if (may_throw && parent.bcatch && parent.bcatch !== node) return true;
11800 }
11801 }
11802 }
11803
11804 function strip_assignment(def) {
11805 if (def) def.fixed = false;
11806 return (self.operator != "=" ? make_node(AST_Binary, self, {
11807 operator: self.operator.slice(0, -1),
11808 left: self.left,
11809 right: self.right,
11810 }) : maintain_this_binding(compressor, compressor.parent(), self, self.right)).optimize(compressor);
11811 }
11812 });
11813
11814 OPT(AST_Conditional, function(self, compressor) {
11815 if (compressor.option("sequences") && self.condition instanceof AST_Sequence) {
11816 var expressions = self.condition.expressions.slice();
11817 self.condition = expressions.pop();
11818 expressions.push(self);
11819 return make_sequence(self, expressions);
11820 }
11821 if (!compressor.option("conditionals")) return self;
11822 var condition = self.condition;
11823 if (compressor.option("booleans") && !condition.has_side_effects(compressor)) {
11824 mark_duplicate_condition(compressor, condition);
11825 }
11826 condition = fuzzy_eval(compressor, condition);
11827 if (!condition) {
11828 AST_Node.warn("Condition always false [{file}:{line},{col}]", self.start);
11829 return make_sequence(self, [ self.condition, self.alternative ]).optimize(compressor);
11830 } else if (!(condition instanceof AST_Node)) {
11831 AST_Node.warn("Condition always true [{file}:{line},{col}]", self.start);
11832 return make_sequence(self, [ self.condition, self.consequent ]).optimize(compressor);
11833 }
11834 var negated = condition.negate(compressor, first_in_statement(compressor));
11835 if (best_of(compressor, condition, negated) === negated) {
11836 self = make_node(AST_Conditional, self, {
11837 condition: negated,
11838 consequent: self.alternative,
11839 alternative: self.consequent
11840 });
11841 negated = condition;
11842 condition = self.condition;
11843 }
11844 var consequent = self.consequent;
11845 var alternative = self.alternative;
11846 if (repeatable(compressor, condition)) {
11847 // x ? x : y ---> x || y
11848 if (condition.equivalent_to(consequent)) return make_node(AST_Binary, self, {
11849 operator: "||",
11850 left: condition,
11851 right: alternative,
11852 }).optimize(compressor);
11853 // x ? y : x ---> x && y
11854 if (condition.equivalent_to(alternative)) return make_node(AST_Binary, self, {
11855 operator: "&&",
11856 left: condition,
11857 right: consequent,
11858 }).optimize(compressor);
11859 }
11860 // if (foo) exp = something; else exp = something_else;
11861 // |
11862 // v
11863 // exp = foo ? something : something_else;
11864 var seq_tail = consequent.tail_node();
11865 if (seq_tail instanceof AST_Assign) {
11866 var is_eq = seq_tail.operator == "=";
11867 var alt_tail = is_eq ? alternative.tail_node() : alternative;
11868 if ((is_eq || consequent === seq_tail)
11869 && alt_tail instanceof AST_Assign
11870 && seq_tail.operator == alt_tail.operator
11871 && seq_tail.left.equivalent_to(alt_tail.left)
11872 && (is_eq && seq_tail.left instanceof AST_SymbolRef
11873 || !condition.has_side_effects(compressor)
11874 && can_shift_lhs_of_tail(consequent)
11875 && can_shift_lhs_of_tail(alternative))) {
11876 return make_node(AST_Assign, self, {
11877 operator: seq_tail.operator,
11878 left: seq_tail.left,
11879 right: make_node(AST_Conditional, self, {
11880 condition: condition,
11881 consequent: pop_lhs(consequent),
11882 alternative: pop_lhs(alternative)
11883 })
11884 });
11885 }
11886 }
11887 // x ? y : y ---> x, y
11888 if (consequent.equivalent_to(alternative)) return make_sequence(self, [
11889 condition,
11890 consequent
11891 ]).optimize(compressor);
11892 // x ? y.p : z.p ---> (x ? y : z).p
11893 // x ? y(a) : z(a) ---> (x ? y : z)(a)
11894 // x ? y.f(a) : z.f(a) ---> (x ? y : z).f(a)
11895 var combined = combine_tail(consequent, alternative, true);
11896 if (combined) return combined;
11897 // x ? y(a) : y(b) ---> y(x ? a : b)
11898 var arg_index;
11899 if (consequent instanceof AST_Call
11900 && alternative.TYPE == consequent.TYPE
11901 && (arg_index = arg_diff(consequent, alternative)) >= 0
11902 && consequent.expression.equivalent_to(alternative.expression)
11903 && !condition.has_side_effects(compressor)
11904 && !consequent.expression.has_side_effects(compressor)) {
11905 var node = consequent.clone();
11906 var arg = consequent.args[arg_index];
11907 node.args[arg_index] = arg instanceof AST_Spread ? make_node(AST_Spread, self, {
11908 expression: make_node(AST_Conditional, self, {
11909 condition: condition,
11910 consequent: arg.expression,
11911 alternative: alternative.args[arg_index].expression,
11912 }),
11913 }) : make_node(AST_Conditional, self, {
11914 condition: condition,
11915 consequent: arg,
11916 alternative: alternative.args[arg_index],
11917 });
11918 return node;
11919 }
11920 // x ? (y ? a : b) : b ---> x && y ? a : b
11921 if (consequent instanceof AST_Conditional
11922 && consequent.alternative.equivalent_to(alternative)) {
11923 return make_node(AST_Conditional, self, {
11924 condition: make_node(AST_Binary, self, {
11925 left: condition,
11926 operator: "&&",
11927 right: consequent.condition
11928 }),
11929 consequent: consequent.consequent,
11930 alternative: alternative
11931 });
11932 }
11933 // x ? (y ? a : b) : a ---> !x || y ? a : b
11934 if (consequent instanceof AST_Conditional
11935 && consequent.consequent.equivalent_to(alternative)) {
11936 return make_node(AST_Conditional, self, {
11937 condition: make_node(AST_Binary, self, {
11938 left: negated,
11939 operator: "||",
11940 right: consequent.condition
11941 }),
11942 consequent: alternative,
11943 alternative: consequent.alternative
11944 });
11945 }
11946 // x ? a : (y ? a : b) ---> x || y ? a : b
11947 if (alternative instanceof AST_Conditional
11948 && consequent.equivalent_to(alternative.consequent)) {
11949 return make_node(AST_Conditional, self, {
11950 condition: make_node(AST_Binary, self, {
11951 left: condition,
11952 operator: "||",
11953 right: alternative.condition
11954 }),
11955 consequent: consequent,
11956 alternative: alternative.alternative
11957 });
11958 }
11959 // x ? b : (y ? a : b) ---> !x && y ? a : b
11960 if (alternative instanceof AST_Conditional
11961 && consequent.equivalent_to(alternative.alternative)) {
11962 return make_node(AST_Conditional, self, {
11963 condition: make_node(AST_Binary, self, {
11964 left: negated,
11965 operator: "&&",
11966 right: alternative.condition
11967 }),
11968 consequent: alternative.consequent,
11969 alternative: consequent
11970 });
11971 }
11972 // x ? (a, c) : (b, c) ---> x ? a : b, c
11973 if ((consequent instanceof AST_Sequence || alternative instanceof AST_Sequence)
11974 && consequent.tail_node().equivalent_to(alternative.tail_node())) {
11975 return make_sequence(self, [
11976 make_node(AST_Conditional, self, {
11977 condition: condition,
11978 consequent: pop_seq(consequent),
11979 alternative: pop_seq(alternative)
11980 }),
11981 consequent.tail_node()
11982 ]).optimize(compressor);
11983 }
11984 // x ? y && a : a ---> (!x || y) && a
11985 if (consequent instanceof AST_Binary
11986 && consequent.operator == "&&"
11987 && consequent.right.equivalent_to(alternative)) {
11988 return make_node(AST_Binary, self, {
11989 operator: "&&",
11990 left: make_node(AST_Binary, self, {
11991 operator: "||",
11992 left: negated,
11993 right: consequent.left
11994 }),
11995 right: alternative
11996 }).optimize(compressor);
11997 }
11998 // x ? y || a : a ---> x && y || a
11999 if (consequent instanceof AST_Binary
12000 && consequent.operator == "||"
12001 && consequent.right.equivalent_to(alternative)) {
12002 return make_node(AST_Binary, self, {
12003 operator: "||",
12004 left: make_node(AST_Binary, self, {
12005 operator: "&&",
12006 left: condition,
12007 right: consequent.left
12008 }),
12009 right: alternative
12010 }).optimize(compressor);
12011 }
12012 // x ? a : y && a ---> (x || y) && a
12013 if (alternative instanceof AST_Binary
12014 && alternative.operator == "&&"
12015 && alternative.right.equivalent_to(consequent)) {
12016 return make_node(AST_Binary, self, {
12017 operator: "&&",
12018 left: make_node(AST_Binary, self, {
12019 operator: "||",
12020 left: condition,
12021 right: alternative.left
12022 }),
12023 right: consequent
12024 }).optimize(compressor);
12025 }
12026 // x ? a : y || a ---> !x && y || a
12027 if (alternative instanceof AST_Binary
12028 && alternative.operator == "||"
12029 && alternative.right.equivalent_to(consequent)) {
12030 return make_node(AST_Binary, self, {
12031 operator: "||",
12032 left: make_node(AST_Binary, self, {
12033 operator: "&&",
12034 left: negated,
12035 right: alternative.left
12036 }),
12037 right: consequent
12038 }).optimize(compressor);
12039 }
12040 var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
12041 if (is_true(consequent)) {
12042 if (is_false(alternative)) {
12043 // c ? true : false ---> !!c
12044 return booleanize(condition);
12045 }
12046 // c ? true : x ---> !!c || x
12047 return make_node(AST_Binary, self, {
12048 operator: "||",
12049 left: booleanize(condition),
12050 right: alternative
12051 });
12052 }
12053 if (is_false(consequent)) {
12054 if (is_true(alternative)) {
12055 // c ? false : true ---> !c
12056 return booleanize(condition.negate(compressor));
12057 }
12058 // c ? false : x ---> !c && x
12059 return make_node(AST_Binary, self, {
12060 operator: "&&",
12061 left: booleanize(condition.negate(compressor)),
12062 right: alternative
12063 });
12064 }
12065 if (is_true(alternative)) {
12066 // c ? x : true ---> !c || x
12067 return make_node(AST_Binary, self, {
12068 operator: "||",
12069 left: booleanize(condition.negate(compressor)),
12070 right: consequent
12071 });
12072 }
12073 if (is_false(alternative)) {
12074 // c ? x : false ---> !!c && x
12075 return make_node(AST_Binary, self, {
12076 operator: "&&",
12077 left: booleanize(condition),
12078 right: consequent
12079 });
12080 }
12081 if (compressor.option("typeofs")) mark_locally_defined(condition, consequent, alternative);
12082 return self;
12083
12084 function booleanize(node) {
12085 if (node.is_boolean(compressor)) return node;
12086 // !!expression
12087 return make_node(AST_UnaryPrefix, node, {
12088 operator: "!",
12089 expression: node.negate(compressor)
12090 });
12091 }
12092
12093 // AST_True or !0
12094 function is_true(node) {
12095 return node instanceof AST_True
12096 || in_bool
12097 && node instanceof AST_Constant
12098 && node.value
12099 || (node instanceof AST_UnaryPrefix
12100 && node.operator == "!"
12101 && node.expression instanceof AST_Constant
12102 && !node.expression.value);
12103 }
12104 // AST_False or !1 or void 0
12105 function is_false(node) {
12106 return node instanceof AST_False
12107 || in_bool
12108 && (node instanceof AST_Constant
12109 && !node.value
12110 || node instanceof AST_UnaryPrefix
12111 && node.operator == "void"
12112 && !node.expression.has_side_effects(compressor))
12113 || (node instanceof AST_UnaryPrefix
12114 && node.operator == "!"
12115 && node.expression instanceof AST_Constant
12116 && node.expression.value);
12117 }
12118
12119 function arg_diff(consequent, alternative) {
12120 var a = consequent.args;
12121 var b = alternative.args;
12122 var len = a.length;
12123 if (len != b.length) return -2;
12124 for (var i = 0; i < len; i++) {
12125 if (!a[i].equivalent_to(b[i])) {
12126 if (a[i] instanceof AST_Spread !== b[i] instanceof AST_Spread) return -3;
12127 for (var j = i + 1; j < len; j++) {
12128 if (!a[j].equivalent_to(b[j])) return -2;
12129 }
12130 return i;
12131 }
12132 }
12133 return -1;
12134 }
12135
12136 function is_tail_equivalent(consequent, alternative) {
12137 if (consequent.TYPE != alternative.TYPE) return;
12138 if (consequent.optional != alternative.optional) return;
12139 if (consequent instanceof AST_Call) {
12140 if (arg_diff(consequent, alternative) != -1) return;
12141 return consequent.TYPE != "Call"
12142 || !(consequent.expression instanceof AST_PropAccess
12143 || alternative.expression instanceof AST_PropAccess)
12144 || is_tail_equivalent(consequent.expression, alternative.expression);
12145 }
12146 if (!(consequent instanceof AST_PropAccess)) return;
12147 var p = consequent.property;
12148 var q = alternative.property;
12149 return (p instanceof AST_Node ? p.equivalent_to(q) : p == q)
12150 && !(consequent.expression instanceof AST_Super || alternative.expression instanceof AST_Super);
12151 }
12152
12153 function combine_tail(consequent, alternative, top) {
12154 if (!is_tail_equivalent(consequent, alternative)) return !top && make_node(AST_Conditional, self, {
12155 condition: condition,
12156 consequent: consequent,
12157 alternative: alternative,
12158 });
12159 var node = consequent.clone();
12160 node.expression = combine_tail(consequent.expression, alternative.expression);
12161 return node;
12162 }
12163
12164 function can_shift_lhs_of_tail(node) {
12165 return node === node.tail_node() || all(node.expressions.slice(0, -1), function(expr) {
12166 return !expr.has_side_effects(compressor);
12167 });
12168 }
12169
12170 function pop_lhs(node) {
12171 if (!(node instanceof AST_Sequence)) return node.right;
12172 var exprs = node.expressions.slice();
12173 exprs.push(exprs.pop().right);
12174 return make_sequence(node, exprs);
12175 }
12176
12177 function pop_seq(node) {
12178 if (!(node instanceof AST_Sequence)) return make_node(AST_Number, node, {
12179 value: 0
12180 });
12181 return make_sequence(node, node.expressions.slice(0, -1));
12182 }
12183 });
12184
12185 OPT(AST_Boolean, function(self, compressor) {
12186 if (!compressor.option("booleans")) return self;
12187 if (compressor.in_boolean_context()) return make_node(AST_Number, self, {
12188 value: +self.value
12189 });
12190 var p = compressor.parent();
12191 if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) {
12192 AST_Node.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
12193 operator : p.operator,
12194 value : self.value,
12195 file : p.start.file,
12196 line : p.start.line,
12197 col : p.start.col,
12198 });
12199 return make_node(AST_Number, self, {
12200 value: +self.value
12201 });
12202 }
12203 return make_node(AST_UnaryPrefix, self, {
12204 operator: "!",
12205 expression: make_node(AST_Number, self, {
12206 value: 1 - self.value
12207 })
12208 });
12209 });
12210
12211 OPT(AST_Spread, function(self, compressor) {
12212 var exp = self.expression;
12213 if (compressor.option("spreads") && exp instanceof AST_Array && !(compressor.parent() instanceof AST_Object)) {
12214 return List.splice(exp.elements.map(function(node) {
12215 return node instanceof AST_Hole ? make_node(AST_Undefined, node).optimize(compressor) : node;
12216 }));
12217 }
12218 return self;
12219 });
12220
12221 function safe_to_flatten(value, compressor) {
12222 if (!value) return false;
12223 var parent = compressor.parent();
12224 if (parent.TYPE != "Call") return true;
12225 if (parent.expression !== compressor.self()) return true;
12226 if (value instanceof AST_SymbolRef) {
12227 value = value.fixed_value();
12228 if (!value) return false;
12229 }
12230 return value instanceof AST_Lambda && !value.contains_this();
12231 }
12232
12233 OPT(AST_Sub, function(self, compressor) {
12234 var expr = self.expression;
12235 var prop = self.property;
12236 var terminated = trim_optional_chain(self, compressor);
12237 if (terminated) return terminated;
12238 if (compressor.option("properties")) {
12239 var key = prop.evaluate(compressor);
12240 if (key !== prop) {
12241 if (typeof key == "string") {
12242 if (key == "undefined") {
12243 key = undefined;
12244 } else {
12245 var value = parseFloat(key);
12246 if (value.toString() == key) {
12247 key = value;
12248 }
12249 }
12250 }
12251 prop = self.property = best_of_expression(prop, make_node_from_constant(key, prop).transform(compressor));
12252 var property = "" + key;
12253 if (is_identifier_string(property)
12254 && property.length <= prop.print_to_string().length + 1) {
12255 return make_node(AST_Dot, self, {
12256 optional: self.optional,
12257 expression: expr,
12258 property: property,
12259 }).optimize(compressor);
12260 }
12261 }
12262 }
12263 var parent = compressor.parent();
12264 var assigned = is_lhs(compressor.self(), parent);
12265 var def, fn, fn_parent, index;
12266 if (compressor.option("arguments")
12267 && expr instanceof AST_SymbolRef
12268 && is_arguments(def = expr.definition())
12269 && !expr.in_arg
12270 && prop instanceof AST_Number
12271 && Math.floor(index = prop.value) == index
12272 && (fn = def.scope) === find_lambda()
12273 && fn.uses_arguments < (assigned ? 2 : 3)) {
12274 if (parent instanceof AST_UnaryPrefix && parent.operator == "delete") {
12275 if (!def.deleted) def.deleted = [];
12276 def.deleted[index] = true;
12277 }
12278 var argname = fn.argnames[index];
12279 if (def.deleted && def.deleted[index]) {
12280 argname = null;
12281 } else if (argname) {
12282 var arg_def;
12283 if (!(argname instanceof AST_SymbolFunarg)
12284 || argname.name == "await"
12285 || expr.scope.find_variable(argname.name) !== (arg_def = argname.definition())) {
12286 argname = null;
12287 } else if (compressor.has_directive("use strict")
12288 || fn.name
12289 || fn.rest
12290 || !(fn_parent instanceof AST_Call
12291 && index < fn_parent.args.length
12292 && all(fn_parent.args.slice(0, index + 1), function(arg) {
12293 return !(arg instanceof AST_Spread);
12294 }))
12295 || !all(fn.argnames, function(argname) {
12296 return argname instanceof AST_SymbolFunarg;
12297 })) {
12298 if (has_reassigned() || arg_def.assignments || arg_def.orig.length > 1) argname = null;
12299 }
12300 } else if ((assigned || !has_reassigned())
12301 && index < fn.argnames.length + 5
12302 && compressor.drop_fargs(fn, fn_parent)
12303 && !fn.rest) {
12304 while (index >= fn.argnames.length) {
12305 argname = fn.make_var(AST_SymbolFunarg, fn, "argument_" + fn.argnames.length);
12306 fn.argnames.push(argname);
12307 }
12308 }
12309 if (argname && find_if(function(node) {
12310 return node.name === argname.name;
12311 }, fn.argnames) === argname) {
12312 if (assigned) def.reassigned--;
12313 var sym = make_node(AST_SymbolRef, self, argname);
12314 sym.reference();
12315 argname.unused = undefined;
12316 return sym;
12317 }
12318 }
12319 if (assigned) return self;
12320 if (compressor.option("sequences")
12321 && parent.TYPE != "Call"
12322 && !(parent instanceof AST_ForEnumeration && parent.init === self)) {
12323 var seq = lift_sequence_in_expression(self, compressor);
12324 if (seq !== self) return seq.optimize(compressor);
12325 }
12326 if (key !== prop) {
12327 var sub = self.flatten_object(property, compressor);
12328 if (sub) {
12329 expr = self.expression = sub.expression;
12330 prop = self.property = sub.property;
12331 }
12332 }
12333 var elements;
12334 if (compressor.option("properties")
12335 && compressor.option("side_effects")
12336 && prop instanceof AST_Number
12337 && expr instanceof AST_Array
12338 && all(elements = expr.elements, function(value) {
12339 return !(value instanceof AST_Spread);
12340 })) {
12341 var index = prop.value;
12342 var retValue = elements[index];
12343 if (safe_to_flatten(retValue, compressor)) {
12344 var is_hole = retValue instanceof AST_Hole;
12345 var flatten = !is_hole;
12346 var values = [];
12347 for (var i = elements.length; --i > index;) {
12348 var value = elements[i].drop_side_effect_free(compressor);
12349 if (value) {
12350 values.unshift(value);
12351 if (flatten && value.has_side_effects(compressor)) flatten = false;
12352 }
12353 }
12354 if (!flatten) values.unshift(retValue);
12355 while (--i >= 0) {
12356 var value = elements[i].drop_side_effect_free(compressor);
12357 if (value) {
12358 values.unshift(value);
12359 } else if (is_hole) {
12360 values.unshift(make_node(AST_Hole, elements[i]));
12361 } else {
12362 index--;
12363 }
12364 }
12365 if (flatten) {
12366 values.push(retValue);
12367 return make_sequence(self, values).optimize(compressor);
12368 } else return make_node(AST_Sub, self, {
12369 expression: make_node(AST_Array, expr, { elements: values }),
12370 property: make_node(AST_Number, prop, { value: index }),
12371 });
12372 }
12373 }
12374 return try_evaluate(compressor, self);
12375
12376 function find_lambda() {
12377 var i = 0, p;
12378 while (p = compressor.parent(i++)) {
12379 if (p instanceof AST_Lambda) {
12380 if (p instanceof AST_Accessor) return;
12381 if (is_arrow(p)) continue;
12382 fn_parent = compressor.parent(i);
12383 return p;
12384 }
12385 }
12386 }
12387
12388 function has_reassigned() {
12389 return !compressor.option("reduce_vars") || def.reassigned;
12390 }
12391 });
12392
12393 AST_LambdaExpression.DEFMETHOD("contains_super", function() {
12394 var result = false;
12395 var self = this;
12396 self.walk(new TreeWalker(function(node) {
12397 if (result) return true;
12398 if (node instanceof AST_Super) return result = true;
12399 if (node !== self && node instanceof AST_Scope && !is_arrow(node)) return true;
12400 }));
12401 return result;
12402 });
12403
12404 // contains_this()
12405 // returns false only if context bound by the specified scope (or scope
12406 // containing the specified expression) is not referenced by `this`
12407 (function(def) {
12408 // scope of arrow function cannot bind to any context
12409 def(AST_Arrow, return_false);
12410 def(AST_AsyncArrow, return_false);
12411 def(AST_Node, function() {
12412 var result = false;
12413 var self = this;
12414 self.walk(new TreeWalker(function(node) {
12415 if (result) return true;
12416 if (node instanceof AST_This) return result = true;
12417 if (node !== self && node instanceof AST_Scope && !is_arrow(node)) return true;
12418 }));
12419 return result;
12420 });
12421 })(function(node, func) {
12422 node.DEFMETHOD("contains_this", func);
12423 });
12424
12425 function can_hoist_property(prop) {
12426 return prop instanceof AST_ObjectKeyVal
12427 && typeof prop.key == "string"
12428 && !(prop instanceof AST_ObjectMethod && prop.value.contains_super());
12429 }
12430
12431 AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) {
12432 if (!compressor.option("properties")) return;
12433 if (key === "__proto__") return;
12434 var expr = this.expression;
12435 if (expr instanceof AST_Object) {
12436 var props = expr.properties;
12437 for (var i = props.length; --i >= 0;) {
12438 var prop = props[i];
12439 if (prop.key !== key) continue;
12440 if (!all(props, can_hoist_property)) return;
12441 if (!safe_to_flatten(prop.value, compressor)) return;
12442 var scope, values = [];
12443 for (var j = 0; j < props.length; j++) {
12444 var value = props[j].value;
12445 if (props[j] instanceof AST_ObjectMethod) {
12446 var arrow = !(value.uses_arguments || is_generator(value) || value.contains_this());
12447 if (arrow) {
12448 if (!scope) scope = compressor.find_parent(AST_Scope);
12449 var avoid = avoid_await_yield(scope);
12450 value.each_argname(function(argname) {
12451 if (avoid[argname.name]) arrow = false;
12452 });
12453 }
12454 var ctor;
12455 if (arrow) {
12456 ctor = is_async(value) ? AST_AsyncArrow : AST_Arrow;
12457 } else if (i === j && !(compressor.parent() instanceof AST_Call)) {
12458 return;
12459 } else {
12460 ctor = value.CTOR;
12461 }
12462 value = make_node(ctor, value, value);
12463 }
12464 values.push(value);
12465 }
12466 return make_node(AST_Sub, this, {
12467 expression: make_node(AST_Array, expr, { elements: values }),
12468 property: make_node(AST_Number, this, { value: i }),
12469 });
12470 }
12471 }
12472 });
12473
12474 OPT(AST_Dot, function(self, compressor) {
12475 if (self.property == "arguments" || self.property == "caller") {
12476 AST_Node.warn("Function.prototype.{prop} not supported [{file}:{line},{col}]", {
12477 prop: self.property,
12478 file: self.start.file,
12479 line: self.start.line,
12480 col: self.start.col,
12481 });
12482 }
12483 var parent = compressor.parent();
12484 if (is_lhs(compressor.self(), parent)) return self;
12485 var terminated = trim_optional_chain(self, compressor);
12486 if (terminated) return terminated;
12487 if (compressor.option("sequences")
12488 && parent.TYPE != "Call"
12489 && !(parent instanceof AST_ForEnumeration && parent.init === self)) {
12490 var seq = lift_sequence_in_expression(self, compressor);
12491 if (seq !== self) return seq.optimize(compressor);
12492 }
12493 if (compressor.option("unsafe_proto")
12494 && self.expression instanceof AST_Dot
12495 && self.expression.property == "prototype") {
12496 var exp = self.expression.expression;
12497 if (is_undeclared_ref(exp)) switch (exp.name) {
12498 case "Array":
12499 self.expression = make_node(AST_Array, self.expression, {
12500 elements: []
12501 });
12502 break;
12503 case "Function":
12504 self.expression = make_node(AST_Function, self.expression, {
12505 argnames: [],
12506 body: []
12507 }).init_vars(exp.scope);
12508 break;
12509 case "Number":
12510 self.expression = make_node(AST_Number, self.expression, {
12511 value: 0
12512 });
12513 break;
12514 case "Object":
12515 self.expression = make_node(AST_Object, self.expression, {
12516 properties: []
12517 });
12518 break;
12519 case "RegExp":
12520 self.expression = make_node(AST_RegExp, self.expression, {
12521 value: /t/
12522 });
12523 break;
12524 case "String":
12525 self.expression = make_node(AST_String, self.expression, {
12526 value: ""
12527 });
12528 break;
12529 }
12530 }
12531 var sub = self.flatten_object(self.property, compressor);
12532 if (sub) return sub.optimize(compressor);
12533 return try_evaluate(compressor, self);
12534 });
12535
12536 OPT(AST_DestructuredArray, function(self, compressor) {
12537 if (compressor.option("rests") && self.rest instanceof AST_DestructuredArray) {
12538 return make_node(AST_DestructuredArray, self, {
12539 elements: self.elements.concat(self.rest.elements),
12540 rest: self.rest.rest,
12541 });
12542 }
12543 return self;
12544 });
12545
12546 OPT(AST_DestructuredKeyVal, function(self, compressor) {
12547 if (compressor.option("objects")) {
12548 var key = self.key;
12549 if (key instanceof AST_Node) {
12550 key = key.evaluate(compressor);
12551 if (key !== self.key) self.key = "" + key;
12552 }
12553 }
12554 return self;
12555 });
12556
12557 OPT(AST_Object, function(self, compressor) {
12558 if (!compressor.option("objects")) return self;
12559 var changed = false;
12560 var found = false;
12561 var generated = false;
12562 var keep_duplicate = compressor.has_directive("use strict");
12563 var keys = [];
12564 var map = new Dictionary();
12565 var values = [];
12566 self.properties.forEach(function(prop) {
12567 if (!(prop instanceof AST_Spread)) return process(prop);
12568 found = true;
12569 var exp = prop.expression;
12570 if (compressor.option("spreads") && exp instanceof AST_Object && all(exp.properties, function(prop) {
12571 if (prop instanceof AST_ObjectGetter) return false;
12572 if (prop instanceof AST_Spread) return false;
12573 if (prop.key !== "__proto__") return true;
12574 if (prop instanceof AST_ObjectSetter) return true;
12575 return !prop.value.has_side_effects(compressor);
12576 })) {
12577 changed = true;
12578 exp.properties.forEach(function(prop) {
12579 var key = prop.key;
12580 var setter = prop instanceof AST_ObjectSetter;
12581 if (key === "__proto__") {
12582 if (!setter) return;
12583 key = make_node_from_constant(key, prop);
12584 }
12585 process(setter ? make_node(AST_ObjectKeyVal, prop, {
12586 key: key,
12587 value: make_node(AST_Undefined, prop).optimize(compressor),
12588 }) : prop);
12589 });
12590 } else {
12591 generated = true;
12592 flush();
12593 values.push(prop);
12594 }
12595 });
12596 flush();
12597 if (!changed) return self;
12598 if (found && generated && values.length == 1) {
12599 var value = values[0];
12600 if (value instanceof AST_ObjectProperty && value.key instanceof AST_Number) {
12601 value.key = "" + value.key.value;
12602 }
12603 }
12604 return make_node(AST_Object, self, { properties: values });
12605
12606 function flush() {
12607 keys.forEach(function(key) {
12608 var props = map.get(key);
12609 switch (props.length) {
12610 case 0:
12611 return;
12612 case 1:
12613 return values.push(props[0]);
12614 }
12615 changed = true;
12616 var tail = keep_duplicate && !generated && props.pop();
12617 values.push(props.length == 1 ? props[0] : make_node(AST_ObjectKeyVal, self, {
12618 key: props[0].key,
12619 value: make_sequence(self, props.map(function(prop) {
12620 return prop.value;
12621 })),
12622 }));
12623 if (tail) values.push(tail);
12624 props.length = 0;
12625 });
12626 keys = [];
12627 map = new Dictionary();
12628 }
12629
12630 function process(prop) {
12631 var key = prop.key;
12632 if (key instanceof AST_Node) {
12633 found = true;
12634 key = key.evaluate(compressor);
12635 if (key === prop.key || key === "__proto__") {
12636 generated = true;
12637 } else {
12638 key = prop.key = "" + key;
12639 }
12640 }
12641 if (can_hoist_property(prop)) {
12642 if (prop.value.has_side_effects(compressor)) flush();
12643 keys.push(key);
12644 map.add(key, prop);
12645 } else {
12646 flush();
12647 values.push(prop);
12648 }
12649 if (found && !generated && typeof key == "string" && RE_POSITIVE_INTEGER.test(key)) {
12650 generated = true;
12651 if (map.has(key)) prop = map.get(key)[0];
12652 prop.key = make_node(AST_Number, prop, { value: +key });
12653 }
12654 }
12655 });
12656
12657 OPT(AST_Return, function(self, compressor) {
12658 if (compressor.option("side_effects")
12659 && self.value
12660 && is_undefined(self.value, compressor)
12661 && !in_async_generator(compressor.find_parent(AST_Scope))) {
12662 self.value = null;
12663 }
12664 return self;
12665 });
12666})(function(node, optimizer) {
12667 node.DEFMETHOD("optimize", function(compressor) {
12668 var self = this;
12669 if (self._optimized) return self;
12670 if (compressor.has_directive("use asm")) return self;
12671 var opt = optimizer(self, compressor);
12672 opt._optimized = true;
12673 return opt;
12674 });
12675});