UNPKG

556 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;
189merge(Compressor.prototype, {
190 option: function(key) { return this.options[key] },
191 exposed: function(def) {
192 if (def.exported) return true;
193 if (def.undeclared) return true;
194 if (!(def.global || def.scope.resolve() instanceof AST_Toplevel)) return false;
195 var toplevel = this.toplevel;
196 return !all(def.orig, function(sym) {
197 return toplevel[sym instanceof AST_SymbolDefun ? "funcs" : "vars"];
198 });
199 },
200 compress: function(node) {
201 node = node.resolve_defines(this);
202 node.hoist_exports(this);
203 if (this.option("expression")) {
204 node.process_expression(true);
205 }
206 var passes = +this.options.passes || 1;
207 var min_count = 1 / 0;
208 var stopping = false;
209 var mangle = { ie: this.option("ie") };
210 for (var pass = 0; pass < passes; pass++) {
211 node.figure_out_scope(mangle);
212 if (pass > 0 || this.option("reduce_vars"))
213 node.reset_opt_flags(this);
214 node = node.transform(this);
215 if (passes > 1) {
216 var count = 0;
217 node.walk(new TreeWalker(function() {
218 count++;
219 }));
220 AST_Node.info("pass {pass}: last_count: {min_count}, count: {count}", {
221 pass: pass,
222 min_count: min_count,
223 count: count,
224 });
225 if (count < min_count) {
226 min_count = count;
227 stopping = false;
228 } else if (stopping) {
229 break;
230 } else {
231 stopping = true;
232 }
233 }
234 }
235 if (this.option("expression")) {
236 node.process_expression(false);
237 }
238 return node;
239 },
240 before: function(node, descend, in_list) {
241 if (node._squeezed) return node;
242 var is_scope = node instanceof AST_Scope;
243 if (is_scope) {
244 node.hoist_properties(this);
245 node.hoist_declarations(this);
246 node.process_boolean_returns(this);
247 }
248 // Before https://github.com/mishoo/UglifyJS/pull/1602 AST_Node.optimize()
249 // would call AST_Node.transform() if a different instance of AST_Node is
250 // produced after OPT().
251 // This corrupts TreeWalker.stack, which cause AST look-ups to malfunction.
252 // Migrate and defer all children's AST_Node.transform() to below, which
253 // will now happen after this parent AST_Node has been properly substituted
254 // thus gives a consistent AST snapshot.
255 descend(node, this);
256 // Existing code relies on how AST_Node.optimize() worked, and omitting the
257 // following replacement call would result in degraded efficiency of both
258 // output and performance.
259 descend(node, this);
260 var opt = node.optimize(this);
261 if (is_scope && opt === node && !this.has_directive("use asm") && !opt.pinned()) {
262 opt.merge_variables(this);
263 opt.drop_unused(this);
264 descend(opt, this);
265 }
266 if (opt === node) opt._squeezed = true;
267 return opt;
268 }
269});
270
271(function(OPT) {
272 OPT(AST_Node, function(self, compressor) {
273 return self;
274 });
275
276 AST_Node.DEFMETHOD("equivalent_to", function(node) {
277 return this.TYPE == node.TYPE && this.print_to_string() == node.print_to_string();
278 });
279
280 AST_Toplevel.DEFMETHOD("hoist_exports", function(compressor) {
281 if (!compressor.option("hoist_exports")) return;
282 var body = this.body, props = [];
283 for (var i = 0; i < body.length; i++) {
284 var stat = body[i];
285 if (stat instanceof AST_ExportDeclaration) {
286 body[i] = stat = stat.body;
287 if (stat instanceof AST_Definitions) {
288 stat.definitions.forEach(function(defn) {
289 defn.name.match_symbol(export_symbol, true);
290 });
291 } else {
292 export_symbol(stat.name);
293 }
294 } else if (stat instanceof AST_ExportReferences) {
295 body.splice(i--, 1);
296 [].push.apply(props, stat.properties);
297 }
298 }
299 if (props.length) body.push(make_node(AST_ExportReferences, this, { properties: props }));
300
301 function export_symbol(sym) {
302 if (!(sym instanceof AST_SymbolDeclaration)) return;
303 var node = make_node(AST_SymbolExport, sym, sym);
304 node.alias = node.name;
305 props.push(node);
306 }
307 });
308
309 AST_Scope.DEFMETHOD("process_expression", function(insert, transform) {
310 var self = this;
311 var tt = new TreeTransformer(function(node) {
312 if (insert && node instanceof AST_SimpleStatement) {
313 return transform ? transform(node) : make_node(AST_Return, node, { value: node.body });
314 }
315 if (!insert && node instanceof AST_Return) {
316 return transform ? transform(node) : make_node(AST_SimpleStatement, node, {
317 body: node.value || make_node(AST_UnaryPrefix, node, {
318 operator: "void",
319 expression: make_node(AST_Number, node, { value: 0 }),
320 }),
321 });
322 }
323 if (node instanceof AST_Block) {
324 if (node instanceof AST_Lambda) {
325 if (node !== self) return node;
326 } else if (insert === "awaits" && node instanceof AST_Try) {
327 if (node.bfinally) return node;
328 }
329 for (var index = node.body.length; --index >= 0;) {
330 var stat = node.body[index];
331 if (!is_declaration(stat, true)) {
332 node.body[index] = stat.transform(tt);
333 break;
334 }
335 }
336 } else if (node instanceof AST_If) {
337 node.body = node.body.transform(tt);
338 if (node.alternative) {
339 node.alternative = node.alternative.transform(tt);
340 }
341 } else if (node instanceof AST_With) {
342 node.body = node.body.transform(tt);
343 }
344 return node;
345 });
346 self.transform(tt);
347 });
348
349 function read_property(obj, node) {
350 var key = node.get_property();
351 if (key instanceof AST_Node) return;
352 var value;
353 if (obj instanceof AST_Array) {
354 var elements = obj.elements;
355 if (key == "length") return make_node_from_constant(elements.length, obj);
356 if (typeof key == "number" && key in elements) value = elements[key];
357 } else if (obj instanceof AST_Lambda) {
358 if (key == "length") {
359 obj.length_read = true;
360 return make_node_from_constant(obj.argnames.length, obj);
361 }
362 } else if (obj instanceof AST_Object) {
363 key = "" + key;
364 var props = obj.properties;
365 for (var i = props.length; --i >= 0;) {
366 var prop = props[i];
367 if (!can_hoist_property(prop)) return;
368 if (!value && props[i].key === key) value = props[i].value;
369 }
370 }
371 return value instanceof AST_SymbolRef && value.fixed_value() || value;
372 }
373
374 function is_read_only_fn(value, name) {
375 if (value instanceof AST_Boolean) return native_fns.Boolean[name];
376 if (value instanceof AST_Number) return native_fns.Number[name];
377 if (value instanceof AST_String) return native_fns.String[name];
378 if (name == "valueOf") return false;
379 if (value instanceof AST_Array) return native_fns.Array[name];
380 if (value instanceof AST_Lambda) return native_fns.Function[name];
381 if (value instanceof AST_Object) return native_fns.Object[name];
382 if (value instanceof AST_RegExp) return native_fns.RegExp[name] && !value.value.global;
383 }
384
385 function is_modified(compressor, tw, node, value, level, immutable, recursive) {
386 var parent = tw.parent(level);
387 if (compressor.option("unsafe") && parent instanceof AST_Dot && is_read_only_fn(value, parent.property)) {
388 return;
389 }
390 var lhs = is_lhs(node, parent);
391 if (lhs) return lhs;
392 if (parent instanceof AST_Array) return is_modified(compressor, tw, parent, parent, level + 1);
393 if (parent instanceof AST_Binary) {
394 if (!lazy_op[parent.operator]) return;
395 return is_modified(compressor, tw, parent, parent, level + 1);
396 }
397 if (parent instanceof AST_Call) {
398 return !immutable
399 && parent.expression === node
400 && !parent.is_expr_pure(compressor)
401 && (!(value instanceof AST_LambdaExpression) || !(parent instanceof AST_New) && value.contains_this());
402 }
403 if (parent instanceof AST_Conditional) {
404 if (parent.condition === node) return;
405 return is_modified(compressor, tw, parent, parent, level + 1);
406 }
407 if (parent instanceof AST_ForEnumeration) return parent.init === node;
408 if (parent instanceof AST_ObjectKeyVal) {
409 if (parent.value !== node) return;
410 var obj = tw.parent(level + 1);
411 return is_modified(compressor, tw, obj, obj, level + 2);
412 }
413 if (parent instanceof AST_PropAccess) {
414 if (parent.expression !== node) return;
415 var prop = read_property(value, parent);
416 return (!immutable || recursive) && is_modified(compressor, tw, parent, prop, level + 1);
417 }
418 if (parent instanceof AST_Sequence) {
419 if (parent.tail_node() !== node) return;
420 return is_modified(compressor, tw, parent, value, level + 1, immutable, recursive);
421 }
422 }
423
424 function is_lambda(node) {
425 return node instanceof AST_Class || node instanceof AST_Lambda;
426 }
427
428 function safe_for_extends(node) {
429 return node instanceof AST_Class || node instanceof AST_Defun || node instanceof AST_Function;
430 }
431
432 function is_arguments(def) {
433 return def.name == "arguments" && def.scope.uses_arguments;
434 }
435
436 function is_funarg(def) {
437 return def.orig[0] instanceof AST_SymbolFunarg || def.orig[1] instanceof AST_SymbolFunarg;
438 }
439
440 function cross_scope(def, sym) {
441 do {
442 if (def === sym) return false;
443 if (sym instanceof AST_Scope) return true;
444 } while (sym = sym.parent_scope);
445 }
446
447 function can_drop_symbol(ref, compressor, keep_lambda) {
448 var def = ref.definition();
449 if (ref.in_arg && is_funarg(def)) return false;
450 return all(def.orig, function(sym) {
451 if (sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet) {
452 return compressor && can_varify(compressor, sym);
453 }
454 return !(keep_lambda && sym instanceof AST_SymbolLambda);
455 });
456 }
457
458 function has_escaped(d, scope, node, parent) {
459 if (parent instanceof AST_Assign) return parent.operator == "=" && parent.right === node;
460 if (parent instanceof AST_Call) return parent.expression !== node || parent instanceof AST_New;
461 if (parent instanceof AST_Exit) return parent.value === node && scope.resolve() !== d.scope.resolve();
462 if (parent instanceof AST_VarDef) return parent.value === node;
463 }
464
465 var RE_POSITIVE_INTEGER = /^(0|[1-9][0-9]*)$/;
466 (function(def) {
467 def(AST_Node, noop);
468
469 function reset_def(tw, compressor, def) {
470 def.assignments = 0;
471 def.bool_fn = 0;
472 def.cross_loop = false;
473 def.direct_access = false;
474 def.escaped = [];
475 def.fixed = !def.const_redefs
476 && !def.scope.pinned()
477 && !compressor.exposed(def)
478 && !(def.init instanceof AST_LambdaExpression && def.init !== def.scope)
479 && def.init;
480 def.reassigned = 0;
481 def.recursive_refs = 0;
482 def.references = [];
483 def.should_replace = undefined;
484 def.single_use = undefined;
485 }
486
487 function reset_block_variables(tw, compressor, scope) {
488 scope.variables.each(function(def) {
489 reset_def(tw, compressor, def);
490 });
491 }
492
493 function reset_variables(tw, compressor, scope) {
494 scope.fn_defs = [];
495 scope.variables.each(function(def) {
496 reset_def(tw, compressor, def);
497 var init = def.init;
498 if (init instanceof AST_LambdaDefinition) {
499 scope.fn_defs.push(init);
500 init.safe_ids = null;
501 }
502 if (def.fixed === null) {
503 def.safe_ids = tw.safe_ids;
504 mark(tw, def);
505 } else if (def.fixed) {
506 tw.loop_ids[def.id] = tw.in_loop;
507 mark(tw, def);
508 }
509 });
510 scope.may_call_this = function() {
511 scope.may_call_this = scope.contains_this() ? return_true : return_false;
512 };
513 if (scope.uses_arguments) scope.each_argname(function(node) {
514 node.definition().last_ref = false;
515 });
516 if (compressor.option("ie")) scope.variables.each(function(def) {
517 var d = def.orig[0].definition();
518 if (d !== def) d.fixed = false;
519 });
520 }
521
522 function walk_fn_def(tw, fn) {
523 var was_scanning = tw.fn_scanning;
524 tw.fn_scanning = fn;
525 fn.walk(tw);
526 tw.fn_scanning = was_scanning;
527 }
528
529 function revisit_fn_def(tw, fn) {
530 fn.enclosed.forEach(function(d) {
531 if (fn.variables.get(d.name) === d) return;
532 if (safe_to_read(tw, d)) return;
533 d.single_use = false;
534 var fixed = d.fixed;
535 if (typeof fixed == "function") fixed = fixed();
536 if (fixed instanceof AST_Lambda && HOP(fixed, "safe_ids")) return;
537 d.fixed = false;
538 });
539 }
540
541 function mark_fn_def(tw, def, fn) {
542 if (!HOP(fn, "safe_ids")) return;
543 var marker = fn.safe_ids;
544 if (marker === false) return;
545 if (fn.parent_scope.resolve().may_call_this === return_true) {
546 if (member(fn, tw.fn_visited)) revisit_fn_def(tw, fn);
547 } else if (marker) {
548 var visited = member(fn, tw.fn_visited);
549 if (marker === tw.safe_ids) {
550 if (!visited) walk_fn_def(tw, fn);
551 } else if (visited) {
552 revisit_fn_def(tw, fn);
553 } else {
554 fn.safe_ids = false;
555 }
556 } else if (tw.fn_scanning && tw.fn_scanning !== def.scope.resolve()) {
557 fn.safe_ids = false;
558 } else {
559 fn.safe_ids = tw.safe_ids;
560 walk_fn_def(tw, fn);
561 }
562 }
563
564 function pop_scope(tw, scope) {
565 var fn_defs = scope.fn_defs;
566 var tangled = scope.may_call_this === return_true ? fn_defs : fn_defs.filter(function(fn) {
567 if (fn.safe_ids === false) return true;
568 fn.safe_ids = tw.safe_ids;
569 walk_fn_def(tw, fn);
570 return false;
571 });
572 pop(tw);
573 tangled.forEach(function(fn) {
574 fn.safe_ids = tw.safe_ids;
575 walk_fn_def(tw, fn);
576 });
577 fn_defs.forEach(function(fn) {
578 delete fn.safe_ids;
579 });
580 delete scope.fn_defs;
581 delete scope.may_call_this;
582 }
583
584 function push(tw) {
585 tw.safe_ids = Object.create(tw.safe_ids);
586 }
587
588 function pop(tw) {
589 tw.safe_ids = Object.getPrototypeOf(tw.safe_ids);
590 }
591
592 function mark(tw, def) {
593 tw.safe_ids[def.id] = {};
594 }
595
596 function push_ref(def, ref) {
597 def.references.push(ref);
598 if (def.last_ref !== false) def.last_ref = ref;
599 }
600
601 function safe_to_read(tw, def) {
602 if (def.single_use == "m") return false;
603 var safe = tw.safe_ids[def.id];
604 if (safe) {
605 if (!HOP(tw.safe_ids, def.id)) safe.read = safe.read && safe.read !== tw.safe_ids ? true : tw.safe_ids;
606 if (def.fixed == null) {
607 if (is_arguments(def)) return false;
608 if (def.global && def.name == "arguments") return false;
609 tw.loop_ids[def.id] = null;
610 def.fixed = make_node(AST_Undefined, def.orig[0]);
611 return true;
612 }
613 return !safe.assign || safe.assign === tw.safe_ids;
614 }
615 return def.fixed instanceof AST_LambdaDefinition;
616 }
617
618 function safe_to_assign(tw, def, declare) {
619 if (!declare) {
620 if (is_funarg(def) && def.scope.uses_arguments && !tw.has_directive("use strict")) return false;
621 if (!all(def.orig, function(sym) {
622 return !(sym instanceof AST_SymbolConst);
623 })) return false;
624 }
625 if (def.fixed === undefined) return declare || all(def.orig, function(sym) {
626 return !(sym instanceof AST_SymbolLet);
627 });
628 if (def.fixed === false) return false;
629 var safe = tw.safe_ids[def.id];
630 if (def.safe_ids) {
631 def.safe_ids[def.id] = false;
632 delete def.safe_ids;
633 return def.fixed === null || HOP(tw.safe_ids, def.id) && !safe.read;
634 }
635 if (!HOP(tw.safe_ids, def.id)) {
636 if (!safe) return false;
637 if (safe.read) {
638 var scope = tw.find_parent(AST_BlockScope);
639 if (scope instanceof AST_Class) return false;
640 if (def.scope.resolve() !== scope.resolve()) return false;
641 }
642 safe.assign = safe.assign && safe.assign !== tw.safe_ids ? true : tw.safe_ids;
643 }
644 if (def.fixed != null && safe.read) {
645 if (safe.read !== tw.safe_ids) return false;
646 if (tw.loop_ids[def.id] !== tw.in_loop) return false;
647 }
648 return safe_to_read(tw, def) && all(def.orig, function(sym) {
649 return !(sym instanceof AST_SymbolLambda);
650 });
651 }
652
653 function make_ref(ref, fixed) {
654 var node = make_node(AST_SymbolRef, ref, ref);
655 node.fixed = fixed || make_node(AST_Undefined, ref);
656 return node;
657 }
658
659 function ref_once(compressor, def) {
660 return compressor.option("unused")
661 && !def.scope.pinned()
662 && def.single_use !== false
663 && def.references.length - def.recursive_refs == 1
664 && !(is_funarg(def) && def.scope.uses_arguments);
665 }
666
667 function is_immutable(value) {
668 if (!value) return false;
669 if (value instanceof AST_Assign) {
670 var op = value.operator;
671 return op == "=" ? is_immutable(value.right) : !lazy_op[op.slice(0, -1)];
672 }
673 if (value instanceof AST_Sequence) return is_immutable(value.tail_node());
674 return value.is_constant() || is_lambda(value) || value instanceof AST_ObjectIdentity;
675 }
676
677 function value_in_use(node, parent) {
678 if (parent instanceof AST_Array) return true;
679 if (parent instanceof AST_Binary) return lazy_op[parent.operator];
680 if (parent instanceof AST_Conditional) return parent.condition !== node;
681 if (parent instanceof AST_Sequence) return parent.tail_node() === node;
682 if (parent instanceof AST_Spread) return true;
683 }
684
685 function mark_escaped(tw, d, scope, node, value, level, depth) {
686 var parent = tw.parent(level);
687 if (value && value.is_constant()) return;
688 if (has_escaped(d, scope, node, parent)) {
689 d.escaped.push(parent);
690 if (depth > 1 && !(value && value.is_constant_expression(scope))) depth = 1;
691 if (!d.escaped.depth || d.escaped.depth > depth) d.escaped.depth = depth;
692 if (d.scope.resolve() !== scope.resolve()) d.escaped.cross_scope = true;
693 return;
694 } else if (value_in_use(node, parent)) {
695 mark_escaped(tw, d, scope, parent, parent, level + 1, depth);
696 } else if (parent instanceof AST_ObjectKeyVal && parent.value === node) {
697 var obj = tw.parent(level + 1);
698 mark_escaped(tw, d, scope, obj, obj, level + 2, depth);
699 } else if (parent instanceof AST_PropAccess && parent.expression === node) {
700 value = read_property(value, parent);
701 mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1);
702 if (value) return;
703 }
704 if (level > 0) return;
705 if (parent instanceof AST_Call && parent.expression === node) return;
706 if (parent instanceof AST_Sequence && parent.tail_node() !== node) return;
707 if (parent instanceof AST_SimpleStatement) return;
708 if (parent instanceof AST_Unary && !unary_side_effects[parent.operator]) return;
709 d.direct_access = true;
710 }
711
712 function mark_assignment_to_arguments(node) {
713 if (!(node instanceof AST_Sub)) return;
714 var expr = node.expression;
715 if (!(expr instanceof AST_SymbolRef)) return;
716 var def = expr.definition();
717 if (!is_arguments(def)) return;
718 var key = node.property;
719 if (key.is_constant()) key = key.value;
720 if (!(key instanceof AST_Node) && !RE_POSITIVE_INTEGER.test(key)) return;
721 def.reassigned++;
722 (key instanceof AST_Node ? def.scope.argnames : [ def.scope.argnames[key] ]).forEach(function(argname) {
723 if (argname instanceof AST_SymbolFunarg) argname.definition().fixed = false;
724 });
725 }
726
727 function scan_declaration(tw, compressor, lhs, fixed, visit) {
728 var scanner = new TreeWalker(function(node) {
729 if (node instanceof AST_DefaultValue) {
730 reset_flags(node);
731 push(tw);
732 node.value.walk(tw);
733 pop(tw);
734 var save = fixed;
735 if (save) fixed = function() {
736 var value = save();
737 var ev;
738 if (is_undefined(value, compressor)
739 || (ev = fuzzy_eval(compressor, value, true)) === undefined) {
740 return make_sequence(node, [ value, node.value ]);
741 }
742 return ev instanceof AST_Node ? node : value;
743 };
744 node.name.walk(scanner);
745 fixed = save;
746 return true;
747 }
748 if (node instanceof AST_DestructuredArray) {
749 reset_flags(node);
750 var save = fixed;
751 node.elements.forEach(function(node, index) {
752 if (node instanceof AST_Hole) return reset_flags(node);
753 if (save) fixed = function() {
754 return make_node(AST_Sub, node, {
755 expression: save(),
756 property: make_node(AST_Number, node, { value: index }),
757 });
758 };
759 node.walk(scanner);
760 });
761 if (node.rest) {
762 if (save) fixed = compressor.option("rests") && function() {
763 var value = save();
764 return value instanceof AST_Array ? make_node(AST_Array, node, {
765 elements: value.elements.slice(node.elements.length),
766 }) : node;
767 };
768 node.rest.walk(scanner);
769 }
770 fixed = save;
771 return true;
772 }
773 if (node instanceof AST_DestructuredObject) {
774 reset_flags(node);
775 var save = fixed;
776 node.properties.forEach(function(node) {
777 reset_flags(node);
778 if (node.key instanceof AST_Node) {
779 push(tw);
780 node.key.walk(tw);
781 pop(tw);
782 }
783 if (save) fixed = function() {
784 var key = node.key;
785 var type = AST_Sub;
786 if (typeof key == "string") {
787 if (is_identifier_string(key)) {
788 type = AST_Dot;
789 } else {
790 key = make_node_from_constant(key, node);
791 }
792 }
793 return make_node(type, node, {
794 expression: save(),
795 property: key
796 });
797 };
798 node.value.walk(scanner);
799 });
800 if (node.rest) {
801 fixed = false;
802 node.rest.walk(scanner);
803 }
804 fixed = save;
805 return true;
806 }
807 visit(node, fixed, function() {
808 var save_len = tw.stack.length;
809 for (var i = 0, len = scanner.stack.length - 1; i < len; i++) {
810 tw.stack.push(scanner.stack[i]);
811 }
812 node.walk(tw);
813 tw.stack.length = save_len;
814 });
815 return true;
816 });
817 lhs.walk(scanner);
818 }
819
820 function reduce_iife(tw, descend, compressor) {
821 var fn = this;
822 fn.inlined = false;
823 var iife = tw.parent();
824 var hit = is_async(fn) || is_generator(fn);
825 var aborts = false;
826 fn.walk(new TreeWalker(function(node) {
827 if (hit) return aborts = true;
828 if (node instanceof AST_Return) return hit = true;
829 if (node instanceof AST_Scope && node !== fn) return true;
830 }));
831 if (aborts) push(tw);
832 reset_variables(tw, compressor, fn);
833 // Virtually turn IIFE parameters into variable definitions:
834 // (function(a,b) {...})(c,d) ---> (function() {var a=c,b=d; ...})()
835 // So existing transformation rules can work on them.
836 var safe = !fn.uses_arguments || tw.has_directive("use strict");
837 fn.argnames.forEach(function(argname, i) {
838 var value = iife.args[i];
839 scan_declaration(tw, compressor, argname, function() {
840 var j = fn.argnames.indexOf(argname);
841 var arg = j < 0 ? value : iife.args[j];
842 if (arg instanceof AST_Sequence && arg.expressions.length < 2) arg = arg.expressions[0];
843 return arg || make_node(AST_Undefined, iife);
844 }, visit);
845 });
846 var rest = fn.rest;
847 if (rest) scan_declaration(tw, compressor, rest, compressor.option("rests") && function() {
848 return fn.rest === rest ? make_node(AST_Array, fn, {
849 elements: iife.args.slice(fn.argnames.length),
850 }) : rest;
851 }, visit);
852 walk_lambda(fn, tw);
853 var safe_ids = tw.safe_ids;
854 pop_scope(tw, fn);
855 if (!aborts) tw.safe_ids = safe_ids;
856 return true;
857
858 function visit(node, fixed) {
859 var d = node.definition();
860 if (fixed && safe && d.fixed === undefined) {
861 mark(tw, d);
862 tw.loop_ids[d.id] = tw.in_loop;
863 d.fixed = fixed;
864 d.fixed.assigns = [ node ];
865 } else {
866 d.fixed = false;
867 }
868 }
869 }
870
871 def(AST_Assign, function(tw, descend, compressor) {
872 var node = this;
873 var left = node.left;
874 var right = node.right;
875 var ld = left instanceof AST_SymbolRef && left.definition();
876 var scan = ld || left instanceof AST_Destructured;
877 switch (node.operator) {
878 case "=":
879 if (left.equivalent_to(right) && !left.has_side_effects(compressor)) {
880 right.walk(tw);
881 walk_prop(left);
882 node.__drop = true;
883 return true;
884 }
885 if (ld && right instanceof AST_LambdaExpression) {
886 walk_assign();
887 right.parent_scope.resolve().fn_defs.push(right);
888 right.safe_ids = null;
889 if (!ld.fixed || !node.write_only) mark_fn_def(tw, ld, right);
890 return true;
891 }
892 if (scan) {
893 right.walk(tw);
894 walk_assign();
895 return true;
896 }
897 mark_assignment_to_arguments(left);
898 return;
899 case "&&=":
900 case "||=":
901 case "??=":
902 left.walk(tw);
903 push(tw);
904 if (scan) {
905 right.walk(tw);
906 walk_assign();
907 } else {
908 mark_assignment_to_arguments(left);
909 right.walk(tw);
910 }
911 pop(tw);
912 return true;
913 default:
914 if (!scan) {
915 mark_assignment_to_arguments(left);
916 return;
917 }
918 ld.assignments++;
919 var fixed = ld.fixed;
920 if (is_modified(compressor, tw, node, node, 0)) {
921 ld.fixed = false;
922 return;
923 }
924 var safe = safe_to_read(tw, ld);
925 right.walk(tw);
926 if (safe && !left.in_arg && safe_to_assign(tw, ld)) {
927 push_ref(ld, left);
928 mark(tw, ld);
929 if (ld.single_use) ld.single_use = false;
930 left.fixed = ld.fixed = function() {
931 return make_node(AST_Binary, node, {
932 operator: node.operator.slice(0, -1),
933 left: make_ref(left, fixed),
934 right: node.right,
935 });
936 };
937 left.fixed.assigns = !fixed || !fixed.assigns ? [] : fixed.assigns.slice();
938 left.fixed.assigns.push(node);
939 } else {
940 left.walk(tw);
941 ld.fixed = false;
942 }
943 return true;
944 }
945
946 function walk_prop(lhs) {
947 if (lhs instanceof AST_Dot) {
948 walk_prop(lhs.expression);
949 } else if (lhs instanceof AST_Sub) {
950 walk_prop(lhs.expression);
951 lhs.property.walk(tw);
952 } else if (lhs instanceof AST_SymbolRef) {
953 var d = lhs.definition();
954 push_ref(d, lhs);
955 if (d.fixed) {
956 lhs.fixed = d.fixed;
957 if (lhs.fixed.assigns) {
958 lhs.fixed.assigns.push(node);
959 } else {
960 lhs.fixed.assigns = [ node ];
961 }
962 }
963 } else {
964 lhs.walk(tw);
965 }
966 }
967
968 function walk_assign() {
969 var recursive = ld && recursive_ref(tw, ld);
970 var modified = is_modified(compressor, tw, node, right, 0, is_immutable(right), recursive);
971 scan_declaration(tw, compressor, left, function() {
972 return node.right;
973 }, function(sym, fixed, walk) {
974 if (!(sym instanceof AST_SymbolRef)) {
975 mark_assignment_to_arguments(sym);
976 walk();
977 return;
978 }
979 var d = sym.definition();
980 d.assignments++;
981 if (fixed && !modified && !sym.in_arg && safe_to_assign(tw, d)) {
982 push_ref(d, sym);
983 mark(tw, d);
984 if (left instanceof AST_Destructured
985 || d.orig.length == 1 && d.orig[0] instanceof AST_SymbolDefun) {
986 d.single_use = false;
987 }
988 tw.loop_ids[d.id] = tw.in_loop;
989 mark_escaped(tw, d, sym.scope, node, right, 0, 1);
990 sym.fixed = d.fixed = fixed;
991 sym.fixed.assigns = [ node ];
992 } else {
993 walk();
994 d.fixed = false;
995 }
996 });
997 }
998 });
999 def(AST_Binary, function(tw) {
1000 if (!lazy_op[this.operator]) return;
1001 this.left.walk(tw);
1002 push(tw);
1003 this.right.walk(tw);
1004 pop(tw);
1005 return true;
1006 });
1007 def(AST_BlockScope, function(tw, descend, compressor) {
1008 reset_block_variables(tw, compressor, this);
1009 });
1010 def(AST_Call, function(tw, descend) {
1011 var node = this;
1012 var exp = node.expression;
1013 if (exp instanceof AST_LambdaExpression) {
1014 var iife = is_iife_single(node);
1015 node.args.forEach(function(arg) {
1016 arg.walk(tw);
1017 if (arg instanceof AST_Spread) iife = false;
1018 });
1019 if (iife) exp.reduce_vars = reduce_iife;
1020 exp.walk(tw);
1021 if (iife) delete exp.reduce_vars;
1022 return true;
1023 }
1024 if (node.TYPE == "Call" && tw.in_boolean_context()) {
1025 if (exp instanceof AST_SymbolRef) {
1026 exp.definition().bool_fn++;
1027 } else if (exp instanceof AST_Assign && exp.operator == "=" && exp.left instanceof AST_SymbolRef) {
1028 exp.left.definition().bool_fn++;
1029 }
1030 }
1031 exp.walk(tw);
1032 var optional = node.optional;
1033 if (optional) push(tw);
1034 node.args.forEach(function(arg) {
1035 arg.walk(tw);
1036 });
1037 if (optional) pop(tw);
1038 var fixed = exp instanceof AST_SymbolRef && exp.fixed_value();
1039 if (fixed instanceof AST_Lambda) {
1040 mark_fn_def(tw, exp.definition(), fixed);
1041 } else {
1042 tw.find_parent(AST_Scope).may_call_this();
1043 }
1044 return true;
1045 });
1046 def(AST_Class, function(tw, descend, compressor) {
1047 var node = this;
1048 reset_block_variables(tw, compressor, node);
1049 if (node.extends) node.extends.walk(tw);
1050 var props = node.properties.filter(function(prop) {
1051 reset_flags(prop);
1052 if (prop.key instanceof AST_Node) prop.key.walk(tw);
1053 return prop.value;
1054 });
1055 if (node.name) {
1056 var d = node.name.definition();
1057 var parent = tw.parent();
1058 if (parent instanceof AST_ExportDeclaration || parent instanceof AST_ExportDefault) d.single_use = false;
1059 if (safe_to_assign(tw, d, true)) {
1060 mark(tw, d);
1061 tw.loop_ids[d.id] = tw.in_loop;
1062 d.fixed = function() {
1063 return node;
1064 };
1065 d.fixed.assigns = [ node ];
1066 if (!is_safe_lexical(d)) d.single_use = false;
1067 } else {
1068 d.fixed = false;
1069 }
1070 }
1071 props.forEach(function(prop) {
1072 if (!prop.static || prop instanceof AST_ClassField && prop.value.contains_this()) {
1073 push(tw);
1074 prop.value.walk(tw);
1075 pop(tw);
1076 } else {
1077 prop.value.walk(tw);
1078 }
1079 });
1080 return true;
1081 });
1082 def(AST_Conditional, function(tw) {
1083 this.condition.walk(tw);
1084 push(tw);
1085 this.consequent.walk(tw);
1086 pop(tw);
1087 push(tw);
1088 this.alternative.walk(tw);
1089 pop(tw);
1090 return true;
1091 });
1092 def(AST_DefaultValue, function(tw) {
1093 this.name.walk(tw);
1094 push(tw);
1095 this.value.walk(tw);
1096 pop(tw);
1097 return true;
1098 });
1099 def(AST_Do, function(tw) {
1100 var save_loop = tw.in_loop;
1101 tw.in_loop = this;
1102 push(tw);
1103 this.body.walk(tw);
1104 if (has_loop_control(this, tw.parent())) {
1105 pop(tw);
1106 push(tw);
1107 }
1108 this.condition.walk(tw);
1109 pop(tw);
1110 tw.in_loop = save_loop;
1111 return true;
1112 });
1113 def(AST_For, function(tw, descend, compressor) {
1114 var node = this;
1115 reset_block_variables(tw, compressor, node);
1116 if (node.init) node.init.walk(tw);
1117 var save_loop = tw.in_loop;
1118 tw.in_loop = node;
1119 push(tw);
1120 if (node.condition) node.condition.walk(tw);
1121 node.body.walk(tw);
1122 if (node.step) {
1123 if (has_loop_control(node, tw.parent())) {
1124 pop(tw);
1125 push(tw);
1126 }
1127 node.step.walk(tw);
1128 }
1129 pop(tw);
1130 tw.in_loop = save_loop;
1131 return true;
1132 });
1133 def(AST_ForEnumeration, function(tw, descend, compressor) {
1134 var node = this;
1135 reset_block_variables(tw, compressor, node);
1136 node.object.walk(tw);
1137 var save_loop = tw.in_loop;
1138 tw.in_loop = node;
1139 push(tw);
1140 var init = node.init;
1141 if (init instanceof AST_Definitions) {
1142 init.definitions[0].name.mark_symbol(function(node) {
1143 if (node instanceof AST_SymbolDeclaration) {
1144 var def = node.definition();
1145 def.assignments++;
1146 def.fixed = false;
1147 }
1148 }, tw);
1149 } else if (init instanceof AST_Destructured || init instanceof AST_SymbolRef) {
1150 init.mark_symbol(function(node) {
1151 if (node instanceof AST_SymbolRef) {
1152 var def = node.definition();
1153 push_ref(def, node);
1154 def.assignments++;
1155 if (!node.is_immutable()) def.fixed = false;
1156 }
1157 }, tw);
1158 } else {
1159 init.walk(tw);
1160 }
1161 node.body.walk(tw);
1162 pop(tw);
1163 tw.in_loop = save_loop;
1164 return true;
1165 });
1166 def(AST_If, function(tw) {
1167 this.condition.walk(tw);
1168 push(tw);
1169 this.body.walk(tw);
1170 pop(tw);
1171 if (this.alternative) {
1172 push(tw);
1173 this.alternative.walk(tw);
1174 pop(tw);
1175 }
1176 return true;
1177 });
1178 def(AST_LabeledStatement, function(tw) {
1179 push(tw);
1180 this.body.walk(tw);
1181 pop(tw);
1182 return true;
1183 });
1184 def(AST_Lambda, function(tw, descend, compressor) {
1185 var fn = this;
1186 if (HOP(fn, "safe_ids") && fn.safe_ids !== tw.safe_ids) return true;
1187 if (!push_uniq(tw.fn_visited, fn)) return true;
1188 fn.inlined = false;
1189 push(tw);
1190 reset_variables(tw, compressor, fn);
1191 descend();
1192 pop_scope(tw, fn);
1193 if (fn.name) mark_escaped(tw, fn.name.definition(), fn, fn.name, fn, 0, 1);
1194 return true;
1195 });
1196 def(AST_LambdaDefinition, function(tw, descend, compressor) {
1197 var fn = this;
1198 var def = fn.name.definition();
1199 var parent = tw.parent();
1200 if (parent instanceof AST_ExportDeclaration || parent instanceof AST_ExportDefault) def.single_use = false;
1201 if (HOP(fn, "safe_ids") && fn.safe_ids !== tw.safe_ids) return true;
1202 if (!push_uniq(tw.fn_visited, fn)) return true;
1203 fn.inlined = false;
1204 push(tw);
1205 reset_variables(tw, compressor, fn);
1206 descend();
1207 pop_scope(tw, fn);
1208 return true;
1209 });
1210 def(AST_Sub, function(tw) {
1211 if (!this.optional) return;
1212 this.expression.walk(tw);
1213 push(tw);
1214 this.property.walk(tw);
1215 pop(tw);
1216 return true;
1217 });
1218 def(AST_Switch, function(tw, descend, compressor) {
1219 var node = this;
1220 reset_block_variables(tw, compressor, node);
1221 node.expression.walk(tw);
1222 var first = true;
1223 node.body.forEach(function(branch) {
1224 if (branch instanceof AST_Default) return;
1225 branch.expression.walk(tw);
1226 if (first) {
1227 first = false;
1228 push(tw);
1229 }
1230 })
1231 if (!first) pop(tw);
1232 walk_body(node, tw);
1233 return true;
1234 });
1235 def(AST_SwitchBranch, function(tw) {
1236 push(tw);
1237 walk_body(this, tw);
1238 pop(tw);
1239 return true;
1240 });
1241 def(AST_SymbolCatch, function() {
1242 this.definition().fixed = false;
1243 });
1244 def(AST_SymbolImport, function() {
1245 this.definition().fixed = false;
1246 });
1247 def(AST_SymbolRef, function(tw, descend, compressor) {
1248 var d = this.definition();
1249 push_ref(d, this);
1250 if (d.references.length == 1 && !d.fixed && d.orig[0] instanceof AST_SymbolDefun) {
1251 tw.loop_ids[d.id] = tw.in_loop;
1252 }
1253 var recursive = recursive_ref(tw, d);
1254 if (recursive) recursive.enclosed.forEach(function(def) {
1255 if (d === def) return;
1256 if (def.scope.resolve() === recursive) return;
1257 var assigns = def.fixed && def.fixed.assigns;
1258 if (!assigns) return;
1259 if (assigns[assigns.length - 1] instanceof AST_VarDef) return;
1260 var safe = tw.safe_ids[def.id];
1261 if (!safe) return;
1262 safe.assign = true;
1263 });
1264 if (d.fixed === false) {
1265 var redef = d.redefined();
1266 if (redef && cross_scope(d.scope, this.scope)) redef.single_use = false;
1267 } else if (d.fixed === undefined || !safe_to_read(tw, d)) {
1268 d.fixed = false;
1269 } else if (d.fixed) {
1270 if (this.in_arg && d.orig[0] instanceof AST_SymbolLambda) this.fixed = d.scope;
1271 var value = this.fixed_value();
1272 if (recursive) {
1273 d.recursive_refs++;
1274 } else if (value && ref_once(compressor, d)) {
1275 d.in_loop = tw.loop_ids[d.id] !== tw.in_loop;
1276 d.single_use = is_lambda(value)
1277 && !value.pinned()
1278 && (!d.in_loop || tw.parent() instanceof AST_Call)
1279 || !d.in_loop
1280 && d.scope === this.scope.resolve()
1281 && value.is_constant_expression();
1282 } else {
1283 d.single_use = false;
1284 }
1285 if (is_modified(compressor, tw, this, value, 0, is_immutable(value), recursive)) {
1286 if (d.single_use) {
1287 d.single_use = "m";
1288 } else {
1289 d.fixed = false;
1290 }
1291 }
1292 if (d.fixed && tw.loop_ids[d.id] !== tw.in_loop) d.cross_loop = true;
1293 mark_escaped(tw, d, this.scope, this, value, 0, 1);
1294 }
1295 if (!this.fixed) this.fixed = d.fixed;
1296 var parent;
1297 if (value instanceof AST_Lambda
1298 && !((parent = tw.parent()) instanceof AST_Call && parent.expression === this)) {
1299 mark_fn_def(tw, d, value);
1300 }
1301 });
1302 def(AST_Template, function(tw, descend) {
1303 var node = this;
1304 var tag = node.tag;
1305 if (!tag) return;
1306 if (tag instanceof AST_LambdaExpression) {
1307 node.expressions.forEach(function(exp) {
1308 exp.walk(tw);
1309 });
1310 tag.walk(tw);
1311 return true;
1312 }
1313 tag.walk(tw);
1314 node.expressions.forEach(function(exp) {
1315 exp.walk(tw);
1316 });
1317 var fixed = tag instanceof AST_SymbolRef && tag.fixed_value();
1318 if (fixed instanceof AST_Lambda) {
1319 mark_fn_def(tw, tag.definition(), fixed);
1320 } else {
1321 tw.find_parent(AST_Scope).may_call_this();
1322 }
1323 return true;
1324 });
1325 def(AST_Toplevel, function(tw, descend, compressor) {
1326 var node = this;
1327 node.globals.each(function(def) {
1328 reset_def(tw, compressor, def);
1329 });
1330 push(tw);
1331 reset_variables(tw, compressor, node);
1332 descend();
1333 pop_scope(tw, node);
1334 return true;
1335 });
1336 def(AST_Try, function(tw, descend, compressor) {
1337 var node = this;
1338 reset_block_variables(tw, compressor, node);
1339 push(tw);
1340 walk_body(node, tw);
1341 pop(tw);
1342 if (node.bcatch) {
1343 push(tw);
1344 node.bcatch.walk(tw);
1345 pop(tw);
1346 }
1347 if (node.bfinally) node.bfinally.walk(tw);
1348 return true;
1349 });
1350 def(AST_Unary, function(tw, descend) {
1351 var node = this;
1352 if (!UNARY_POSTFIX[node.operator]) return;
1353 var exp = node.expression;
1354 if (!(exp instanceof AST_SymbolRef)) {
1355 mark_assignment_to_arguments(exp);
1356 return;
1357 }
1358 var d = exp.definition();
1359 d.assignments++;
1360 var fixed = d.fixed;
1361 if (safe_to_read(tw, d) && !exp.in_arg && safe_to_assign(tw, d)) {
1362 push_ref(d, exp);
1363 mark(tw, d);
1364 if (d.single_use) d.single_use = false;
1365 d.fixed = function() {
1366 return make_node(AST_Binary, node, {
1367 operator: node.operator.slice(0, -1),
1368 left: make_node(AST_UnaryPrefix, node, {
1369 operator: "+",
1370 expression: make_ref(exp, fixed)
1371 }),
1372 right: make_node(AST_Number, node, {
1373 value: 1
1374 })
1375 });
1376 };
1377 d.fixed.assigns = fixed && fixed.assigns ? fixed.assigns.slice() : [];
1378 d.fixed.assigns.push(node);
1379 if (node instanceof AST_UnaryPrefix) {
1380 exp.fixed = d.fixed;
1381 } else {
1382 exp.fixed = function() {
1383 return make_node(AST_UnaryPrefix, node, {
1384 operator: "+",
1385 expression: make_ref(exp, fixed)
1386 });
1387 };
1388 exp.fixed.assigns = fixed && fixed.assigns;
1389 }
1390 } else {
1391 exp.walk(tw);
1392 d.fixed = false;
1393 }
1394 return true;
1395 });
1396 def(AST_VarDef, function(tw, descend, compressor) {
1397 var node = this;
1398 var value = node.value;
1399 if (value instanceof AST_LambdaExpression && node.name instanceof AST_SymbolDeclaration) {
1400 walk_defn();
1401 value.parent_scope.resolve().fn_defs.push(value);
1402 value.safe_ids = null;
1403 var ld = node.name.definition();
1404 if (!ld.fixed) mark_fn_def(tw, ld, value);
1405 } else if (value) {
1406 value.walk(tw);
1407 walk_defn();
1408 } else if (tw.parent() instanceof AST_Let) {
1409 walk_defn();
1410 }
1411 return true;
1412
1413 function walk_defn() {
1414 scan_declaration(tw, compressor, node.name, function() {
1415 return node.value || make_node(AST_Undefined, node);
1416 }, function(name, fixed) {
1417 var d = name.definition();
1418 if (fixed && safe_to_assign(tw, d, true)) {
1419 mark(tw, d);
1420 tw.loop_ids[d.id] = tw.in_loop;
1421 d.fixed = fixed;
1422 d.fixed.assigns = [ node ];
1423 if (name instanceof AST_SymbolConst && d.redefined()
1424 || !(can_drop_symbol(name) || is_safe_lexical(d))) {
1425 d.single_use = false;
1426 }
1427 } else {
1428 d.fixed = false;
1429 }
1430 });
1431 }
1432 });
1433 def(AST_While, function(tw, descend) {
1434 var save_loop = tw.in_loop;
1435 tw.in_loop = this;
1436 push(tw);
1437 descend();
1438 pop(tw);
1439 tw.in_loop = save_loop;
1440 return true;
1441 });
1442 })(function(node, func) {
1443 node.DEFMETHOD("reduce_vars", func);
1444 });
1445
1446 function reset_flags(node) {
1447 node._squeezed = false;
1448 node._optimized = false;
1449 delete node.fixed;
1450 if (node instanceof AST_Scope) delete node._var_names;
1451 }
1452
1453 AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) {
1454 var tw = new TreeWalker(compressor.option("reduce_vars") ? function(node, descend) {
1455 reset_flags(node);
1456 return node.reduce_vars(tw, descend, compressor);
1457 } : reset_flags);
1458 // Flow control for visiting lambda definitions
1459 tw.fn_scanning = null;
1460 tw.fn_visited = [];
1461 // Record the loop body in which `AST_SymbolDeclaration` is first encountered
1462 tw.in_loop = null;
1463 tw.loop_ids = Object.create(null);
1464 // Stack of look-up tables to keep track of whether a `SymbolDef` has been
1465 // properly assigned before use:
1466 // - `push()` & `pop()` when visiting conditional branches
1467 // - backup & restore via `save_ids` when visiting out-of-order sections
1468 tw.safe_ids = Object.create(null);
1469 this.walk(tw);
1470 });
1471
1472 AST_Symbol.DEFMETHOD("fixed_value", function() {
1473 var fixed = this.definition().fixed;
1474 if (!fixed) return fixed;
1475 if (this.fixed) fixed = this.fixed;
1476 return fixed instanceof AST_Node ? fixed : fixed();
1477 });
1478
1479 AST_SymbolRef.DEFMETHOD("is_immutable", function() {
1480 var def = this.redef || this.definition();
1481 return def.orig.length == 1 && def.orig[0] instanceof AST_SymbolLambda;
1482 });
1483
1484 AST_Node.DEFMETHOD("convert_symbol", noop);
1485 function convert_destructured(type, process) {
1486 return this.transform(new TreeTransformer(function(node, descend) {
1487 if (node instanceof AST_DefaultValue) {
1488 node = node.clone();
1489 node.name = node.name.transform(this);
1490 return node;
1491 }
1492 if (node instanceof AST_Destructured) {
1493 node = node.clone();
1494 descend(node, this);
1495 return node;
1496 }
1497 if (node instanceof AST_DestructuredKeyVal) {
1498 node = node.clone();
1499 node.value = node.value.transform(this);
1500 return node;
1501 }
1502 return node.convert_symbol(type, process);
1503 }));
1504 }
1505 AST_DefaultValue.DEFMETHOD("convert_symbol", convert_destructured);
1506 AST_Destructured.DEFMETHOD("convert_symbol", convert_destructured);
1507 function convert_symbol(type, process) {
1508 var node = make_node(type, this, this);
1509 process(node, this);
1510 return node;
1511 }
1512 AST_SymbolDeclaration.DEFMETHOD("convert_symbol", convert_symbol);
1513 AST_SymbolRef.DEFMETHOD("convert_symbol", convert_symbol);
1514
1515 function mark_destructured(process, tw) {
1516 var marker = new TreeWalker(function(node) {
1517 if (node instanceof AST_DefaultValue) {
1518 node.value.walk(tw);
1519 node.name.walk(marker);
1520 return true;
1521 }
1522 if (node instanceof AST_DestructuredKeyVal) {
1523 if (node.key instanceof AST_Node) node.key.walk(tw);
1524 node.value.walk(marker);
1525 return true;
1526 }
1527 return process(node);
1528 });
1529 this.walk(marker);
1530 }
1531 AST_DefaultValue.DEFMETHOD("mark_symbol", mark_destructured);
1532 AST_Destructured.DEFMETHOD("mark_symbol", mark_destructured);
1533 function mark_symbol(process) {
1534 return process(this);
1535 }
1536 AST_SymbolDeclaration.DEFMETHOD("mark_symbol", mark_symbol);
1537 AST_SymbolRef.DEFMETHOD("mark_symbol", mark_symbol);
1538
1539 AST_Node.DEFMETHOD("match_symbol", function(predicate) {
1540 return predicate(this);
1541 });
1542 function match_destructured(predicate, ignore_side_effects) {
1543 var found = false;
1544 var tw = new TreeWalker(function(node) {
1545 if (found) return true;
1546 if (node instanceof AST_DefaultValue) {
1547 if (!ignore_side_effects) return found = true;
1548 node.name.walk(tw);
1549 return true;
1550 }
1551 if (node instanceof AST_DestructuredKeyVal) {
1552 if (!ignore_side_effects && node.key instanceof AST_Node) return found = true;
1553 node.value.walk(tw);
1554 return true;
1555 }
1556 if (predicate(node)) return found = true;
1557 });
1558 this.walk(tw);
1559 return found;
1560 }
1561 AST_DefaultValue.DEFMETHOD("match_symbol", match_destructured);
1562 AST_Destructured.DEFMETHOD("match_symbol", match_destructured);
1563
1564 function in_async_generator(scope) {
1565 return scope instanceof AST_AsyncGeneratorDefun || scope instanceof AST_AsyncGeneratorFunction;
1566 }
1567
1568 function find_scope(compressor) {
1569 var level = 0, node;
1570 while (node = compressor.parent(level++)) {
1571 if (node.variables) return node;
1572 }
1573 }
1574
1575 var identifier_atom = makePredicate("Infinity NaN undefined");
1576 function is_lhs_read_only(lhs, compressor) {
1577 if (lhs instanceof AST_ObjectIdentity) return true;
1578 if (lhs instanceof AST_PropAccess) {
1579 if (lhs.property === "__proto__") return true;
1580 lhs = lhs.expression;
1581 if (lhs instanceof AST_SymbolRef) {
1582 if (lhs.is_immutable()) return false;
1583 lhs = lhs.fixed_value();
1584 }
1585 if (!lhs) return true;
1586 if (lhs.tail_node().is_constant()) return true;
1587 return is_lhs_read_only(lhs, compressor);
1588 }
1589 if (lhs instanceof AST_SymbolRef) {
1590 if (lhs.is_immutable()) return true;
1591 var def = lhs.definition();
1592 return compressor.exposed(def) && identifier_atom[def.name];
1593 }
1594 return false;
1595 }
1596
1597 function make_node(ctor, orig, props) {
1598 if (!props) props = {};
1599 if (orig) {
1600 if (!props.start) props.start = orig.start;
1601 if (!props.end) props.end = orig.end;
1602 }
1603 return new ctor(props);
1604 }
1605
1606 function make_sequence(orig, expressions) {
1607 if (expressions.length == 1) return expressions[0];
1608 return make_node(AST_Sequence, orig, {
1609 expressions: expressions.reduce(merge_sequence, [])
1610 });
1611 }
1612
1613 function make_node_from_constant(val, orig) {
1614 switch (typeof val) {
1615 case "string":
1616 return make_node(AST_String, orig, {
1617 value: val
1618 });
1619 case "number":
1620 if (isNaN(val)) return make_node(AST_NaN, orig);
1621 if (isFinite(val)) {
1622 return 1 / val < 0 ? make_node(AST_UnaryPrefix, orig, {
1623 operator: "-",
1624 expression: make_node(AST_Number, orig, { value: -val })
1625 }) : make_node(AST_Number, orig, { value: val });
1626 }
1627 return val < 0 ? make_node(AST_UnaryPrefix, orig, {
1628 operator: "-",
1629 expression: make_node(AST_Infinity, orig)
1630 }) : make_node(AST_Infinity, orig);
1631 case "boolean":
1632 return make_node(val ? AST_True : AST_False, orig);
1633 case "undefined":
1634 return make_node(AST_Undefined, orig);
1635 default:
1636 if (val === null) {
1637 return make_node(AST_Null, orig, { value: null });
1638 }
1639 if (val instanceof RegExp) {
1640 return make_node(AST_RegExp, orig, { value: val });
1641 }
1642 throw new Error(string_template("Can't handle constant of type: {type}", {
1643 type: typeof val
1644 }));
1645 }
1646 }
1647
1648 function needs_unbinding(compressor, val) {
1649 return val instanceof AST_PropAccess
1650 || is_undeclared_ref(val) && val.name == "eval";
1651 }
1652
1653 // we shouldn't compress (1,func)(something) to
1654 // func(something) because that changes the meaning of
1655 // the func (becomes lexical instead of global).
1656 function maintain_this_binding(compressor, parent, orig, val) {
1657 var wrap = false;
1658 if (parent.TYPE == "Call") {
1659 wrap = parent.expression === orig && needs_unbinding(compressor, val);
1660 } else if (parent instanceof AST_Template) {
1661 wrap = parent.tag === orig && needs_unbinding(compressor, val);
1662 } else if (parent instanceof AST_UnaryPrefix) {
1663 wrap = parent.operator == "delete"
1664 || parent.operator == "typeof" && is_undeclared_ref(val);
1665 }
1666 return wrap ? make_sequence(orig, [ make_node(AST_Number, orig, { value: 0 }), val ]) : val;
1667 }
1668
1669 function merge_sequence(array, node) {
1670 if (node instanceof AST_Sequence) {
1671 array.push.apply(array, node.expressions);
1672 } else {
1673 array.push(node);
1674 }
1675 return array;
1676 }
1677
1678 function is_lexical_definition(stat) {
1679 return stat instanceof AST_Const || stat instanceof AST_DefClass || stat instanceof AST_Let;
1680 }
1681
1682 function safe_to_trim(stat) {
1683 if (stat instanceof AST_LambdaDefinition) {
1684 var def = stat.name.definition();
1685 var scope = stat.name.scope;
1686 return def.scope === scope || all(def.references, function(ref) {
1687 var s = ref.scope;
1688 do {
1689 if (s === scope) return true;
1690 } while (s = s.parent_scope);
1691 });
1692 }
1693 return !is_lexical_definition(stat);
1694 }
1695
1696 function as_statement_array(thing) {
1697 if (thing === null) return [];
1698 if (thing instanceof AST_BlockStatement) return all(thing.body, safe_to_trim) ? thing.body : [ thing ];
1699 if (thing instanceof AST_EmptyStatement) return [];
1700 if (is_statement(thing)) return [ thing ];
1701 throw new Error("Can't convert thing to statement array");
1702 }
1703
1704 function is_empty(thing) {
1705 if (thing === null) return true;
1706 if (thing instanceof AST_EmptyStatement) return true;
1707 if (thing instanceof AST_BlockStatement) return thing.body.length == 0;
1708 return false;
1709 }
1710
1711 function has_declarations_only(block) {
1712 return all(block.body, function(stat) {
1713 return is_empty(stat)
1714 || stat instanceof AST_Defun
1715 || stat instanceof AST_Var && declarations_only(stat);
1716 });
1717 }
1718
1719 function loop_body(x) {
1720 if (x instanceof AST_IterationStatement) {
1721 return x.body instanceof AST_BlockStatement ? x.body : x;
1722 }
1723 return x;
1724 }
1725
1726 function is_iife_call(node) {
1727 if (node.TYPE != "Call") return false;
1728 do {
1729 node = node.expression;
1730 } while (node instanceof AST_PropAccess);
1731 return node instanceof AST_LambdaExpression ? !is_arrow(node) : is_iife_call(node);
1732 }
1733
1734 function is_iife_single(call) {
1735 var exp = call.expression;
1736 if (exp.name) return false;
1737 if (!(call instanceof AST_New)) return true;
1738 var found = false;
1739 exp.walk(new TreeWalker(function(node) {
1740 if (found) return true;
1741 if (node instanceof AST_NewTarget) return found = true;
1742 if (node instanceof AST_Scope && node !== exp) return true;
1743 }));
1744 return !found;
1745 }
1746
1747 function is_undeclared_ref(node) {
1748 return node instanceof AST_SymbolRef && node.definition().undeclared;
1749 }
1750
1751 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");
1752 AST_SymbolRef.DEFMETHOD("is_declared", function(compressor) {
1753 return this.defined
1754 || !this.definition().undeclared
1755 || compressor.option("unsafe") && global_names[this.name];
1756 });
1757
1758 function declarations_only(node) {
1759 return all(node.definitions, function(var_def) {
1760 return !var_def.value;
1761 });
1762 }
1763
1764 function is_declaration(stat, lexical) {
1765 if (stat instanceof AST_DefClass) return lexical && !stat.extends && all(stat.properties, function(prop) {
1766 if (prop.key instanceof AST_Node) return false;
1767 if (prop instanceof AST_ClassField && prop.static && prop.value) return false;
1768 return true;
1769 });
1770 if (stat instanceof AST_Definitions) return (lexical || stat instanceof AST_Var) && declarations_only(stat);
1771 if (stat instanceof AST_ExportDeclaration) return is_declaration(stat.body, lexical);
1772 if (stat instanceof AST_ExportDefault) return is_declaration(stat.body, lexical);
1773 return stat instanceof AST_LambdaDefinition;
1774 }
1775
1776 function is_last_statement(body, stat) {
1777 var index = body.lastIndexOf(stat);
1778 if (index < 0) return false;
1779 while (++index < body.length) {
1780 if (!is_declaration(body[index], true)) return false;
1781 }
1782 return true;
1783 }
1784
1785 function tighten_body(statements, compressor) {
1786 var in_loop, in_try, scope;
1787 find_loop_scope_try();
1788 var CHANGED, max_iter = 10;
1789 do {
1790 CHANGED = false;
1791 eliminate_spurious_blocks(statements);
1792 if (compressor.option("dead_code")) {
1793 eliminate_dead_code(statements, compressor);
1794 }
1795 if (compressor.option("if_return")) {
1796 handle_if_return(statements, compressor);
1797 }
1798 if (compressor.sequences_limit > 0) {
1799 sequencesize(statements, compressor);
1800 sequencesize_2(statements, compressor);
1801 }
1802 if (compressor.option("join_vars")) {
1803 join_consecutive_vars(statements);
1804 }
1805 if (compressor.option("collapse_vars")) {
1806 collapse(statements, compressor);
1807 }
1808 } while (CHANGED && max_iter-- > 0);
1809 return statements;
1810
1811 function find_loop_scope_try() {
1812 var node = compressor.self(), level = 0;
1813 do {
1814 if (node instanceof AST_Catch) {
1815 if (compressor.parent(level).bfinally) {
1816 if (!in_try) in_try = {};
1817 in_try.bfinally = true;
1818 }
1819 level++;
1820 } else if (node instanceof AST_Finally) {
1821 level++;
1822 } else if (node instanceof AST_IterationStatement) {
1823 in_loop = true;
1824 } else if (node instanceof AST_Scope) {
1825 scope = node;
1826 break;
1827 } else if (node instanceof AST_Try) {
1828 if (!in_try) in_try = {};
1829 if (node.bcatch) in_try.bcatch = true;
1830 if (node.bfinally) in_try.bfinally = true;
1831 }
1832 } while (node = compressor.parent(level++));
1833 }
1834
1835 // Search from right to left for assignment-like expressions:
1836 // - `var a = x;`
1837 // - `a = x;`
1838 // - `++a`
1839 // For each candidate, scan from left to right for first usage, then try
1840 // to fold assignment into the site for compression.
1841 // Will not attempt to collapse assignments into or past code blocks
1842 // which are not sequentially executed, e.g. loops and conditionals.
1843 function collapse(statements, compressor) {
1844 if (scope.pinned()) return statements;
1845 var args;
1846 var assignments = Object.create(null);
1847 var candidates = [];
1848 var declare_only = Object.create(null);
1849 var force_single;
1850 var stat_index = statements.length;
1851 var scanner = new TreeTransformer(function(node, descend) {
1852 if (abort) return node;
1853 // Skip nodes before `candidate` as quickly as possible
1854 if (!hit) {
1855 if (node !== hit_stack[hit_index]) return node;
1856 hit_index++;
1857 if (hit_index < hit_stack.length) return handle_custom_scan_order(node, scanner);
1858 hit = true;
1859 stop_after = (value_def ? find_stop_value : find_stop)(node, 0);
1860 if (stop_after === node) abort = true;
1861 return node;
1862 }
1863 // Stop immediately if these node types are encountered
1864 var parent = scanner.parent();
1865 if (should_stop(node, parent)) {
1866 abort = true;
1867 return node;
1868 }
1869 // Stop only if candidate is found within conditional branches
1870 if (!stop_if_hit && in_conditional(node, parent)) {
1871 stop_if_hit = parent;
1872 }
1873 // Skip transient nodes caused by single-use variable replacement
1874 if (node.single_use && parent instanceof AST_VarDef && parent.value === node) return node;
1875 // Replace variable with assignment when found
1876 var hit_rhs;
1877 if (!(node instanceof AST_SymbolDeclaration)
1878 && (scan_lhs && lhs.equivalent_to(node)
1879 || scan_rhs && (hit_rhs = scan_rhs(node, this)))) {
1880 if (!can_replace || stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) {
1881 if (!hit_rhs && !value_def) abort = true;
1882 return node;
1883 }
1884 if (is_lhs(node, parent)) {
1885 if (value_def && !hit_rhs) assign_used = true;
1886 return node;
1887 } else if (value_def) {
1888 if (stop_if_hit && assign_pos == 0) assign_pos = remaining - replaced;
1889 if (!hit_rhs) replaced++;
1890 return node;
1891 } else {
1892 replaced++;
1893 }
1894 CHANGED = abort = true;
1895 AST_Node.info("Collapsing {node} [{file}:{line},{col}]", {
1896 node: node,
1897 file: node.start.file,
1898 line: node.start.line,
1899 col: node.start.col,
1900 });
1901 if (candidate.TYPE == "Binary") return make_node(AST_Assign, candidate, {
1902 operator: "=",
1903 left: candidate.right.left,
1904 right: make_node(AST_Conditional, candidate, {
1905 condition: candidate.operator == "&&" ? candidate.left : candidate.left.negate(compressor),
1906 consequent: candidate.right.right,
1907 alternative: node,
1908 }),
1909 });
1910 if (candidate instanceof AST_UnaryPostfix) {
1911 if (lhs instanceof AST_SymbolRef) lhs.definition().fixed = false;
1912 return make_node(AST_UnaryPrefix, candidate, candidate);
1913 }
1914 if (candidate instanceof AST_VarDef) {
1915 var def = candidate.name.definition();
1916 if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
1917 def.replaced++;
1918 return maintain_this_binding(compressor, parent, node, candidate.value);
1919 }
1920 return make_node(AST_Assign, candidate, {
1921 operator: "=",
1922 left: make_node(AST_SymbolRef, candidate.name, candidate.name),
1923 right: candidate.value,
1924 });
1925 }
1926 var assign = candidate;
1927 while (assign.write_only) {
1928 assign.write_only = false;
1929 if (!(assign instanceof AST_Assign)) break;
1930 assign = assign.right;
1931 }
1932 return candidate;
1933 }
1934 // These node types have child nodes that execute sequentially,
1935 // but are otherwise not safe to scan into or beyond them.
1936 if (is_last_node(node, parent) || may_throw(node)) {
1937 stop_after = node;
1938 if (node instanceof AST_Scope) abort = true;
1939 }
1940 // Scan but don't replace inside getter/setter
1941 if (node instanceof AST_Accessor) {
1942 var replace = can_replace;
1943 can_replace = false;
1944 descend(node, scanner);
1945 can_replace = replace;
1946 return signal_abort(node);
1947 }
1948 // Scan but don't replace inside destructuring expression
1949 if (node instanceof AST_Destructured) {
1950 var replace = can_replace;
1951 can_replace = false;
1952 descend(node, scanner);
1953 can_replace = replace;
1954 return signal_abort(node);
1955 }
1956 // Scan but don't replace inside default value
1957 if (node instanceof AST_DefaultValue) {
1958 node.name = node.name.transform(scanner);
1959 var replace = can_replace;
1960 can_replace = false;
1961 node.value = node.value.transform(scanner);
1962 can_replace = replace;
1963 return signal_abort(node);
1964 }
1965 // Scan but don't replace inside block scope with colliding variable
1966 if (node instanceof AST_BlockScope
1967 && !(node instanceof AST_Scope)
1968 && !(node.variables && node.variables.all(function(def) {
1969 return !lvalues.has(def.name);
1970 }))) {
1971 var replace = can_replace;
1972 can_replace = false;
1973 if (!handle_custom_scan_order(node, scanner)) descend(node, scanner);
1974 can_replace = replace;
1975 return signal_abort(node);
1976 }
1977 return handle_custom_scan_order(node, scanner);
1978 }, signal_abort);
1979 var multi_replacer = new TreeTransformer(function(node) {
1980 if (abort) return node;
1981 // Skip nodes before `candidate` as quickly as possible
1982 if (!hit) {
1983 if (node !== hit_stack[hit_index]) return node;
1984 hit_index++;
1985 switch (hit_stack.length - hit_index) {
1986 case 0:
1987 hit = true;
1988 if (assign_used) return node;
1989 if (node !== candidate) return node;
1990 if (node instanceof AST_VarDef) return node;
1991 def.replaced++;
1992 var parent = multi_replacer.parent();
1993 if (parent instanceof AST_Sequence && parent.tail_node() !== node) {
1994 value_def.replaced++;
1995 return List.skip;
1996 }
1997 return rvalue;
1998 case 1:
1999 if (!assign_used && node.body === candidate) {
2000 hit = true;
2001 def.replaced++;
2002 value_def.replaced++;
2003 return null;
2004 }
2005 default:
2006 return handle_custom_scan_order(node, multi_replacer);
2007 }
2008 }
2009 // Replace variable when found
2010 if (node instanceof AST_SymbolRef && node.definition() === def) {
2011 if (is_lhs(node, multi_replacer.parent())) return node;
2012 if (!--replaced) abort = true;
2013 var ref = rvalue.clone();
2014 ref.scope = node.scope;
2015 ref.reference();
2016 if (replaced == assign_pos) {
2017 abort = true;
2018 return make_node(AST_Assign, candidate, {
2019 operator: "=",
2020 left: node,
2021 right: ref,
2022 });
2023 }
2024 def.replaced++;
2025 return ref;
2026 }
2027 // Skip (non-executed) functions and (leading) default case in switch statements
2028 if (node instanceof AST_Default || node instanceof AST_Scope) return node;
2029 }, patch_sequence);
2030 while (--stat_index >= 0) {
2031 // Treat parameters as collapsible in IIFE, i.e.
2032 // function(a, b){ ... }(x());
2033 // would be translated into equivalent assignments:
2034 // var a = x(), b = undefined;
2035 if (stat_index == 0 && compressor.option("unused")) extract_args();
2036 // Find collapsible assignments
2037 var hit_stack = [];
2038 extract_candidates(statements[stat_index]);
2039 while (candidates.length > 0) {
2040 hit_stack = candidates.pop();
2041 var hit_index = 0;
2042 var candidate = hit_stack[hit_stack.length - 1];
2043 var assign_pos = -1;
2044 var assign_used = false;
2045 var remaining;
2046 var value_def = null;
2047 var stop_after = null;
2048 var stop_if_hit = null;
2049 var lhs = get_lhs(candidate);
2050 var side_effects = lhs && lhs.has_side_effects(compressor);
2051 var scan_lhs = lhs && !side_effects && !is_lhs_read_only(lhs, compressor);
2052 var scan_rhs = foldable(candidate);
2053 if (!scan_lhs && !scan_rhs) continue;
2054 var funarg = candidate.name instanceof AST_SymbolFunarg;
2055 var may_throw = return_false;
2056 if (candidate.may_throw(compressor)) {
2057 if (funarg && is_async(scope)) continue;
2058 may_throw = in_try ? function(node) {
2059 return node.has_side_effects(compressor);
2060 } : side_effects_external;
2061 }
2062 var read_toplevel = false;
2063 var modify_toplevel = false;
2064 // Locate symbols which may execute code outside of scanning range
2065 var well_defined = true;
2066 var lvalues = get_lvalues(candidate);
2067 var lhs_local = is_lhs_local(lhs);
2068 var rvalue = get_rvalue(candidate);
2069 if (!side_effects) side_effects = value_has_side_effects();
2070 var check_destructured = in_try || !lhs_local ? function(node) {
2071 return node instanceof AST_Destructured;
2072 } : return_false;
2073 var replace_all = replace_all_symbols(candidate);
2074 var hit = funarg;
2075 var abort = false;
2076 var replaced = 0;
2077 var can_replace = !args || !hit;
2078 if (!can_replace) {
2079 for (var j = candidate.arg_index + 1; !abort && j < args.length; j++) {
2080 if (args[j]) args[j].transform(scanner);
2081 }
2082 can_replace = true;
2083 }
2084 for (var i = stat_index; !abort && i < statements.length; i++) {
2085 statements[i].transform(scanner);
2086 }
2087 if (value_def) {
2088 if (!replaced || remaining > replaced + assign_used) {
2089 candidates.push(hit_stack);
2090 force_single = true;
2091 continue;
2092 }
2093 if (replaced == assign_pos) assign_used = true;
2094 var def = lhs.definition();
2095 abort = false;
2096 hit_index = 0;
2097 hit = funarg;
2098 for (var i = stat_index; !abort && i < statements.length; i++) {
2099 if (!statements[i].transform(multi_replacer)) statements.splice(i--, 1);
2100 }
2101 replaced = candidate instanceof AST_VarDef
2102 && candidate === hit_stack[hit_stack.length - 1]
2103 && def.references.length == def.replaced
2104 && !compressor.exposed(def);
2105 value_def.last_ref = false;
2106 value_def.single_use = false;
2107 CHANGED = true;
2108 }
2109 if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
2110 }
2111 }
2112
2113 function signal_abort(node) {
2114 if (abort) return node;
2115 if (stop_after === node) abort = true;
2116 if (stop_if_hit === node) stop_if_hit = null;
2117 return node;
2118 }
2119
2120 function handle_custom_scan_order(node, tt) {
2121 if (!(node instanceof AST_BlockScope)) {
2122 if (!(node instanceof AST_ClassProperty && !node.static)) return;
2123 // Skip non-static class property values
2124 if (node.key instanceof AST_Node) node.key = node.key.transform(tt);
2125 return node;
2126 }
2127 // Skip (non-executed) functions
2128 if (node instanceof AST_Scope) return node;
2129 // Scan object only in a for-in/of statement
2130 if (node instanceof AST_ForEnumeration) {
2131 node.object = node.object.transform(tt);
2132 abort = true;
2133 return node;
2134 }
2135 // Scan first case expression only in a switch statement
2136 if (node instanceof AST_Switch) {
2137 node.expression = node.expression.transform(tt);
2138 for (var i = 0; !abort && i < node.body.length; i++) {
2139 var branch = node.body[i];
2140 if (branch instanceof AST_Case) {
2141 if (!hit) {
2142 if (branch !== hit_stack[hit_index]) continue;
2143 hit_index++;
2144 }
2145 branch.expression = branch.expression.transform(tt);
2146 if (!replace_all) break;
2147 scan_rhs = false;
2148 }
2149 }
2150 abort = true;
2151 return node;
2152 }
2153 }
2154
2155 function is_direct_assignment(node, parent) {
2156 if (parent instanceof AST_Assign) return parent.operator == "=" && parent.left === node;
2157 if (parent instanceof AST_DefaultValue) return parent.name === node;
2158 if (parent instanceof AST_DestructuredArray) return true;
2159 if (parent instanceof AST_DestructuredKeyVal) return parent.value === node;
2160 }
2161
2162 function should_stop(node, parent) {
2163 if (node === rvalue) return true;
2164 if (parent instanceof AST_For) {
2165 if (node !== parent.init) return true;
2166 }
2167 if (node instanceof AST_Assign) {
2168 return node.operator != "=" && lhs.equivalent_to(node.left);
2169 }
2170 if (node instanceof AST_Call) {
2171 if (!(lhs instanceof AST_PropAccess)) return false;
2172 if (!lhs.equivalent_to(node.expression)) return false;
2173 return !(rvalue instanceof AST_LambdaExpression && !rvalue.contains_this());
2174 }
2175 if (node instanceof AST_Class) return !compressor.has_directive("use strict");
2176 if (node instanceof AST_Debugger) return true;
2177 if (node instanceof AST_Defun) return funarg && lhs.name === node.name.name;
2178 if (node instanceof AST_DestructuredKeyVal) return node.key instanceof AST_Node;
2179 if (node instanceof AST_DWLoop) return true;
2180 if (node instanceof AST_LoopControl) return true;
2181 if (node instanceof AST_SymbolRef) {
2182 if (node.is_declared(compressor)) {
2183 if (node.fixed_value()) return false;
2184 if (can_drop_symbol(node)) {
2185 return !(parent instanceof AST_PropAccess && parent.expression === node)
2186 && is_arguments(node.definition());
2187 }
2188 } else if (is_direct_assignment(node, parent)) {
2189 return false;
2190 }
2191 if (!replace_all) return true;
2192 scan_rhs = false;
2193 return false;
2194 }
2195 if (node instanceof AST_Try) return true;
2196 if (node instanceof AST_With) return true;
2197 return false;
2198 }
2199
2200 function in_conditional(node, parent) {
2201 if (parent instanceof AST_Assign) return parent.left !== node && lazy_op[parent.operator.slice(0, -1)];
2202 if (parent instanceof AST_Binary) return parent.left !== node && lazy_op[parent.operator];
2203 if (parent instanceof AST_Call) return parent.optional && parent.expression !== node;
2204 if (parent instanceof AST_Case) return parent.expression !== node;
2205 if (parent instanceof AST_Conditional) return parent.condition !== node;
2206 if (parent instanceof AST_If) return parent.condition !== node;
2207 if (parent instanceof AST_Sub) return parent.optional && parent.expression !== node;
2208 }
2209
2210 function is_last_node(node, parent) {
2211 if (node instanceof AST_Await) return true;
2212 if (node.TYPE == "Binary") return node.operator == "in" && !is_object(node.right);
2213 if (node instanceof AST_Call) {
2214 var def, fn = node.expression;
2215 if (fn instanceof AST_SymbolRef) {
2216 def = fn.definition();
2217 fn = fn.fixed_value();
2218 }
2219 if (!(fn instanceof AST_Lambda)) return !node.is_expr_pure(compressor);
2220 if (def && recursive_ref(compressor, def, fn)) return true;
2221 if (fn.collapse_scanning) return false;
2222 fn.collapse_scanning = true;
2223 var replace = can_replace;
2224 can_replace = false;
2225 var after = stop_after;
2226 var if_hit = stop_if_hit;
2227 if (!all(fn.argnames, function(argname) {
2228 if (argname instanceof AST_DefaultValue) {
2229 argname.value.transform(scanner);
2230 if (abort) return false;
2231 argname = argname.name;
2232 }
2233 return !(argname instanceof AST_Destructured);
2234 })) {
2235 abort = true;
2236 } else if (is_arrow(fn) && fn.value) {
2237 fn.value.transform(scanner);
2238 } else for (var i = 0; !abort && i < fn.body.length; i++) {
2239 var stat = fn.body[i];
2240 if (stat instanceof AST_Return) {
2241 if (stat.value) stat.value.transform(scanner);
2242 break;
2243 }
2244 stat.transform(scanner);
2245 }
2246 stop_if_hit = if_hit;
2247 stop_after = after;
2248 can_replace = replace;
2249 delete fn.collapse_scanning;
2250 if (!abort) return false;
2251 abort = false;
2252 return true;
2253 }
2254 if (node instanceof AST_Exit) {
2255 if (in_try) {
2256 if (in_try.bfinally) return true;
2257 if (in_try.bcatch && node instanceof AST_Throw) return true;
2258 }
2259 return side_effects || lhs instanceof AST_PropAccess || may_modify(lhs);
2260 }
2261 if (node instanceof AST_Function) {
2262 return compressor.option("ie") && node.name && lvalues.has(node.name.name);
2263 }
2264 if (node instanceof AST_ObjectIdentity) return symbol_in_lvalues(node, parent);
2265 if (node instanceof AST_PropAccess) {
2266 if (side_effects) return true;
2267 var exp = node.expression;
2268 if (exp instanceof AST_SymbolRef && is_arguments(exp.definition())) return true;
2269 if (compressor.option("unsafe")) {
2270 if (is_undeclared_ref(exp) && global_names[exp.name]) return false;
2271 if (is_static_fn(exp)) return false;
2272 }
2273 if (!well_defined) return true;
2274 if (value_def) return false;
2275 if (!in_try && lhs_local) return false;
2276 if (node.optional) return false;
2277 return exp.may_throw_on_access(compressor);
2278 }
2279 if (node instanceof AST_Spread) return true;
2280 if (node instanceof AST_SymbolRef) {
2281 if (symbol_in_lvalues(node, parent)) return !is_direct_assignment(node, parent);
2282 if (side_effects && may_modify(node)) return true;
2283 var def = node.definition();
2284 return (in_try || def.scope.resolve() !== scope) && !can_drop_symbol(node);
2285 }
2286 if (node instanceof AST_Template) return !node.is_expr_pure(compressor);
2287 if (node instanceof AST_VarDef) {
2288 if (check_destructured(node.name)) return true;
2289 return (node.value || parent instanceof AST_Let) && node.name.match_symbol(function(node) {
2290 return node instanceof AST_SymbolDeclaration
2291 && (lvalues.has(node.name) || side_effects && may_modify(node));
2292 }, true);
2293 }
2294 if (node instanceof AST_Yield) return true;
2295 var sym = is_lhs(node.left, node);
2296 if (!sym) return false;
2297 if (sym instanceof AST_PropAccess) return true;
2298 if (check_destructured(sym)) return true;
2299 return sym.match_symbol(function(node) {
2300 return node instanceof AST_SymbolRef
2301 && (lvalues.has(node.name) || read_toplevel && compressor.exposed(node.definition()));
2302 }, true);
2303 }
2304
2305 function may_throw_destructured(node, value) {
2306 if (!value) return !(node instanceof AST_Symbol);
2307 if (node instanceof AST_DefaultValue) {
2308 return value.has_side_effects(compressor)
2309 || node.value.has_side_effects(compressor)
2310 || may_throw_destructured(node.name, is_undefined(value) && node.value);
2311 }
2312 if (node instanceof AST_Destructured) {
2313 if (node.rest && may_throw_destructured(node.rest)) return true;
2314 if (node instanceof AST_DestructuredArray) {
2315 if (!(value instanceof AST_Array || value.is_string(compressor))) return true;
2316 return !all(node.elements, function(element) {
2317 return !may_throw_destructured(element);
2318 });
2319 }
2320 if (node instanceof AST_DestructuredObject) {
2321 if (!value.is_defined(compressor)) return true;
2322 return !all(node.properties, function(prop) {
2323 if (prop instanceof AST_Node && prop.has_side_effects(compressor)) return false;
2324 return !may_throw_destructured(prop.value);
2325 });
2326 }
2327 }
2328 }
2329
2330 function extract_args() {
2331 var iife, fn = compressor.self();
2332 if (fn instanceof AST_LambdaExpression
2333 && !is_generator(fn)
2334 && !fn.uses_arguments
2335 && !fn.pinned()
2336 && (iife = compressor.parent()) instanceof AST_Call
2337 && iife.expression === fn
2338 && is_iife_single(iife)
2339 && all(iife.args, function(arg) {
2340 return !(arg instanceof AST_Spread);
2341 })) {
2342 var fn_strict = compressor.has_directive("use strict");
2343 if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
2344 var has_await = is_async(fn) ? function(node) {
2345 return node instanceof AST_Symbol && node.name == "await";
2346 } : function(node) {
2347 return node instanceof AST_Await && !tw.find_parent(AST_Scope);
2348 };
2349 var arg_scope = null;
2350 var tw = new TreeWalker(function(node, descend) {
2351 if (!arg) return true;
2352 if (has_await(node) || node instanceof AST_Yield) {
2353 arg = null;
2354 return true;
2355 }
2356 if (node instanceof AST_ObjectIdentity && (fn_strict || !arg_scope)) {
2357 arg = null;
2358 return true;
2359 }
2360 if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
2361 var s = node.definition().scope;
2362 if (s !== scope) while (s = s.parent_scope) {
2363 if (s === scope) return true;
2364 }
2365 arg = null;
2366 }
2367 if (node instanceof AST_Scope && !is_arrow(node)) {
2368 var save_scope = arg_scope;
2369 arg_scope = node;
2370 descend();
2371 arg_scope = save_scope;
2372 return true;
2373 }
2374 });
2375 args = iife.args.slice();
2376 var len = args.length;
2377 var names = Object.create(null);
2378 for (var i = fn.argnames.length; --i >= 0;) {
2379 var sym = fn.argnames[i];
2380 var arg = args[i];
2381 var value;
2382 if (sym instanceof AST_DefaultValue) {
2383 value = sym.value;
2384 sym = sym.name;
2385 args[len + i] = value;
2386 }
2387 if (sym instanceof AST_Destructured) {
2388 if (!may_throw_destructured(sym, arg)) continue;
2389 candidates.length = 0;
2390 break;
2391 }
2392 if (sym.name in names) continue;
2393 names[sym.name] = true;
2394 if (value) arg = !arg || is_undefined(arg) ? value : null;
2395 if (!arg && !value) {
2396 arg = make_node(AST_Undefined, sym).transform(compressor);
2397 } else if (arg instanceof AST_Lambda && arg.pinned()) {
2398 arg = null;
2399 } else if (arg) {
2400 arg.walk(tw);
2401 }
2402 if (!arg) continue;
2403 var candidate = make_node(AST_VarDef, sym, {
2404 name: sym,
2405 value: arg,
2406 });
2407 candidate.name_index = i;
2408 candidate.arg_index = value ? len + i : i;
2409 candidates.unshift([ candidate ]);
2410 }
2411 }
2412 }
2413
2414 function extract_candidates(expr, unused) {
2415 hit_stack.push(expr);
2416 if (expr instanceof AST_Array) {
2417 expr.elements.forEach(function(node) {
2418 extract_candidates(node, unused);
2419 });
2420 } else if (expr instanceof AST_Assign) {
2421 var lhs = expr.left;
2422 if (!(lhs instanceof AST_Destructured)) candidates.push(hit_stack.slice());
2423 extract_candidates(lhs);
2424 extract_candidates(expr.right);
2425 if (lhs instanceof AST_SymbolRef && expr.operator == "=") {
2426 assignments[lhs.name] = (assignments[lhs.name] || 0) + 1;
2427 }
2428 } else if (expr instanceof AST_Await) {
2429 extract_candidates(expr.expression, unused);
2430 } else if (expr instanceof AST_Binary) {
2431 var lazy = lazy_op[expr.operator];
2432 if (unused
2433 && lazy
2434 && expr.operator != "??"
2435 && expr.right instanceof AST_Assign
2436 && expr.right.operator == "="
2437 && !(expr.right.left instanceof AST_Destructured)) {
2438 candidates.push(hit_stack.slice());
2439 }
2440 extract_candidates(expr.left, !lazy && unused);
2441 extract_candidates(expr.right, unused);
2442 } else if (expr instanceof AST_Call) {
2443 extract_candidates(expr.expression);
2444 expr.args.forEach(extract_candidates);
2445 } else if (expr instanceof AST_Case) {
2446 extract_candidates(expr.expression);
2447 } else if (expr instanceof AST_Conditional) {
2448 extract_candidates(expr.condition);
2449 extract_candidates(expr.consequent, unused);
2450 extract_candidates(expr.alternative, unused);
2451 } else if (expr instanceof AST_Definitions) {
2452 expr.definitions.forEach(extract_candidates);
2453 } else if (expr instanceof AST_Dot) {
2454 extract_candidates(expr.expression);
2455 } else if (expr instanceof AST_DWLoop) {
2456 extract_candidates(expr.condition);
2457 if (!(expr.body instanceof AST_Block)) {
2458 extract_candidates(expr.body);
2459 }
2460 } else if (expr instanceof AST_Exit) {
2461 if (expr.value) extract_candidates(expr.value);
2462 } else if (expr instanceof AST_For) {
2463 if (expr.init) extract_candidates(expr.init, true);
2464 if (expr.condition) extract_candidates(expr.condition);
2465 if (expr.step) extract_candidates(expr.step, true);
2466 if (!(expr.body instanceof AST_Block)) {
2467 extract_candidates(expr.body);
2468 }
2469 } else if (expr instanceof AST_ForEnumeration) {
2470 extract_candidates(expr.object);
2471 if (!(expr.body instanceof AST_Block)) {
2472 extract_candidates(expr.body);
2473 }
2474 } else if (expr instanceof AST_If) {
2475 extract_candidates(expr.condition);
2476 if (!(expr.body instanceof AST_Block)) {
2477 extract_candidates(expr.body);
2478 }
2479 if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
2480 extract_candidates(expr.alternative);
2481 }
2482 } else if (expr instanceof AST_Object) {
2483 expr.properties.forEach(function(prop) {
2484 hit_stack.push(prop);
2485 if (prop.key instanceof AST_Node) extract_candidates(prop.key);
2486 if (prop instanceof AST_ObjectKeyVal) extract_candidates(prop.value, unused);
2487 hit_stack.pop();
2488 });
2489 } else if (expr instanceof AST_Sequence) {
2490 var end = expr.expressions.length - (unused ? 0 : 1);
2491 expr.expressions.forEach(function(node, index) {
2492 extract_candidates(node, index < end);
2493 });
2494 } else if (expr instanceof AST_SimpleStatement) {
2495 extract_candidates(expr.body, true);
2496 } else if (expr instanceof AST_Spread) {
2497 extract_candidates(expr.expression);
2498 } else if (expr instanceof AST_Sub) {
2499 extract_candidates(expr.expression);
2500 extract_candidates(expr.property);
2501 } else if (expr instanceof AST_Switch) {
2502 extract_candidates(expr.expression);
2503 expr.body.forEach(extract_candidates);
2504 } else if (expr instanceof AST_Unary) {
2505 if (UNARY_POSTFIX[expr.operator]) {
2506 candidates.push(hit_stack.slice());
2507 } else {
2508 extract_candidates(expr.expression);
2509 }
2510 } else if (expr instanceof AST_VarDef) {
2511 if (expr.name instanceof AST_SymbolVar) {
2512 if (expr.value) {
2513 var def = expr.name.definition();
2514 if (def.references.length > def.replaced) {
2515 candidates.push(hit_stack.slice());
2516 }
2517 } else {
2518 declare_only[expr.name.name] = (declare_only[expr.name.name] || 0) + 1;
2519 }
2520 }
2521 if (expr.value) extract_candidates(expr.value);
2522 } else if (expr instanceof AST_Yield) {
2523 if (expr.expression) extract_candidates(expr.expression);
2524 }
2525 hit_stack.pop();
2526 }
2527
2528 function find_stop(node, level) {
2529 var parent = scanner.parent(level);
2530 if (parent instanceof AST_Array) return node;
2531 if (parent instanceof AST_Assign) return node;
2532 if (parent instanceof AST_Await) return node;
2533 if (parent instanceof AST_Binary) return node;
2534 if (parent instanceof AST_Call) return node;
2535 if (parent instanceof AST_Case) return node;
2536 if (parent instanceof AST_Conditional) return node;
2537 if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
2538 if (parent instanceof AST_Exit) return node;
2539 if (parent instanceof AST_If) return node;
2540 if (parent instanceof AST_IterationStatement) return node;
2541 if (parent instanceof AST_ObjectProperty) return node;
2542 if (parent instanceof AST_PropAccess) return node;
2543 if (parent instanceof AST_Sequence) {
2544 return (parent.tail_node() === node ? find_stop : find_stop_unused)(parent, level + 1);
2545 }
2546 if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
2547 if (parent instanceof AST_Spread) return node;
2548 if (parent instanceof AST_Switch) return node;
2549 if (parent instanceof AST_Unary) return node;
2550 if (parent instanceof AST_VarDef) return node;
2551 if (parent instanceof AST_Yield) return node;
2552 return null;
2553 }
2554
2555 function find_stop_logical(parent, op, level) {
2556 var node;
2557 do {
2558 node = parent;
2559 parent = scanner.parent(++level);
2560 } while (parent instanceof AST_Assign && parent.operator.slice(0, -1) == op
2561 || parent instanceof AST_Binary && parent.operator == op);
2562 return node;
2563 }
2564
2565 function find_stop_expr(expr, cont, node, parent, level) {
2566 var replace = can_replace;
2567 can_replace = false;
2568 var after = stop_after;
2569 var if_hit = stop_if_hit;
2570 var stack = scanner.stack;
2571 scanner.stack = [ parent ];
2572 expr.transform(scanner);
2573 scanner.stack = stack;
2574 stop_if_hit = if_hit;
2575 stop_after = after;
2576 can_replace = replace;
2577 if (abort) {
2578 abort = false;
2579 return node;
2580 }
2581 return cont(parent, level + 1);
2582 }
2583
2584 function find_stop_value(node, level) {
2585 var parent = scanner.parent(level);
2586 if (parent instanceof AST_Array) return find_stop_value(parent, level + 1);
2587 if (parent instanceof AST_Assign) {
2588 if (may_throw(parent)) return node;
2589 if (parent.left.match_symbol(function(ref) {
2590 return ref instanceof AST_SymbolRef && (lhs.name == ref.name || value_def.name == ref.name);
2591 })) return node;
2592 var op;
2593 if (parent.left === node || !lazy_op[op = parent.operator.slice(0, -1)]) {
2594 return find_stop_value(parent, level + 1);
2595 }
2596 return find_stop_logical(parent, op, level);
2597 }
2598 if (parent instanceof AST_Binary) {
2599 var op;
2600 if (parent.left === node || !lazy_op[op = parent.operator]) {
2601 return find_stop_value(parent, level + 1);
2602 }
2603 return find_stop_logical(parent, op, level);
2604 }
2605 if (parent instanceof AST_Call) return parent;
2606 if (parent instanceof AST_Case) {
2607 if (parent.expression !== node) return node;
2608 return find_stop_value(parent, level + 1);
2609 }
2610 if (parent instanceof AST_Conditional) {
2611 if (parent.condition !== node) return node;
2612 return find_stop_value(parent, level + 1);
2613 }
2614 if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
2615 if (parent instanceof AST_Do) return node;
2616 if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1);
2617 if (parent instanceof AST_For) {
2618 if (parent.init !== node && parent.condition !== node) return node;
2619 return find_stop_value(parent, level + 1);
2620 }
2621 if (parent instanceof AST_ForEnumeration) {
2622 if (parent.init !== node) return node;
2623 return find_stop_value(parent, level + 1);
2624 }
2625 if (parent instanceof AST_If) {
2626 if (parent.condition !== node) return node;
2627 return find_stop_value(parent, level + 1);
2628 }
2629 if (parent instanceof AST_ObjectProperty) {
2630 var obj = scanner.parent(level + 1);
2631 return all(obj.properties, function(prop) {
2632 return prop instanceof AST_ObjectKeyVal;
2633 }) ? find_stop_value(obj, level + 2) : obj;
2634 }
2635 if (parent instanceof AST_PropAccess) {
2636 var exp = parent.expression;
2637 return exp === node ? find_stop_value(parent, level + 1) : node;
2638 }
2639 if (parent instanceof AST_Sequence) {
2640 return (parent.tail_node() === node ? find_stop_value : find_stop_unused)(parent, level + 1);
2641 }
2642 if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
2643 if (parent instanceof AST_Spread) return find_stop_value(parent, level + 1);
2644 if (parent instanceof AST_Switch) {
2645 if (parent.expression !== node) return node;
2646 return find_stop_value(parent, level + 1);
2647 }
2648 if (parent instanceof AST_Unary) {
2649 if (parent.operator == "delete") return node;
2650 return find_stop_value(parent, level + 1);
2651 }
2652 if (parent instanceof AST_VarDef) return parent.name.match_symbol(function(sym) {
2653 return sym instanceof AST_SymbolDeclaration && (lhs.name == sym.name || value_def.name == sym.name);
2654 }) ? node : find_stop_value(parent, level + 1);
2655 if (parent instanceof AST_While) {
2656 if (parent.condition !== node) return node;
2657 return find_stop_value(parent, level + 1);
2658 }
2659 if (parent instanceof AST_Yield) return find_stop_value(parent, level + 1);
2660 return null;
2661 }
2662
2663 function find_stop_unused(node, level) {
2664 var parent = scanner.parent(level);
2665 if (is_last_node(node, parent)) return node;
2666 if (in_conditional(node, parent)) return node;
2667 if (parent instanceof AST_Array) return find_stop_unused(parent, level + 1);
2668 if (parent instanceof AST_Assign) return check_assignment(parent.left);
2669 if (parent instanceof AST_Await) return node;
2670 if (parent instanceof AST_Binary) return find_stop_unused(parent, level + 1);
2671 if (parent instanceof AST_Call) return find_stop_unused(parent, level + 1);
2672 if (parent instanceof AST_Case) return find_stop_unused(parent, level + 1);
2673 if (parent instanceof AST_Conditional) return find_stop_unused(parent, level + 1);
2674 if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
2675 if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1);
2676 if (parent instanceof AST_If) return find_stop_unused(parent, level + 1);
2677 if (parent instanceof AST_IterationStatement) return node;
2678 if (parent instanceof AST_ObjectProperty) {
2679 var obj = scanner.parent(level + 1);
2680 return all(obj.properties, function(prop) {
2681 return prop instanceof AST_ObjectKeyVal;
2682 }) ? find_stop_unused(obj, level + 2) : obj;
2683 }
2684 if (parent instanceof AST_PropAccess) {
2685 var exp = parent.expression;
2686 if (exp === node) return find_stop_unused(parent, level + 1);
2687 return find_stop_expr(exp, find_stop_unused, node, parent, level);
2688 }
2689 if (parent instanceof AST_Sequence) return find_stop_unused(parent, level + 1);
2690 if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
2691 if (parent instanceof AST_Spread) return node;
2692 if (parent instanceof AST_Switch) return find_stop_unused(parent, level + 1);
2693 if (parent instanceof AST_Unary) return find_stop_unused(parent, level + 1);
2694 if (parent instanceof AST_VarDef) return check_assignment(parent.name);
2695 if (parent instanceof AST_Yield) return node;
2696 return null;
2697
2698 function check_assignment(lhs) {
2699 if (may_throw(parent)) return node;
2700 if (lhs !== node && lhs instanceof AST_Destructured) {
2701 return find_stop_expr(lhs, find_stop_unused, node, parent, level);
2702 }
2703 return find_stop_unused(parent, level + 1);
2704 }
2705 }
2706
2707 function mangleable_var(rhs) {
2708 if (force_single) {
2709 force_single = false;
2710 return;
2711 }
2712 if (remaining < 1) return;
2713 var value = rhs instanceof AST_Assign && rhs.operator == "=" ? rhs.left : rhs;
2714 if (!(value instanceof AST_SymbolRef)) return;
2715 var def = value.definition();
2716 if (def.undeclared) return;
2717 if (is_arguments(def)) return;
2718 if (value !== rhs) {
2719 if (is_lhs_read_only(value, compressor)) return;
2720 var referenced = def.references.length - def.replaced;
2721 if (referenced < 2) return;
2722 var expr = candidate.clone();
2723 expr[expr instanceof AST_Assign ? "right" : "value"] = value;
2724 if (candidate.name_index >= 0) {
2725 expr.name_index = candidate.name_index;
2726 expr.arg_index = candidate.arg_index;
2727 }
2728 candidate = expr;
2729 }
2730 return value_def = def;
2731 }
2732
2733 function remaining_refs(def) {
2734 return def.references.length - def.replaced - (assignments[def.name] || 0);
2735 }
2736
2737 function get_lhs(expr) {
2738 if (expr instanceof AST_Assign) {
2739 var lhs = expr.left;
2740 if (expr.operator != "=") return lhs;
2741 if (!(lhs instanceof AST_SymbolRef)) return lhs;
2742 var def = lhs.definition();
2743 if (scope.uses_arguments && is_funarg(def)) return lhs;
2744 if (compressor.exposed(def)) return lhs;
2745 remaining = remaining_refs(def);
2746 if (def.fixed && lhs.fixed) {
2747 var matches = def.references.filter(function(ref) {
2748 return ref.fixed === lhs.fixed;
2749 }).length - 1;
2750 if (matches < remaining) {
2751 remaining = matches;
2752 assign_pos = 0;
2753 }
2754 }
2755 mangleable_var(expr.right);
2756 return lhs;
2757 }
2758 if (expr instanceof AST_Binary) return expr.right.left;
2759 if (expr instanceof AST_Unary) return expr.expression;
2760 if (expr instanceof AST_VarDef) {
2761 var lhs = expr.name;
2762 var def = lhs.definition();
2763 if (def.const_redefs) return;
2764 if (!member(lhs, def.orig)) return;
2765 if (scope.uses_arguments && is_funarg(def)) return;
2766 var declared = def.orig.length - def.eliminated - (declare_only[def.name] || 0);
2767 remaining = remaining_refs(def);
2768 if (def.fixed) remaining = Math.min(remaining, def.references.filter(function(ref) {
2769 if (!ref.fixed) return true;
2770 if (!ref.fixed.assigns) return true;
2771 var assign = ref.fixed.assigns[0];
2772 return assign === lhs || get_rvalue(assign) === expr.value;
2773 }).length);
2774 if (declared > 1 && !(lhs instanceof AST_SymbolFunarg)) {
2775 mangleable_var(expr.value);
2776 return make_node(AST_SymbolRef, lhs, lhs);
2777 }
2778 if (mangleable_var(expr.value) || remaining == 1 && !compressor.exposed(def)) {
2779 return make_node(AST_SymbolRef, lhs, lhs);
2780 }
2781 return;
2782 }
2783 }
2784
2785 function get_rvalue(expr) {
2786 if (expr instanceof AST_Assign) return expr.right;
2787 if (expr instanceof AST_Binary) {
2788 var node = expr.clone();
2789 node.right = expr.right.right;
2790 return node;
2791 }
2792 if (expr instanceof AST_VarDef) return expr.value;
2793 }
2794
2795 function invariant(expr) {
2796 if (expr instanceof AST_Array) return false;
2797 if (expr instanceof AST_Binary && lazy_op[expr.operator]) {
2798 return invariant(expr.left) && invariant(expr.right);
2799 }
2800 if (expr instanceof AST_Call) return false;
2801 if (expr instanceof AST_Conditional) {
2802 return invariant(expr.consequent) && invariant(expr.alternative);
2803 }
2804 if (expr instanceof AST_Object) return false;
2805 return !expr.has_side_effects(compressor);
2806 }
2807
2808 function foldable(expr) {
2809 if (expr instanceof AST_Assign && expr.right.single_use) return;
2810 var lhs_ids = Object.create(null);
2811 var marker = new TreeWalker(function(node) {
2812 if (node instanceof AST_SymbolRef) lhs_ids[node.definition().id] = true;
2813 });
2814 while (expr instanceof AST_Assign && expr.operator == "=") {
2815 expr.left.walk(marker);
2816 expr = expr.right;
2817 }
2818 if (expr instanceof AST_ObjectIdentity) return rhs_exact_match;
2819 if (expr instanceof AST_SymbolRef) {
2820 var value = expr.evaluate(compressor);
2821 if (value === expr) return rhs_exact_match;
2822 return rhs_fuzzy_match(value, rhs_exact_match);
2823 }
2824 if (expr.is_truthy()) return rhs_fuzzy_match(true, return_false);
2825 if (expr.is_constant()) {
2826 var ev = expr.evaluate(compressor);
2827 if (!(ev instanceof AST_Node)) return rhs_fuzzy_match(ev, rhs_exact_match);
2828 }
2829 if (!(lhs instanceof AST_SymbolRef)) return false;
2830 if (!invariant(expr)) return false;
2831 var circular;
2832 expr.walk(new TreeWalker(function(node) {
2833 if (circular) return true;
2834 if (node instanceof AST_SymbolRef && lhs_ids[node.definition().id]) circular = true;
2835 }));
2836 return !circular && rhs_exact_match;
2837
2838 function rhs_exact_match(node) {
2839 return expr.equivalent_to(node);
2840 }
2841 }
2842
2843 function rhs_fuzzy_match(value, fallback) {
2844 return function(node, tw) {
2845 if (tw.in_boolean_context()) {
2846 if (value && node.is_truthy() && !node.has_side_effects(compressor)) {
2847 return true;
2848 }
2849 if (node.is_constant()) {
2850 var ev = node.evaluate(compressor);
2851 if (!(ev instanceof AST_Node)) return !ev == !value;
2852 }
2853 }
2854 return fallback(node);
2855 };
2856 }
2857
2858 function may_be_global(node) {
2859 if (node instanceof AST_SymbolRef) {
2860 node = node.fixed_value();
2861 if (!node) return true;
2862 }
2863 if (node instanceof AST_Assign) return node.operator == "=" && may_be_global(node.right);
2864 return node instanceof AST_PropAccess || node instanceof AST_ObjectIdentity;
2865 }
2866
2867 function get_lvalues(expr) {
2868 var lvalues = new Dictionary();
2869 if (expr instanceof AST_VarDef) {
2870 if (!expr.name.definition().fixed) well_defined = false;
2871 lvalues.add(expr.name.name, lhs);
2872 }
2873 var find_arguments = scope.uses_arguments && !compressor.has_directive("use strict");
2874 var scan_toplevel = scope instanceof AST_Toplevel;
2875 var tw = new TreeWalker(function(node) {
2876 var value;
2877 if (node instanceof AST_SymbolRef) {
2878 value = node.fixed_value();
2879 if (!value) {
2880 value = node;
2881 var def = node.definition();
2882 if (!def.undeclared
2883 && (def.assignments || !def.escaped || def.escaped.cross_scope)
2884 && (has_escaped(def, node.scope, node, tw.parent()) || !same_scope(def))) {
2885 well_defined = false;
2886 }
2887 }
2888 } else if (node instanceof AST_ObjectIdentity) {
2889 value = node;
2890 }
2891 if (value) lvalues.add(node.name, is_modified(compressor, tw, node, value, 0));
2892 if (find_arguments && node instanceof AST_Sub) {
2893 scope.each_argname(function(argname) {
2894 if (!compressor.option("reduce_vars") || argname.definition().assignments) {
2895 if (!argname.definition().fixed) well_defined = false;
2896 lvalues.add(argname.name, true);
2897 }
2898 });
2899 find_arguments = false;
2900 }
2901 if (!scan_toplevel) return;
2902 if (node.TYPE == "Call") {
2903 if (modify_toplevel) return;
2904 var exp = node.expression;
2905 if (exp instanceof AST_PropAccess) return;
2906 if (exp instanceof AST_LambdaExpression && !exp.contains_this()) return;
2907 modify_toplevel = true;
2908 } else if (node instanceof AST_PropAccess && may_be_global(node.expression)) {
2909 if (node === lhs && !(expr instanceof AST_Unary)) {
2910 modify_toplevel = true;
2911 } else {
2912 read_toplevel = true;
2913 }
2914 }
2915 });
2916 expr.walk(tw);
2917 return lvalues;
2918 }
2919
2920 function remove_candidate(expr) {
2921 var index = expr.name_index;
2922 if (index >= 0) {
2923 var argname = scope.argnames[index];
2924 if (argname instanceof AST_DefaultValue) {
2925 argname.value = make_node(AST_Number, argname, {
2926 value: 0
2927 });
2928 argname.name.definition().fixed = false;
2929 } else {
2930 var args = compressor.parent().args;
2931 if (args[index]) {
2932 args[index] = make_node(AST_Number, args[index], {
2933 value: 0
2934 });
2935 argname.definition().fixed = false;
2936 }
2937 }
2938 return true;
2939 }
2940 var end = hit_stack.length - 1;
2941 if (hit_stack[end - 1].body === hit_stack[end]) end--;
2942 var tt = new TreeTransformer(function(node, descend, in_list) {
2943 if (hit) return node;
2944 if (node !== hit_stack[hit_index]) return node;
2945 hit_index++;
2946 if (hit_index <= end) return handle_custom_scan_order(node, tt);
2947 hit = true;
2948 if (node instanceof AST_VarDef) {
2949 declare_only[node.name.name] = (declare_only[node.name.name] || 0) + 1;
2950 if (value_def) value_def.replaced++;
2951 node = node.clone();
2952 node.value = null;
2953 return node;
2954 }
2955 return in_list ? List.skip : null;
2956 }, patch_sequence);
2957 abort = false;
2958 hit = false;
2959 hit_index = 0;
2960 return statements[stat_index].transform(tt);
2961 }
2962
2963 function patch_sequence(node) {
2964 if (node instanceof AST_Sequence) switch (node.expressions.length) {
2965 case 0: return null;
2966 case 1: return maintain_this_binding(compressor, this.parent(), node, node.expressions[0]);
2967 }
2968 }
2969
2970 function is_lhs_local(lhs) {
2971 var sym = root_expr(lhs);
2972 return sym instanceof AST_SymbolRef
2973 && sym.definition().scope.resolve() === scope
2974 && !(in_loop
2975 && (lvalues.has(sym.name) && lvalues.get(sym.name)[0] !== lhs
2976 || candidate instanceof AST_Unary
2977 || candidate instanceof AST_Assign && candidate.operator != "="));
2978 }
2979
2980 function value_has_side_effects() {
2981 if (candidate instanceof AST_Unary) return false;
2982 return rvalue.has_side_effects(compressor);
2983 }
2984
2985 function replace_all_symbols(expr) {
2986 if (expr instanceof AST_Unary) return false;
2987 if (side_effects) return false;
2988 if (value_def) return true;
2989 if (!(lhs instanceof AST_SymbolRef)) return false;
2990 var referenced;
2991 if (expr instanceof AST_VarDef) {
2992 referenced = 1;
2993 } else if (expr.operator == "=") {
2994 referenced = 2;
2995 } else {
2996 return false;
2997 }
2998 var def = lhs.definition();
2999 return def.references.length - def.replaced == referenced;
3000 }
3001
3002 function symbol_in_lvalues(sym, parent) {
3003 var lvalue = lvalues.get(sym.name);
3004 if (!lvalue || all(lvalue, function(lhs) {
3005 return !lhs;
3006 })) return;
3007 if (lvalue[0] !== lhs) return true;
3008 scan_rhs = false;
3009 }
3010
3011 function may_modify(sym) {
3012 var def = sym.definition();
3013 if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return false;
3014 if (def.scope.resolve() !== scope) return true;
3015 if (modify_toplevel && compressor.exposed(def)) return true;
3016 return !all(def.references, function(ref) {
3017 return ref.scope.resolve() === scope;
3018 });
3019 }
3020
3021 function side_effects_external(node, lhs) {
3022 if (node instanceof AST_Assign) return side_effects_external(node.left, true);
3023 if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
3024 if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
3025 if (lhs) {
3026 if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
3027 if (node instanceof AST_Sub) return side_effects_external(node.expression, true);
3028 if (node instanceof AST_SymbolRef) return node.definition().scope.resolve() !== scope;
3029 }
3030 return false;
3031 }
3032 }
3033
3034 function eliminate_spurious_blocks(statements) {
3035 var seen_dirs = [];
3036 for (var i = 0; i < statements.length;) {
3037 var stat = statements[i];
3038 if (stat instanceof AST_BlockStatement) {
3039 if (all(stat.body, safe_to_trim)) {
3040 CHANGED = true;
3041 eliminate_spurious_blocks(stat.body);
3042 [].splice.apply(statements, [i, 1].concat(stat.body));
3043 i += stat.body.length;
3044 continue;
3045 }
3046 }
3047 if (stat instanceof AST_Directive) {
3048 if (member(stat.value, seen_dirs)) {
3049 CHANGED = true;
3050 statements.splice(i, 1);
3051 continue;
3052 }
3053 seen_dirs.push(stat.value);
3054 }
3055 if (stat instanceof AST_EmptyStatement) {
3056 CHANGED = true;
3057 statements.splice(i, 1);
3058 continue;
3059 }
3060 i++;
3061 }
3062 }
3063
3064 function handle_if_return(statements, compressor) {
3065 var self = compressor.self();
3066 var parent = compressor.parent();
3067 var in_lambda = last_of(function(node) {
3068 return node instanceof AST_Lambda;
3069 });
3070 var in_iife = in_lambda && parent && parent.TYPE == "Call";
3071 var multiple_if_returns = has_multiple_if_returns(statements);
3072 for (var i = statements.length; --i >= 0;) {
3073 var stat = statements[i];
3074 var j = next_index(i);
3075 var next = statements[j];
3076
3077 if (in_lambda && !next && stat instanceof AST_Return) {
3078 if (!stat.value) {
3079 CHANGED = true;
3080 statements.splice(i, 1);
3081 continue;
3082 }
3083 var tail = stat.value.tail_node();
3084 if (tail instanceof AST_UnaryPrefix && tail.operator == "void") {
3085 CHANGED = true;
3086 var body;
3087 if (tail === stat.value) {
3088 body = tail.expression;
3089 } else {
3090 body = stat.value.clone();
3091 body.expressions[body.length - 1] = tail.expression;
3092 }
3093 statements[i] = make_node(AST_SimpleStatement, stat, {
3094 body: body,
3095 });
3096 continue;
3097 }
3098 }
3099
3100 if (stat instanceof AST_If) {
3101 var ab = aborts(stat.body);
3102 if (can_merge_flow(ab)) {
3103 if (ab.label) remove(ab.label.thedef.references, ab);
3104 CHANGED = true;
3105 stat = stat.clone();
3106 stat.condition = stat.condition.negate(compressor);
3107 var body = as_statement_array_with_return(stat.body, ab);
3108 stat.body = make_node(AST_BlockStatement, stat, {
3109 body: as_statement_array(stat.alternative).concat(extract_functions())
3110 });
3111 stat.alternative = make_node(AST_BlockStatement, stat, {
3112 body: body
3113 });
3114 statements[i] = stat;
3115 statements[i] = stat.transform(compressor);
3116 continue;
3117 }
3118
3119 if (ab && !stat.alternative && stat.body instanceof AST_BlockStatement && next instanceof AST_Jump) {
3120 var negated = stat.condition.negate(compressor);
3121 if (negated.print_to_string().length <= stat.condition.print_to_string().length) {
3122 CHANGED = true;
3123 stat = stat.clone();
3124 stat.condition = negated;
3125 statements[j] = stat.body;
3126 stat.body = next;
3127 statements[i] = stat;
3128 statements[i] = stat.transform(compressor);
3129 continue;
3130 }
3131 }
3132
3133 var alt = aborts(stat.alternative);
3134 if (can_merge_flow(alt)) {
3135 if (alt.label) remove(alt.label.thedef.references, alt);
3136 CHANGED = true;
3137 stat = stat.clone();
3138 stat.body = make_node(AST_BlockStatement, stat.body, {
3139 body: as_statement_array(stat.body).concat(extract_functions())
3140 });
3141 var body = as_statement_array_with_return(stat.alternative, alt);
3142 stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
3143 body: body
3144 });
3145 statements[i] = stat;
3146 statements[i] = stat.transform(compressor);
3147 continue;
3148 }
3149
3150 if (compressor.option("typeofs")) {
3151 if (ab && !alt) {
3152 mark_locally_defined(stat.condition, null, make_node(AST_BlockStatement, self, {
3153 body: statements.slice(i + 1)
3154 }));
3155 }
3156 if (!ab && alt) {
3157 mark_locally_defined(stat.condition, make_node(AST_BlockStatement, self, {
3158 body: statements.slice(i + 1)
3159 }));
3160 }
3161 }
3162 }
3163
3164 if (stat instanceof AST_If && stat.body instanceof AST_Return) {
3165 var value = stat.body.value;
3166 var in_bool = stat.body.in_bool || next instanceof AST_Return && next.in_bool;
3167 //---
3168 // pretty silly case, but:
3169 // if (foo()) return; return; ---> foo(); return;
3170 if (!value && !stat.alternative
3171 && (in_lambda && !next || next instanceof AST_Return && !next.value)) {
3172 CHANGED = true;
3173 statements[i] = make_node(AST_SimpleStatement, stat.condition, {
3174 body: stat.condition
3175 });
3176 continue;
3177 }
3178 //---
3179 // if (foo()) return x; return y; ---> return foo() ? x : y;
3180 if (!stat.alternative && next instanceof AST_Return) {
3181 CHANGED = true;
3182 stat = stat.clone();
3183 stat.alternative = next;
3184 statements.splice(i, 1, stat.transform(compressor));
3185 statements.splice(j, 1);
3186 continue;
3187 }
3188 //---
3189 // if (foo()) return x; [ return ; ] ---> return foo() ? x : undefined;
3190 if (!stat.alternative && !next && in_lambda && (in_bool || value && multiple_if_returns)) {
3191 CHANGED = true;
3192 stat = stat.clone();
3193 stat.alternative = make_node(AST_Return, stat, {
3194 value: null
3195 });
3196 statements.splice(i, 1, stat.transform(compressor));
3197 continue;
3198 }
3199 //---
3200 // if (a) return b; if (c) return d; e; ---> return a ? b : c ? d : void e;
3201 //
3202 // if sequences is not enabled, this can lead to an endless loop (issue #866).
3203 // however, with sequences on this helps producing slightly better output for
3204 // the example code.
3205 var prev = statements[prev_index(i)];
3206 if (compressor.option("sequences") && in_lambda && !stat.alternative
3207 && (!prev && in_iife || prev instanceof AST_If && prev.body instanceof AST_Return)
3208 && next_index(j) == statements.length && next instanceof AST_SimpleStatement) {
3209 CHANGED = true;
3210 stat = stat.clone();
3211 stat.alternative = make_node(AST_BlockStatement, next, {
3212 body: [
3213 next,
3214 make_node(AST_Return, next, {
3215 value: null
3216 })
3217 ]
3218 });
3219 statements.splice(i, 1, stat.transform(compressor));
3220 statements.splice(j, 1);
3221 continue;
3222 }
3223 }
3224 }
3225
3226 function has_multiple_if_returns(statements) {
3227 var n = 0;
3228 for (var i = statements.length; --i >= 0;) {
3229 var stat = statements[i];
3230 if (stat instanceof AST_If && stat.body instanceof AST_Return) {
3231 if (++n > 1) return true;
3232 }
3233 }
3234 return false;
3235 }
3236
3237 function is_return_void(value) {
3238 return !value || value instanceof AST_UnaryPrefix && value.operator == "void";
3239 }
3240
3241 function last_of(predicate) {
3242 var block = self, stat, level = 0;
3243 do {
3244 do {
3245 if (predicate(block)) return true;
3246 block = compressor.parent(level++);
3247 } while (block instanceof AST_If && (stat = block));
3248 } while ((block instanceof AST_BlockStatement || block instanceof AST_Scope)
3249 && is_last_statement(block.body, stat));
3250 }
3251
3252 function match_target(target) {
3253 return last_of(function(node) {
3254 return node === target;
3255 });
3256 }
3257
3258 function can_drop_abort(ab) {
3259 if (ab instanceof AST_Return) return in_lambda && is_return_void(ab.value);
3260 if (!(ab instanceof AST_LoopControl)) return false;
3261 var lct = compressor.loopcontrol_target(ab);
3262 if (ab instanceof AST_Continue) return match_target(loop_body(lct));
3263 if (lct instanceof AST_IterationStatement) return false;
3264 return match_target(lct);
3265 }
3266
3267 function can_merge_flow(ab) {
3268 if (!can_drop_abort(ab)) return false;
3269 for (var j = statements.length; --j > i;) {
3270 var stat = statements[j];
3271 if (stat instanceof AST_DefClass) {
3272 if (stat.name.definition().preinit) return false;
3273 } else if (stat instanceof AST_Const || stat instanceof AST_Let) {
3274 if (!all(stat.definitions, function(defn) {
3275 return !defn.name.match_symbol(function(node) {
3276 return node instanceof AST_SymbolDeclaration && node.definition().preinit;
3277 });
3278 })) return false;
3279 }
3280 }
3281 return true;
3282 }
3283
3284 function extract_functions() {
3285 var defuns = [];
3286 var lexical = false;
3287 var tail = statements.splice(i + 1).filter(function(stat) {
3288 if (stat instanceof AST_LambdaDefinition) {
3289 defuns.push(stat);
3290 return false;
3291 }
3292 if (is_lexical_definition(stat)) lexical = true;
3293 return true;
3294 });
3295 [].push.apply(lexical ? tail : statements, defuns);
3296 return tail;
3297 }
3298
3299 function as_statement_array_with_return(node, ab) {
3300 var body = as_statement_array(node);
3301 var block = body, last;
3302 while ((last = block[block.length - 1]) !== ab) {
3303 block = last.body;
3304 }
3305 block.pop();
3306 if (ab.value) block.push(make_node(AST_SimpleStatement, ab.value, {
3307 body: ab.value.expression
3308 }));
3309 return body;
3310 }
3311
3312 function next_index(i) {
3313 for (var j = i + 1; j < statements.length; j++) {
3314 if (!is_declaration(statements[j])) break;
3315 }
3316 return j;
3317 }
3318
3319 function prev_index(i) {
3320 for (var j = i; --j >= 0;) {
3321 if (!is_declaration(statements[j])) break;
3322 }
3323 return j;
3324 }
3325 }
3326
3327 function eliminate_dead_code(statements, compressor) {
3328 var has_quit;
3329 var self = compressor.self();
3330 for (var i = 0, n = 0, len = statements.length; i < len; i++) {
3331 var stat = statements[i];
3332 if (stat instanceof AST_LoopControl) {
3333 var lct = compressor.loopcontrol_target(stat);
3334 if (loop_body(lct) !== self
3335 || stat instanceof AST_Break && lct instanceof AST_IterationStatement) {
3336 statements[n++] = stat;
3337 } else if (stat.label) {
3338 remove(stat.label.thedef.references, stat);
3339 }
3340 } else {
3341 statements[n++] = stat;
3342 }
3343 if (aborts(stat)) {
3344 has_quit = statements.slice(i + 1);
3345 break;
3346 }
3347 }
3348 statements.length = n;
3349 CHANGED = n != len;
3350 if (has_quit) has_quit.forEach(function(stat) {
3351 extract_declarations_from_unreachable_code(compressor, stat, statements);
3352 });
3353 }
3354
3355 function sequencesize(statements, compressor) {
3356 if (statements.length < 2) return;
3357 var seq = [], n = 0;
3358 function push_seq() {
3359 if (!seq.length) return;
3360 var body = make_sequence(seq[0], seq);
3361 statements[n++] = make_node(AST_SimpleStatement, body, { body: body });
3362 seq = [];
3363 }
3364 for (var i = 0, len = statements.length; i < len; i++) {
3365 var stat = statements[i];
3366 if (stat instanceof AST_SimpleStatement) {
3367 if (seq.length >= compressor.sequences_limit) push_seq();
3368 var body = stat.body;
3369 if (seq.length > 0) body = body.drop_side_effect_free(compressor);
3370 if (body) merge_sequence(seq, body);
3371 } else if (is_declaration(stat)) {
3372 statements[n++] = stat;
3373 } else {
3374 push_seq();
3375 statements[n++] = stat;
3376 }
3377 }
3378 push_seq();
3379 statements.length = n;
3380 if (n != len) CHANGED = true;
3381 }
3382
3383 function to_simple_statement(block, decls) {
3384 if (!(block instanceof AST_BlockStatement)) return block;
3385 var stat = null;
3386 for (var i = 0; i < block.body.length; i++) {
3387 var line = block.body[i];
3388 if (line instanceof AST_Var && declarations_only(line)) {
3389 decls.push(line);
3390 } else if (stat || is_lexical_definition(line)) {
3391 return false;
3392 } else {
3393 stat = line;
3394 }
3395 }
3396 return stat;
3397 }
3398
3399 function sequencesize_2(statements, compressor) {
3400 function cons_seq(right) {
3401 n--;
3402 CHANGED = true;
3403 var left = prev.body;
3404 return make_sequence(left, [ left, right ]);
3405 }
3406 var n = 0, prev;
3407 for (var i = 0; i < statements.length; i++) {
3408 var stat = statements[i];
3409 if (prev) {
3410 if (stat instanceof AST_Exit) {
3411 if (stat.value || !in_async_generator(scope)) {
3412 stat.value = cons_seq(stat.value || make_node(AST_Undefined, stat)).optimize(compressor);
3413 }
3414 } else if (stat instanceof AST_For) {
3415 if (!(stat.init instanceof AST_Definitions)) {
3416 var abort = false;
3417 prev.body.walk(new TreeWalker(function(node) {
3418 if (abort || node instanceof AST_Scope) return true;
3419 if (node instanceof AST_Binary && node.operator == "in") {
3420 abort = true;
3421 return true;
3422 }
3423 }));
3424 if (!abort) {
3425 if (stat.init) stat.init = cons_seq(stat.init);
3426 else {
3427 stat.init = prev.body;
3428 n--;
3429 CHANGED = true;
3430 }
3431 }
3432 }
3433 } else if (stat instanceof AST_ForIn) {
3434 if (!is_lexical_definition(stat.init)) stat.object = cons_seq(stat.object);
3435 } else if (stat instanceof AST_If) {
3436 stat.condition = cons_seq(stat.condition);
3437 } else if (stat instanceof AST_Switch) {
3438 stat.expression = cons_seq(stat.expression);
3439 } else if (stat instanceof AST_With) {
3440 stat.expression = cons_seq(stat.expression);
3441 }
3442 }
3443 if (compressor.option("conditionals") && stat instanceof AST_If) {
3444 var decls = [];
3445 var body = to_simple_statement(stat.body, decls);
3446 var alt = to_simple_statement(stat.alternative, decls);
3447 if (body !== false && alt !== false && decls.length > 0) {
3448 var len = decls.length;
3449 decls.push(make_node(AST_If, stat, {
3450 condition: stat.condition,
3451 body: body || make_node(AST_EmptyStatement, stat.body),
3452 alternative: alt
3453 }));
3454 decls.unshift(n, 1);
3455 [].splice.apply(statements, decls);
3456 i += len;
3457 n += len + 1;
3458 prev = null;
3459 CHANGED = true;
3460 continue;
3461 }
3462 }
3463 statements[n++] = stat;
3464 prev = stat instanceof AST_SimpleStatement ? stat : null;
3465 }
3466 statements.length = n;
3467 }
3468
3469 function extract_exprs(body) {
3470 if (body instanceof AST_Assign) return [ body ];
3471 if (body instanceof AST_Sequence) return body.expressions.slice();
3472 }
3473
3474 function join_assigns(defn, body, keep) {
3475 var exprs = extract_exprs(body);
3476 if (!exprs) return;
3477 var trimmed = false;
3478 for (var i = exprs.length - 1; --i >= 0;) {
3479 var expr = exprs[i];
3480 if (!(expr instanceof AST_Assign)) continue;
3481 if (expr.operator != "=") continue;
3482 if (!(expr.left instanceof AST_SymbolRef)) continue;
3483 var tail = exprs.slice(i + 1);
3484 if (!trim_assigns(expr.left, expr.right, tail)) continue;
3485 trimmed = true;
3486 exprs = exprs.slice(0, i + 1).concat(tail);
3487 }
3488 if (defn instanceof AST_Definitions) {
3489 keep = keep || 0;
3490 for (var i = defn.definitions.length; --i >= 0;) {
3491 var def = defn.definitions[i];
3492 if (!def.value) continue;
3493 if (trim_assigns(def.name, def.value, exprs)) trimmed = true;
3494 if (merge_conditional_assignments(def, exprs, keep)) trimmed = true;
3495 break;
3496 }
3497 if (defn instanceof AST_Var && join_var_assign(defn.definitions, exprs, keep)) trimmed = true;
3498 }
3499 return trimmed && exprs;
3500 }
3501
3502 function merge_assigns(prev, defn) {
3503 if (!(prev instanceof AST_SimpleStatement)) return;
3504 if (declarations_only(defn)) return;
3505 var exprs = extract_exprs(prev.body);
3506 if (!exprs) return;
3507 var definitions = [];
3508 if (!join_var_assign(definitions, exprs.reverse(), 0)) return;
3509 defn.definitions = definitions.reverse().concat(defn.definitions);
3510 return exprs.reverse();
3511 }
3512
3513 function merge_conditional_assignments(var_def, exprs, keep) {
3514 if (!compressor.option("conditionals")) return;
3515 if (var_def.name instanceof AST_Destructured) return;
3516 var trimmed = false;
3517 var def = var_def.name.definition();
3518 while (exprs.length > keep) {
3519 var cond = to_conditional_assignment(compressor, def, var_def.value, exprs[0]);
3520 if (!cond) break;
3521 var_def.value = cond;
3522 exprs.shift();
3523 trimmed = true;
3524 }
3525 return trimmed;
3526 }
3527
3528 function join_var_assign(definitions, exprs, keep) {
3529 var trimmed = false;
3530 while (exprs.length > keep) {
3531 var expr = exprs[0];
3532 if (!(expr instanceof AST_Assign)) break;
3533 if (expr.operator != "=") break;
3534 var lhs = expr.left;
3535 if (!(lhs instanceof AST_SymbolRef)) break;
3536 if (is_undeclared_ref(lhs)) break;
3537 if (lhs.scope.resolve() !== scope) break;
3538 var def = lhs.definition();
3539 if (def.scope !== scope) break;
3540 if (def.orig.length > def.eliminated + 1) break;
3541 if (def.orig[0].TYPE != "SymbolVar") break;
3542 var name = make_node(AST_SymbolVar, lhs, lhs);
3543 definitions.push(make_node(AST_VarDef, expr, {
3544 name: name,
3545 value: expr.right
3546 }));
3547 def.orig.push(name);
3548 def.replaced++;
3549 exprs.shift();
3550 trimmed = true;
3551 }
3552 return trimmed;
3553 }
3554
3555 function trim_assigns(name, value, exprs) {
3556 if (!(value instanceof AST_Object)) return;
3557 var trimmed = false;
3558 do {
3559 var node = exprs[0];
3560 if (!(node instanceof AST_Assign)) break;
3561 if (node.operator != "=") break;
3562 if (!(node.left instanceof AST_PropAccess)) break;
3563 var sym = node.left.expression;
3564 if (!(sym instanceof AST_SymbolRef)) break;
3565 if (name.name != sym.name) break;
3566 if (!node.right.is_constant_expression(scope)) break;
3567 var prop = node.left.property;
3568 if (prop instanceof AST_Node) {
3569 prop = prop.evaluate(compressor);
3570 }
3571 if (prop instanceof AST_Node) break;
3572 prop = "" + prop;
3573 var diff = prop == "__proto__" || compressor.has_directive("use strict") ? function(node) {
3574 var key = node.key;
3575 return typeof key == "string" && key != prop && key != "__proto__";
3576 } : function(node) {
3577 var key = node.key;
3578 if (node instanceof AST_ObjectGetter || node instanceof AST_ObjectSetter) {
3579 return typeof key == "string" && key != prop;
3580 }
3581 return key !== "__proto__";
3582 };
3583 if (!all(value.properties, diff)) break;
3584 value.properties.push(make_node(AST_ObjectKeyVal, node, {
3585 key: prop,
3586 value: node.right
3587 }));
3588 exprs.shift();
3589 trimmed = true;
3590 } while (exprs.length);
3591 return trimmed;
3592 }
3593
3594 function join_consecutive_vars(statements) {
3595 var defs;
3596 for (var i = 0, j = -1; i < statements.length; i++) {
3597 var stat = statements[i];
3598 var prev = statements[j];
3599 if (stat instanceof AST_Definitions) {
3600 if (prev && prev.TYPE == stat.TYPE) {
3601 prev.definitions = prev.definitions.concat(stat.definitions);
3602 CHANGED = true;
3603 } else if (defs && defs.TYPE == stat.TYPE && declarations_only(stat)) {
3604 defs.definitions = defs.definitions.concat(stat.definitions);
3605 CHANGED = true;
3606 } else if (stat instanceof AST_Var) {
3607 var exprs = merge_assigns(prev, stat);
3608 if (exprs) {
3609 if (exprs.length) {
3610 prev.body = make_sequence(prev, exprs);
3611 j++;
3612 }
3613 CHANGED = true;
3614 } else {
3615 j++;
3616 }
3617 statements[j] = defs = stat;
3618 } else {
3619 statements[++j] = stat;
3620 }
3621 continue;
3622 } else if (stat instanceof AST_Exit) {
3623 stat.value = join_assigns_expr(stat.value);
3624 } else if (stat instanceof AST_For) {
3625 var exprs = join_assigns(prev, stat.init);
3626 if (exprs) {
3627 CHANGED = true;
3628 stat.init = exprs.length ? make_sequence(stat.init, exprs) : null;
3629 } else if (prev instanceof AST_Var && (!stat.init || stat.init.TYPE == prev.TYPE)) {
3630 if (stat.init) {
3631 prev.definitions = prev.definitions.concat(stat.init.definitions);
3632 }
3633 defs = stat.init = prev;
3634 statements[j] = merge_defns(stat);
3635 CHANGED = true;
3636 continue;
3637 } else if (defs && stat.init && defs.TYPE == stat.init.TYPE && declarations_only(stat.init)) {
3638 defs.definitions = defs.definitions.concat(stat.init.definitions);
3639 stat.init = null;
3640 CHANGED = true;
3641 } else if (stat.init instanceof AST_Var) {
3642 defs = stat.init;
3643 exprs = merge_assigns(prev, stat.init);
3644 if (exprs) {
3645 CHANGED = true;
3646 if (exprs.length == 0) {
3647 statements[j] = merge_defns(stat);
3648 continue;
3649 }
3650 prev.body = make_sequence(prev, exprs);
3651 }
3652 }
3653 } else if (stat instanceof AST_ForEnumeration) {
3654 if (defs && defs.TYPE == stat.init.TYPE) {
3655 var defns = defs.definitions.slice();
3656 stat.init = stat.init.definitions[0].name.convert_symbol(AST_SymbolRef, function(ref, name) {
3657 defns.push(make_node(AST_VarDef, name, {
3658 name: name,
3659 value: null,
3660 }));
3661 name.definition().references.push(ref);
3662 });
3663 defs.definitions = defns;
3664 CHANGED = true;
3665 }
3666 stat.object = join_assigns_expr(stat.object);
3667 } else if (stat instanceof AST_If) {
3668 stat.condition = join_assigns_expr(stat.condition);
3669 } else if (stat instanceof AST_SimpleStatement) {
3670 var exprs = join_assigns(prev, stat.body);
3671 if (exprs) {
3672 CHANGED = true;
3673 if (!exprs.length) continue;
3674 stat.body = make_sequence(stat.body, exprs);
3675 }
3676 } else if (stat instanceof AST_Switch) {
3677 stat.expression = join_assigns_expr(stat.expression);
3678 } else if (stat instanceof AST_With) {
3679 stat.expression = join_assigns_expr(stat.expression);
3680 }
3681 statements[++j] = defs ? merge_defns(stat) : stat;
3682 }
3683 statements.length = j + 1;
3684
3685 function join_assigns_expr(value) {
3686 var exprs = join_assigns(prev, value, 1);
3687 if (!exprs) return value;
3688 CHANGED = true;
3689 var tail = value.tail_node();
3690 if (exprs[exprs.length - 1] !== tail) exprs.push(tail.left);
3691 return make_sequence(value, exprs);
3692 }
3693
3694 function merge_defns(stat) {
3695 return stat.transform(new TreeTransformer(function(node, descend, in_list) {
3696 if (node instanceof AST_Definitions) {
3697 if (defs === node) return node;
3698 if (defs.TYPE != node.TYPE) return node;
3699 var parent = this.parent();
3700 if (parent instanceof AST_ForEnumeration && parent.init === node) return node;
3701 if (!declarations_only(node)) return node;
3702 defs.definitions = defs.definitions.concat(node.definitions);
3703 CHANGED = true;
3704 if (parent instanceof AST_For && parent.init === node) return null;
3705 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
3706 }
3707 if (node instanceof AST_ExportDeclaration) return node;
3708 if (node instanceof AST_Scope) return node;
3709 if (!is_statement(node)) return node;
3710 }));
3711 }
3712 }
3713 }
3714
3715 function extract_declarations_from_unreachable_code(compressor, stat, target) {
3716 var block;
3717 var dropped = false;
3718 stat.walk(new TreeWalker(function(node, descend) {
3719 if (node instanceof AST_DefClass) {
3720 node.extends = null;
3721 node.properties = [];
3722 push(node);
3723 return true;
3724 }
3725 if (node instanceof AST_Definitions) {
3726 var defns = [];
3727 if (node.remove_initializers(compressor, defns)) {
3728 AST_Node.warn("Dropping initialization in unreachable code [{file}:{line},{col}]", node.start);
3729 }
3730 if (defns.length > 0) {
3731 node.definitions = defns;
3732 push(node);
3733 }
3734 return true;
3735 }
3736 if (node instanceof AST_LambdaDefinition) {
3737 push(node);
3738 return true;
3739 }
3740 if (node instanceof AST_Scope) return true;
3741 if (node instanceof AST_BlockScope) {
3742 var save = block;
3743 block = [];
3744 descend();
3745 if (block.required) {
3746 target.push(make_node(AST_BlockStatement, stat, { body: block }));
3747 } else if (block.length) {
3748 [].push.apply(target, block);
3749 }
3750 block = save;
3751 return true;
3752 }
3753 if (!(node instanceof AST_LoopControl)) dropped = true;
3754 }));
3755 if (dropped) AST_Node.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
3756
3757 function push(node) {
3758 if (block) {
3759 block.push(node);
3760 if (!safe_to_trim(node)) block.required = true;
3761 } else {
3762 target.push(node);
3763 }
3764 }
3765 }
3766
3767 function is_undefined(node, compressor) {
3768 return node.is_undefined
3769 || node instanceof AST_Undefined
3770 || node instanceof AST_UnaryPrefix
3771 && node.operator == "void"
3772 && !(compressor && node.expression.has_side_effects(compressor));
3773 }
3774
3775 // is_truthy()
3776 // return true if `!!node === true`
3777 (function(def) {
3778 def(AST_Node, return_false);
3779 def(AST_Array, return_true);
3780 def(AST_Assign, function() {
3781 return this.operator == "=" && this.right.is_truthy();
3782 });
3783 def(AST_Lambda, return_true);
3784 def(AST_Object, return_true);
3785 def(AST_RegExp, return_true);
3786 def(AST_Sequence, function() {
3787 return this.tail_node().is_truthy();
3788 });
3789 def(AST_SymbolRef, function() {
3790 var fixed = this.fixed_value();
3791 if (!fixed) return false;
3792 this.is_truthy = return_false;
3793 var result = fixed.is_truthy();
3794 delete this.is_truthy;
3795 return result;
3796 });
3797 })(function(node, func) {
3798 node.DEFMETHOD("is_truthy", func);
3799 });
3800
3801 // is_negative_zero()
3802 // return true if the node may represent -0
3803 (function(def) {
3804 def(AST_Node, return_true);
3805 def(AST_Array, return_false);
3806 function binary(op, left, right) {
3807 switch (op) {
3808 case "-":
3809 return left.is_negative_zero()
3810 && (!(right instanceof AST_Constant) || right.value == 0);
3811 case "&&":
3812 case "||":
3813 return left.is_negative_zero() || right.is_negative_zero();
3814 case "*":
3815 case "/":
3816 case "%":
3817 case "**":
3818 return true;
3819 default:
3820 return false;
3821 }
3822 }
3823 def(AST_Assign, function() {
3824 var op = this.operator;
3825 if (op == "=") return this.right.is_negative_zero();
3826 return binary(op.slice(0, -1), this.left, this.right);
3827 });
3828 def(AST_Binary, function() {
3829 return binary(this.operator, this.left, this.right);
3830 });
3831 def(AST_Constant, function() {
3832 return this.value == 0 && 1 / this.value < 0;
3833 });
3834 def(AST_Lambda, return_false);
3835 def(AST_Object, return_false);
3836 def(AST_RegExp, return_false);
3837 def(AST_Sequence, function() {
3838 return this.tail_node().is_negative_zero();
3839 });
3840 def(AST_SymbolRef, function() {
3841 var fixed = this.fixed_value();
3842 if (!fixed) return true;
3843 this.is_negative_zero = return_true;
3844 var result = fixed.is_negative_zero();
3845 delete this.is_negative_zero;
3846 return result;
3847 });
3848 def(AST_UnaryPrefix, function() {
3849 return this.operator == "+" && this.expression.is_negative_zero()
3850 || this.operator == "-";
3851 });
3852 })(function(node, func) {
3853 node.DEFMETHOD("is_negative_zero", func);
3854 });
3855
3856 // may_throw_on_access()
3857 // returns true if this node may be null, undefined or contain `AST_Accessor`
3858 (function(def) {
3859 AST_Node.DEFMETHOD("may_throw_on_access", function(compressor, force) {
3860 return !compressor.option("pure_getters") || this._dot_throw(compressor, force);
3861 });
3862 function is_strict(compressor, force) {
3863 return force || /strict/.test(compressor.option("pure_getters"));
3864 }
3865 def(AST_Node, is_strict);
3866 def(AST_Array, return_false);
3867 def(AST_Assign, function(compressor) {
3868 var op = this.operator;
3869 var sym = this.left;
3870 var rhs = this.right;
3871 if (op != "=") {
3872 return lazy_op[op.slice(0, -1)] && (sym._dot_throw(compressor) || rhs._dot_throw(compressor));
3873 }
3874 if (!rhs._dot_throw(compressor)) return false;
3875 if (!(sym instanceof AST_SymbolRef)) return true;
3876 if (rhs instanceof AST_Binary && rhs.operator == "||" && sym.name == rhs.left.name) {
3877 return rhs.right._dot_throw(compressor);
3878 }
3879 return true;
3880 });
3881 def(AST_Binary, function(compressor) {
3882 return lazy_op[this.operator] && (this.left._dot_throw(compressor) || this.right._dot_throw(compressor));
3883 });
3884 def(AST_Class, return_false);
3885 def(AST_Conditional, function(compressor) {
3886 return this.consequent._dot_throw(compressor) || this.alternative._dot_throw(compressor);
3887 });
3888 def(AST_Constant, return_false);
3889 def(AST_Dot, function(compressor, force) {
3890 if (!is_strict(compressor, force)) return false;
3891 var exp = this.expression;
3892 if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
3893 return !(this.property == "prototype" && is_lambda(exp));
3894 });
3895 def(AST_Lambda, return_false);
3896 def(AST_Null, return_true);
3897 def(AST_Object, function(compressor, force) {
3898 return is_strict(compressor, force) && !all(this.properties, function(prop) {
3899 if (!(prop instanceof AST_ObjectKeyVal)) return false;
3900 return !(prop.key === "__proto__" && prop.value._dot_throw(compressor, force));
3901 });
3902 });
3903 def(AST_ObjectIdentity, function(compressor, force) {
3904 return is_strict(compressor, force) && !this.scope.resolve().new;
3905 });
3906 def(AST_Sequence, function(compressor) {
3907 return this.tail_node()._dot_throw(compressor);
3908 });
3909 def(AST_SymbolRef, function(compressor, force) {
3910 if (this.is_undefined) return true;
3911 if (!is_strict(compressor, force)) return false;
3912 if (is_undeclared_ref(this) && this.is_declared(compressor)) return false;
3913 if (this.is_immutable()) return false;
3914 var def = this.definition();
3915 if (is_arguments(def) && !def.scope.rest && all(def.scope.argnames, function(argname) {
3916 return argname instanceof AST_SymbolFunarg;
3917 })) return def.scope.uses_arguments > 2;
3918 var fixed = this.fixed_value();
3919 if (!fixed) return true;
3920 this._dot_throw = return_true;
3921 if (fixed._dot_throw(compressor)) {
3922 delete this._dot_throw;
3923 return true;
3924 }
3925 this._dot_throw = return_false;
3926 return false;
3927 });
3928 def(AST_UnaryPrefix, function() {
3929 return this.operator == "void";
3930 });
3931 def(AST_UnaryPostfix, return_false);
3932 def(AST_Undefined, return_true);
3933 })(function(node, func) {
3934 node.DEFMETHOD("_dot_throw", func);
3935 });
3936
3937 (function(def) {
3938 def(AST_Node, return_false);
3939 def(AST_Array, return_true);
3940 function is_binary_defined(compressor, op, node) {
3941 switch (op) {
3942 case "&&":
3943 return node.left.is_defined(compressor) && node.right.is_defined(compressor);
3944 case "||":
3945 return node.left.is_truthy() || node.right.is_defined(compressor);
3946 case "??":
3947 return node.left.is_defined(compressor) || node.right.is_defined(compressor);
3948 default:
3949 return true;
3950 }
3951 }
3952 def(AST_Assign, function(compressor) {
3953 var op = this.operator;
3954 if (op == "=") return this.right.is_defined(compressor);
3955 return is_binary_defined(compressor, op.slice(0, -1), this);
3956 });
3957 def(AST_Binary, function(compressor) {
3958 return is_binary_defined(compressor, this.operator, this);
3959 });
3960 def(AST_Conditional, function(compressor) {
3961 return this.consequent.is_defined(compressor) && this.alternative.is_defined(compressor);
3962 });
3963 def(AST_Constant, return_true);
3964 def(AST_Hole, return_false);
3965 def(AST_Lambda, return_true);
3966 def(AST_Object, return_true);
3967 def(AST_Sequence, function(compressor) {
3968 return this.tail_node().is_defined(compressor);
3969 });
3970 def(AST_SymbolRef, function(compressor) {
3971 if (this.is_undefined) return false;
3972 if (is_undeclared_ref(this) && this.is_declared(compressor)) return true;
3973 if (this.is_immutable()) return true;
3974 var fixed = this.fixed_value();
3975 if (!fixed) return false;
3976 this.is_defined = return_false;
3977 var result = fixed.is_defined(compressor);
3978 delete this.is_defined;
3979 return result;
3980 });
3981 def(AST_UnaryPrefix, function() {
3982 return this.operator != "void";
3983 });
3984 def(AST_UnaryPostfix, return_true);
3985 def(AST_Undefined, return_false);
3986 })(function(node, func) {
3987 node.DEFMETHOD("is_defined", func);
3988 });
3989
3990 /* -----[ boolean/negation helpers ]----- */
3991
3992 // methods to determine whether an expression has a boolean result type
3993 (function(def) {
3994 def(AST_Node, return_false);
3995 def(AST_Assign, function(compressor) {
3996 return this.operator == "=" && this.right.is_boolean(compressor);
3997 });
3998 var binary = makePredicate("in instanceof == != === !== < <= >= >");
3999 def(AST_Binary, function(compressor) {
4000 return binary[this.operator] || lazy_op[this.operator]
4001 && this.left.is_boolean(compressor)
4002 && this.right.is_boolean(compressor);
4003 });
4004 def(AST_Boolean, return_true);
4005 var fn = makePredicate("every hasOwnProperty isPrototypeOf propertyIsEnumerable some");
4006 def(AST_Call, function(compressor) {
4007 if (!compressor.option("unsafe")) return false;
4008 var exp = this.expression;
4009 return exp instanceof AST_Dot && (fn[exp.property]
4010 || exp.property == "test" && exp.expression instanceof AST_RegExp);
4011 });
4012 def(AST_Conditional, function(compressor) {
4013 return this.consequent.is_boolean(compressor) && this.alternative.is_boolean(compressor);
4014 });
4015 def(AST_New, return_false);
4016 def(AST_Sequence, function(compressor) {
4017 return this.tail_node().is_boolean(compressor);
4018 });
4019 def(AST_SymbolRef, function(compressor) {
4020 var fixed = this.fixed_value();
4021 if (!fixed) return false;
4022 this.is_boolean = return_false;
4023 var result = fixed.is_boolean(compressor);
4024 delete this.is_boolean;
4025 return result;
4026 });
4027 var unary = makePredicate("! delete");
4028 def(AST_UnaryPrefix, function() {
4029 return unary[this.operator];
4030 });
4031 })(function(node, func) {
4032 node.DEFMETHOD("is_boolean", func);
4033 });
4034
4035 // methods to determine if an expression has a numeric result type
4036 (function(def) {
4037 def(AST_Node, return_false);
4038 var binary = makePredicate("- * / % ** & | ^ << >> >>>");
4039 def(AST_Assign, function(compressor) {
4040 return binary[this.operator.slice(0, -1)]
4041 || this.operator == "=" && this.right.is_number(compressor);
4042 });
4043 def(AST_Binary, function(compressor) {
4044 if (binary[this.operator]) return true;
4045 if (this.operator != "+") return false;
4046 return (this.left.is_boolean(compressor) || this.left.is_number(compressor))
4047 && (this.right.is_boolean(compressor) || this.right.is_number(compressor));
4048 });
4049 var fn = makePredicate([
4050 "charCodeAt",
4051 "getDate",
4052 "getDay",
4053 "getFullYear",
4054 "getHours",
4055 "getMilliseconds",
4056 "getMinutes",
4057 "getMonth",
4058 "getSeconds",
4059 "getTime",
4060 "getTimezoneOffset",
4061 "getUTCDate",
4062 "getUTCDay",
4063 "getUTCFullYear",
4064 "getUTCHours",
4065 "getUTCMilliseconds",
4066 "getUTCMinutes",
4067 "getUTCMonth",
4068 "getUTCSeconds",
4069 "getYear",
4070 "indexOf",
4071 "lastIndexOf",
4072 "localeCompare",
4073 "push",
4074 "search",
4075 "setDate",
4076 "setFullYear",
4077 "setHours",
4078 "setMilliseconds",
4079 "setMinutes",
4080 "setMonth",
4081 "setSeconds",
4082 "setTime",
4083 "setUTCDate",
4084 "setUTCFullYear",
4085 "setUTCHours",
4086 "setUTCMilliseconds",
4087 "setUTCMinutes",
4088 "setUTCMonth",
4089 "setUTCSeconds",
4090 "setYear",
4091 "toExponential",
4092 "toFixed",
4093 "toPrecision",
4094 ]);
4095 def(AST_Call, function(compressor) {
4096 if (!compressor.option("unsafe")) return false;
4097 var exp = this.expression;
4098 return exp instanceof AST_Dot && (fn[exp.property]
4099 || is_undeclared_ref(exp.expression) && exp.expression.name == "Math");
4100 });
4101 def(AST_Conditional, function(compressor) {
4102 return this.consequent.is_number(compressor) && this.alternative.is_number(compressor);
4103 });
4104 def(AST_New, return_false);
4105 def(AST_Number, return_true);
4106 def(AST_Sequence, function(compressor) {
4107 return this.tail_node().is_number(compressor);
4108 });
4109 def(AST_SymbolRef, function(compressor) {
4110 var fixed = this.fixed_value();
4111 if (!fixed) return false;
4112 this.is_number = return_false;
4113 var result = fixed.is_number(compressor);
4114 delete this.is_number;
4115 return result;
4116 });
4117 var unary = makePredicate("+ - ~ ++ --");
4118 def(AST_Unary, function() {
4119 return unary[this.operator];
4120 });
4121 })(function(node, func) {
4122 node.DEFMETHOD("is_number", func);
4123 });
4124
4125 // methods to determine if an expression has a string result type
4126 (function(def) {
4127 def(AST_Node, return_false);
4128 def(AST_Assign, function(compressor) {
4129 switch (this.operator) {
4130 case "+=":
4131 if (this.left.is_string(compressor)) return true;
4132 case "=":
4133 return this.right.is_string(compressor);
4134 }
4135 });
4136 def(AST_Binary, function(compressor) {
4137 return this.operator == "+" &&
4138 (this.left.is_string(compressor) || this.right.is_string(compressor));
4139 });
4140 var fn = makePredicate([
4141 "charAt",
4142 "substr",
4143 "substring",
4144 "toLowerCase",
4145 "toString",
4146 "toUpperCase",
4147 "trim",
4148 ]);
4149 def(AST_Call, function(compressor) {
4150 if (!compressor.option("unsafe")) return false;
4151 var exp = this.expression;
4152 return exp instanceof AST_Dot && fn[exp.property];
4153 });
4154 def(AST_Conditional, function(compressor) {
4155 return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
4156 });
4157 def(AST_Sequence, function(compressor) {
4158 return this.tail_node().is_string(compressor);
4159 });
4160 def(AST_String, return_true);
4161 def(AST_SymbolRef, function(compressor) {
4162 var fixed = this.fixed_value();
4163 if (!fixed) return false;
4164 this.is_string = return_false;
4165 var result = fixed.is_string(compressor);
4166 delete this.is_string;
4167 return result;
4168 });
4169 def(AST_Template, function(compressor) {
4170 return !this.tag || is_raw_tag(compressor, this.tag);
4171 });
4172 def(AST_UnaryPrefix, function() {
4173 return this.operator == "typeof";
4174 });
4175 })(function(node, func) {
4176 node.DEFMETHOD("is_string", func);
4177 });
4178
4179 var lazy_op = makePredicate("&& || ??");
4180
4181 (function(def) {
4182 function to_node(value, orig) {
4183 if (value instanceof AST_Node) return value.clone(true);
4184 if (Array.isArray(value)) return make_node(AST_Array, orig, {
4185 elements: value.map(function(value) {
4186 return to_node(value, orig);
4187 })
4188 });
4189 if (value && typeof value == "object") {
4190 var props = [];
4191 for (var key in value) if (HOP(value, key)) {
4192 props.push(make_node(AST_ObjectKeyVal, orig, {
4193 key: key,
4194 value: to_node(value[key], orig)
4195 }));
4196 }
4197 return make_node(AST_Object, orig, {
4198 properties: props
4199 });
4200 }
4201 return make_node_from_constant(value, orig);
4202 }
4203
4204 function warn(node) {
4205 AST_Node.warn("global_defs {node} redefined [{file}:{line},{col}]", {
4206 node: node,
4207 file: node.start.file,
4208 line: node.start.line,
4209 col: node.start.col,
4210 });
4211 }
4212
4213 AST_Toplevel.DEFMETHOD("resolve_defines", function(compressor) {
4214 if (!compressor.option("global_defs")) return this;
4215 this.figure_out_scope({ ie: compressor.option("ie") });
4216 return this.transform(new TreeTransformer(function(node) {
4217 var def = node._find_defs(compressor, "");
4218 if (!def) return;
4219 var level = 0, child = node, parent;
4220 while (parent = this.parent(level++)) {
4221 if (!(parent instanceof AST_PropAccess)) break;
4222 if (parent.expression !== child) break;
4223 child = parent;
4224 }
4225 if (is_lhs(child, parent)) {
4226 warn(node);
4227 return;
4228 }
4229 return def;
4230 }));
4231 });
4232 def(AST_Node, noop);
4233 def(AST_Dot, function(compressor, suffix) {
4234 return this.expression._find_defs(compressor, "." + this.property + suffix);
4235 });
4236 def(AST_SymbolDeclaration, function(compressor) {
4237 if (!this.definition().global) return;
4238 if (HOP(compressor.option("global_defs"), this.name)) warn(this);
4239 });
4240 def(AST_SymbolRef, function(compressor, suffix) {
4241 if (!this.definition().global) return;
4242 var defines = compressor.option("global_defs");
4243 var name = this.name + suffix;
4244 if (HOP(defines, name)) return to_node(defines[name], this);
4245 });
4246 })(function(node, func) {
4247 node.DEFMETHOD("_find_defs", func);
4248 });
4249
4250 function best_of_expression(ast1, ast2, threshold) {
4251 var delta = ast2.print_to_string().length - ast1.print_to_string().length;
4252 return delta < (threshold || 0) ? ast2 : ast1;
4253 }
4254
4255 function best_of_statement(ast1, ast2, threshold) {
4256 return best_of_expression(make_node(AST_SimpleStatement, ast1, {
4257 body: ast1
4258 }), make_node(AST_SimpleStatement, ast2, {
4259 body: ast2
4260 }), threshold).body;
4261 }
4262
4263 function best_of(compressor, ast1, ast2, threshold) {
4264 return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2, threshold);
4265 }
4266
4267 function convert_to_predicate(obj) {
4268 var map = Object.create(null);
4269 Object.keys(obj).forEach(function(key) {
4270 map[key] = makePredicate(obj[key]);
4271 });
4272 return map;
4273 }
4274
4275 function skip_directives(body) {
4276 for (var i = 0; i < body.length; i++) {
4277 var stat = body[i];
4278 if (!(stat instanceof AST_Directive)) return stat;
4279 }
4280 }
4281
4282 function arrow_first_statement() {
4283 if (this.value) return make_node(AST_Return, this.value, {
4284 value: this.value
4285 });
4286 return skip_directives(this.body);
4287 }
4288 AST_Arrow.DEFMETHOD("first_statement", arrow_first_statement);
4289 AST_AsyncArrow.DEFMETHOD("first_statement", arrow_first_statement);
4290 AST_Lambda.DEFMETHOD("first_statement", function() {
4291 return skip_directives(this.body);
4292 });
4293
4294 AST_Lambda.DEFMETHOD("length", function() {
4295 var argnames = this.argnames;
4296 for (var i = 0; i < argnames.length; i++) {
4297 if (argnames[i] instanceof AST_DefaultValue) break;
4298 }
4299 return i;
4300 });
4301
4302 function try_evaluate(compressor, node) {
4303 var ev = node.evaluate(compressor);
4304 if (ev === node) return node;
4305 ev = make_node_from_constant(ev, node).optimize(compressor);
4306 return best_of(compressor, node, ev, compressor.eval_threshold);
4307 }
4308
4309 var object_fns = [
4310 "constructor",
4311 "toString",
4312 "valueOf",
4313 ];
4314 var native_fns = convert_to_predicate({
4315 Array: [
4316 "indexOf",
4317 "join",
4318 "lastIndexOf",
4319 "slice",
4320 ].concat(object_fns),
4321 Boolean: object_fns,
4322 Function: object_fns,
4323 Number: [
4324 "toExponential",
4325 "toFixed",
4326 "toPrecision",
4327 ].concat(object_fns),
4328 Object: object_fns,
4329 RegExp: [
4330 "exec",
4331 "test",
4332 ].concat(object_fns),
4333 String: [
4334 "charAt",
4335 "charCodeAt",
4336 "concat",
4337 "indexOf",
4338 "italics",
4339 "lastIndexOf",
4340 "match",
4341 "replace",
4342 "search",
4343 "slice",
4344 "split",
4345 "substr",
4346 "substring",
4347 "toLowerCase",
4348 "toUpperCase",
4349 "trim",
4350 ].concat(object_fns),
4351 });
4352 var static_fns = convert_to_predicate({
4353 Array: [
4354 "isArray",
4355 ],
4356 Math: [
4357 "abs",
4358 "acos",
4359 "asin",
4360 "atan",
4361 "ceil",
4362 "cos",
4363 "exp",
4364 "floor",
4365 "log",
4366 "round",
4367 "sin",
4368 "sqrt",
4369 "tan",
4370 "atan2",
4371 "pow",
4372 "max",
4373 "min",
4374 ],
4375 Number: [
4376 "isFinite",
4377 "isNaN",
4378 ],
4379 Object: [
4380 "create",
4381 "getOwnPropertyDescriptor",
4382 "getOwnPropertyNames",
4383 "getPrototypeOf",
4384 "isExtensible",
4385 "isFrozen",
4386 "isSealed",
4387 "keys",
4388 ],
4389 String: [
4390 "fromCharCode",
4391 "raw",
4392 ],
4393 });
4394
4395 function is_static_fn(node) {
4396 if (!(node instanceof AST_Dot)) return false;
4397 var expr = node.expression;
4398 if (!is_undeclared_ref(expr)) return false;
4399 var static_fn = static_fns[expr.name];
4400 return static_fn && (static_fn[node.property] || expr.name == "Math" && node.property == "random");
4401 }
4402
4403 // Accomodate when compress option evaluate=false
4404 // as well as the common constant expressions !0 and -1
4405 (function(def) {
4406 def(AST_Node, return_false);
4407 def(AST_Constant, return_true);
4408 def(AST_RegExp, return_false);
4409 var unaryPrefix = makePredicate("! ~ - + void");
4410 def(AST_UnaryPrefix, function() {
4411 return unaryPrefix[this.operator] && this.expression instanceof AST_Constant;
4412 });
4413 })(function(node, func) {
4414 node.DEFMETHOD("is_constant", func);
4415 });
4416
4417 // methods to evaluate a constant expression
4418 (function(def) {
4419 // If the node has been successfully reduced to a constant,
4420 // then its value is returned; otherwise the element itself
4421 // is returned.
4422 //
4423 // They can be distinguished as constant value is never a
4424 // descendant of AST_Node.
4425 //
4426 // When `ignore_side_effects` is `true`, inspect the constant value
4427 // produced without worrying about any side effects caused by said
4428 // expression.
4429 AST_Node.DEFMETHOD("evaluate", function(compressor, ignore_side_effects) {
4430 if (!compressor.option("evaluate")) return this;
4431 var cached = [];
4432 var val = this._eval(compressor, ignore_side_effects, cached, 1);
4433 cached.forEach(function(node) {
4434 delete node._eval;
4435 });
4436 if (ignore_side_effects) return val;
4437 if (!val || val instanceof RegExp) return val;
4438 if (typeof val == "function" || typeof val == "object") return this;
4439 return val;
4440 });
4441 var scan_modified = new TreeWalker(function(node) {
4442 if (node instanceof AST_Assign) modified(node.left);
4443 if (node instanceof AST_Unary && UNARY_POSTFIX[node.operator]) modified(node.expression);
4444 });
4445 function modified(node) {
4446 if (node instanceof AST_DestructuredArray) {
4447 node.elements.forEach(modified);
4448 } else if (node instanceof AST_DestructuredObject) {
4449 node.properties.forEach(function(prop) {
4450 modified(prop.value);
4451 });
4452 } else if (node instanceof AST_PropAccess) {
4453 modified(node.expression);
4454 } else if (node instanceof AST_SymbolRef) {
4455 node.definition().references.forEach(function(ref) {
4456 delete ref._eval;
4457 });
4458 }
4459 }
4460 def(AST_Statement, function() {
4461 throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
4462 });
4463 def(AST_Accessor, return_this);
4464 def(AST_BigInt, return_this);
4465 def(AST_Class, return_this);
4466 def(AST_Node, return_this);
4467 def(AST_Constant, function() {
4468 return this.value;
4469 });
4470 def(AST_Assign, function(compressor, ignore_side_effects, cached, depth) {
4471 var lhs = this.left;
4472 if (!ignore_side_effects) {
4473 if (!(lhs instanceof AST_SymbolRef)) return this;
4474 if (!HOP(lhs, "_eval")) {
4475 if (!lhs.fixed) return this;
4476 var def = lhs.definition();
4477 if (!def.fixed) return this;
4478 if (def.undeclared) return this;
4479 if (def.last_ref !== lhs) return this;
4480 if (def.single_use == "m") return this;
4481 }
4482 }
4483 var op = this.operator;
4484 var node;
4485 if (!HOP(lhs, "_eval") && lhs instanceof AST_SymbolRef && lhs.fixed && lhs.definition().fixed) {
4486 node = lhs;
4487 } else if (op == "=") {
4488 node = this.right;
4489 } else {
4490 node = make_node(AST_Binary, this, {
4491 operator: op.slice(0, -1),
4492 left: lhs,
4493 right: this.right,
4494 });
4495 }
4496 lhs.walk(scan_modified);
4497 var value = node._eval(compressor, ignore_side_effects, cached, depth);
4498 if (typeof value == "object") return this;
4499 modified(lhs);
4500 return value;
4501 });
4502 def(AST_Sequence, function(compressor, ignore_side_effects, cached, depth) {
4503 if (!ignore_side_effects) return this;
4504 var exprs = this.expressions;
4505 for (var i = 0, last = exprs.length - 1; i < last; i++) {
4506 exprs[i].walk(scan_modified);
4507 }
4508 var tail = exprs[last];
4509 var value = tail._eval(compressor, ignore_side_effects, cached, depth);
4510 return value === tail ? this : value;
4511 });
4512 def(AST_Lambda, function(compressor) {
4513 if (compressor.option("unsafe")) {
4514 var fn = function() {};
4515 fn.node = this;
4516 fn.toString = function() {
4517 return "function(){}";
4518 };
4519 return fn;
4520 }
4521 return this;
4522 });
4523 def(AST_Array, function(compressor, ignore_side_effects, cached, depth) {
4524 if (compressor.option("unsafe")) {
4525 var elements = [];
4526 for (var i = 0; i < this.elements.length; i++) {
4527 var element = this.elements[i];
4528 if (element instanceof AST_Hole) return this;
4529 var value = element._eval(compressor, ignore_side_effects, cached, depth);
4530 if (element === value) return this;
4531 elements.push(value);
4532 }
4533 return elements;
4534 }
4535 return this;
4536 });
4537 var nonsafe_props = makePredicate("__proto__ toString valueOf");
4538 def(AST_Object, function(compressor, ignore_side_effects, cached, depth) {
4539 if (compressor.option("unsafe")) {
4540 var val = {};
4541 for (var i = 0; i < this.properties.length; i++) {
4542 var prop = this.properties[i];
4543 if (!(prop instanceof AST_ObjectKeyVal)) return this;
4544 var key = prop.key;
4545 if (key instanceof AST_Node) {
4546 key = key._eval(compressor, ignore_side_effects, cached, depth);
4547 if (key === prop.key) return this;
4548 }
4549 if (nonsafe_props[key]) return this;
4550 val[key] = prop.value._eval(compressor, ignore_side_effects, cached, depth);
4551 if (val[key] === prop.value) return this;
4552 }
4553 return val;
4554 }
4555 return this;
4556 });
4557 var non_converting_unary = makePredicate("! typeof void");
4558 def(AST_UnaryPrefix, function(compressor, ignore_side_effects, cached, depth) {
4559 var e = this.expression;
4560 var op = this.operator;
4561 // Function would be evaluated to an array and so typeof would
4562 // incorrectly return "object". Hence making is a special case.
4563 if (compressor.option("typeofs")
4564 && op == "typeof"
4565 && (e instanceof AST_Lambda
4566 || e instanceof AST_SymbolRef
4567 && e.fixed_value() instanceof AST_Lambda)) {
4568 return typeof function(){};
4569 }
4570 var def = e instanceof AST_SymbolRef && e.definition();
4571 if (!non_converting_unary[op] && !(def && def.fixed)) depth++;
4572 e.walk(scan_modified);
4573 var v = e._eval(compressor, ignore_side_effects, cached, depth);
4574 if (v === e) {
4575 if (ignore_side_effects && op == "void") return;
4576 return this;
4577 }
4578 switch (op) {
4579 case "!": return !v;
4580 case "typeof":
4581 // typeof <RegExp> returns "object" or "function" on different platforms
4582 // so cannot evaluate reliably
4583 if (v instanceof RegExp) return this;
4584 return typeof v;
4585 case "void": return;
4586 case "~": return ~v;
4587 case "-": return -v;
4588 case "+": return +v;
4589 case "++":
4590 case "--":
4591 if (!def) return this;
4592 if (!ignore_side_effects) {
4593 if (def.undeclared) return this;
4594 if (def.last_ref !== e) return this;
4595 }
4596 if (HOP(e, "_eval")) v = +(op[0] + 1) + +v;
4597 modified(e);
4598 return v;
4599 }
4600 return this;
4601 });
4602 def(AST_UnaryPostfix, function(compressor, ignore_side_effects, cached, depth) {
4603 var e = this.expression;
4604 if (!(e instanceof AST_SymbolRef)) {
4605 if (!ignore_side_effects) return this;
4606 } else if (!HOP(e, "_eval")) {
4607 if (!e.fixed) return this;
4608 if (!ignore_side_effects) {
4609 var def = e.definition();
4610 if (!def.fixed) return this;
4611 if (def.undeclared) return this;
4612 if (def.last_ref !== e) return this;
4613 }
4614 }
4615 if (!(e instanceof AST_SymbolRef && e.definition().fixed)) depth++;
4616 e.walk(scan_modified);
4617 var v = e._eval(compressor, ignore_side_effects, cached, depth);
4618 if (v === e) return this;
4619 modified(e);
4620 return +v;
4621 });
4622 var non_converting_binary = makePredicate("&& || === !==");
4623 def(AST_Binary, function(compressor, ignore_side_effects, cached, depth) {
4624 if (!non_converting_binary[this.operator]) depth++;
4625 var left = this.left._eval(compressor, ignore_side_effects, cached, depth);
4626 if (left === this.left) return this;
4627 if (this.operator == (left ? "||" : "&&")) return left;
4628 var rhs_ignore_side_effects = ignore_side_effects && !(left && typeof left == "object");
4629 var right = this.right._eval(compressor, rhs_ignore_side_effects, cached, depth);
4630 if (right === this.right) return this;
4631 var result;
4632 switch (this.operator) {
4633 case "&&" : result = left && right; break;
4634 case "||" : result = left || right; break;
4635 case "??" :
4636 result = left == null ? right : left;
4637 break;
4638 case "|" : result = left | right; break;
4639 case "&" : result = left & right; break;
4640 case "^" : result = left ^ right; break;
4641 case "+" : result = left + right; break;
4642 case "-" : result = left - right; break;
4643 case "*" : result = left * right; break;
4644 case "/" : result = left / right; break;
4645 case "%" : result = left % right; break;
4646 case "<<" : result = left << right; break;
4647 case ">>" : result = left >> right; break;
4648 case ">>>": result = left >>> right; break;
4649 case "==" : result = left == right; break;
4650 case "===": result = left === right; break;
4651 case "!=" : result = left != right; break;
4652 case "!==": result = left !== right; break;
4653 case "<" : result = left < right; break;
4654 case "<=" : result = left <= right; break;
4655 case ">" : result = left > right; break;
4656 case ">=" : result = left >= right; break;
4657 case "**":
4658 result = Math.pow(left, right);
4659 break;
4660 case "in":
4661 if (right && typeof right == "object" && HOP(right, left)) {
4662 result = true;
4663 break;
4664 }
4665 default:
4666 return this;
4667 }
4668 if (isNaN(result)) return compressor.find_parent(AST_With) ? this : result;
4669 if (compressor.option("unsafe_math")
4670 && !ignore_side_effects
4671 && result
4672 && typeof result == "number"
4673 && (this.operator == "+" || this.operator == "-")) {
4674 var digits = Math.max(0, decimals(left), decimals(right));
4675 // 53-bit significand ---> 15.95 decimal places
4676 if (digits < 16) return +result.toFixed(digits);
4677 }
4678 return result;
4679
4680 function decimals(operand) {
4681 var match = /(\.[0-9]*)?(e.+)?$/.exec(+operand);
4682 return (match[1] || ".").length - 1 - (match[2] || "").slice(1);
4683 }
4684 });
4685 def(AST_Conditional, function(compressor, ignore_side_effects, cached, depth) {
4686 var condition = this.condition._eval(compressor, ignore_side_effects, cached, depth);
4687 if (condition === this.condition) return this;
4688 var node = condition ? this.consequent : this.alternative;
4689 var value = node._eval(compressor, ignore_side_effects, cached, depth);
4690 return value === node ? this : value;
4691 });
4692 function verify_escaped(ref, depth) {
4693 var escaped = ref.definition().escaped;
4694 switch (escaped.length) {
4695 case 0:
4696 return true;
4697 case 1:
4698 var found = false;
4699 escaped[0].walk(new TreeWalker(function(node) {
4700 if (found) return true;
4701 if (node === ref) return found = true;
4702 if (node instanceof AST_Scope) return true;
4703 }));
4704 return found;
4705 default:
4706 return depth <= escaped.depth;
4707 }
4708 }
4709 def(AST_SymbolRef, function(compressor, ignore_side_effects, cached, depth) {
4710 var fixed = this.fixed_value();
4711 if (!fixed) return this;
4712 var value;
4713 if (HOP(fixed, "_eval")) {
4714 value = fixed._eval();
4715 } else {
4716 this._eval = return_this;
4717 value = fixed._eval(compressor, ignore_side_effects, cached, depth);
4718 delete this._eval;
4719 if (value === fixed) return this;
4720 fixed._eval = function() {
4721 return value;
4722 };
4723 cached.push(fixed);
4724 }
4725 return value && typeof value == "object" && !verify_escaped(this, depth) ? this : value;
4726 });
4727 var global_objs = {
4728 Array: Array,
4729 Math: Math,
4730 Number: Number,
4731 Object: Object,
4732 String: String,
4733 };
4734 var static_values = convert_to_predicate({
4735 Math: [
4736 "E",
4737 "LN10",
4738 "LN2",
4739 "LOG2E",
4740 "LOG10E",
4741 "PI",
4742 "SQRT1_2",
4743 "SQRT2",
4744 ],
4745 Number: [
4746 "MAX_VALUE",
4747 "MIN_VALUE",
4748 "NaN",
4749 "NEGATIVE_INFINITY",
4750 "POSITIVE_INFINITY",
4751 ],
4752 });
4753 var regexp_props = makePredicate("global ignoreCase multiline source");
4754 def(AST_PropAccess, function(compressor, ignore_side_effects, cached, depth) {
4755 if (compressor.option("unsafe")) {
4756 var val;
4757 var exp = this.expression;
4758 if (!is_undeclared_ref(exp)) {
4759 val = exp._eval(compressor, ignore_side_effects, cached, depth + 1);
4760 if (val == null || val === exp) return this;
4761 }
4762 var key = this.property;
4763 if (key instanceof AST_Node) {
4764 key = key._eval(compressor, ignore_side_effects, cached, depth);
4765 if (key === this.property) return this;
4766 }
4767 if (val === undefined) {
4768 var static_value = static_values[exp.name];
4769 if (!static_value || !static_value[key]) return this;
4770 val = global_objs[exp.name];
4771 } else if (val instanceof RegExp) {
4772 if (!regexp_props[key]) return this;
4773 } else if (typeof val == "object") {
4774 if (!HOP(val, key)) return this;
4775 } else if (typeof val == "function") switch (key) {
4776 case "name":
4777 return val.node.name ? val.node.name.name : "";
4778 case "length":
4779 return val.node.length();
4780 default:
4781 return this;
4782 }
4783 return val[key];
4784 }
4785 return this;
4786 });
4787 function eval_all(nodes, compressor, ignore_side_effects, cached, depth) {
4788 var values = [];
4789 for (var i = 0; i < nodes.length; i++) {
4790 var node = nodes[i];
4791 var value = node._eval(compressor, ignore_side_effects, cached, depth);
4792 if (node === value) return;
4793 values.push(value);
4794 }
4795 return values;
4796 }
4797 def(AST_Call, function(compressor, ignore_side_effects, cached, depth) {
4798 var exp = this.expression;
4799 var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
4800 if (fn instanceof AST_Arrow || fn instanceof AST_Defun || fn instanceof AST_Function) {
4801 if (fn.evaluating) return this;
4802 if (fn.name && fn.name.definition().recursive_refs > 0) return this;
4803 if (this.is_expr_pure(compressor)) return this;
4804 var args = eval_all(this.args, compressor, ignore_side_effects, cached, depth);
4805 if (!all(fn.argnames, function(sym, index) {
4806 if (sym instanceof AST_DefaultValue) {
4807 if (!args) return false;
4808 if (args[index] === undefined) {
4809 var value = sym.value._eval(compressor, ignore_side_effects, cached, depth);
4810 if (value === sym.value) return false;
4811 args[index] = value;
4812 }
4813 sym = sym.name;
4814 }
4815 return !(sym instanceof AST_Destructured);
4816 })) return this;
4817 if (fn.rest instanceof AST_Destructured) return this;
4818 if (!args && !ignore_side_effects) return this;
4819 var stat = fn.first_statement();
4820 if (!(stat instanceof AST_Return)) {
4821 if (ignore_side_effects) {
4822 fn.walk(scan_modified);
4823 var found = false;
4824 fn.evaluating = true;
4825 walk_body(fn, new TreeWalker(function(node) {
4826 if (found) return true;
4827 if (node instanceof AST_Return) {
4828 if (node.value && node.value._eval(compressor, true, cached, depth) !== undefined) {
4829 found = true;
4830 }
4831 return true;
4832 }
4833 if (node instanceof AST_Scope && node !== fn) return true;
4834 }));
4835 delete fn.evaluating;
4836 if (!found) return;
4837 }
4838 return this;
4839 }
4840 var val = stat.value;
4841 if (!val) return;
4842 var cached_args = [];
4843 if (!args || all(fn.argnames, function(sym, i) {
4844 return assign(sym, args[i]);
4845 }) && !(fn.rest && !assign(fn.rest, args.slice(fn.argnames.length))) || ignore_side_effects) {
4846 if (ignore_side_effects) fn.argnames.forEach(function(sym) {
4847 if (sym instanceof AST_DefaultValue) sym.value.walk(scan_modified);
4848 });
4849 fn.evaluating = true;
4850 val = val._eval(compressor, ignore_side_effects, cached, depth);
4851 delete fn.evaluating;
4852 }
4853 cached_args.forEach(function(node) {
4854 delete node._eval;
4855 });
4856 return val === stat.value ? this : val;
4857 } else if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
4858 var key = exp.property;
4859 if (key instanceof AST_Node) {
4860 key = key._eval(compressor, ignore_side_effects, cached, depth);
4861 if (key === exp.property) return this;
4862 }
4863 var val;
4864 var e = exp.expression;
4865 if (is_undeclared_ref(e)) {
4866 var static_fn = static_fns[e.name];
4867 if (!static_fn || !static_fn[key]) return this;
4868 val = global_objs[e.name];
4869 } else {
4870 val = e._eval(compressor, ignore_side_effects, cached, depth + 1);
4871 if (val == null || val === e) return this;
4872 var native_fn = native_fns[val.constructor.name];
4873 if (!native_fn || !native_fn[key]) return this;
4874 if (val instanceof RegExp && val.global && !(e instanceof AST_RegExp)) return this;
4875 }
4876 var args = eval_all(this.args, compressor, ignore_side_effects, cached, depth);
4877 if (!args) return this;
4878 if (key == "replace" && typeof args[1] == "function") return this;
4879 try {
4880 return val[key].apply(val, args);
4881 } catch (ex) {
4882 AST_Node.warn("Error evaluating {code} [{file}:{line},{col}]", {
4883 code: this,
4884 file: this.start.file,
4885 line: this.start.line,
4886 col: this.start.col,
4887 });
4888 } finally {
4889 if (val instanceof RegExp) val.lastIndex = 0;
4890 }
4891 }
4892 return this;
4893
4894 function assign(sym, arg) {
4895 if (sym instanceof AST_DefaultValue) sym = sym.name;
4896 var def = sym.definition();
4897 if (def.orig[def.orig.length - 1] !== sym) return false;
4898 var value = arg;
4899 def.references.forEach(function(node) {
4900 node._eval = function() {
4901 return value;
4902 };
4903 cached_args.push(node);
4904 });
4905 return true;
4906 }
4907 });
4908 def(AST_New, return_this);
4909 def(AST_Template, function(compressor, ignore_side_effects, cached, depth) {
4910 if (!compressor.option("templates")) return this;
4911 if (this.tag) {
4912 if (!is_raw_tag(compressor, this.tag)) return this;
4913 decode = function(str) {
4914 return str;
4915 };
4916 }
4917 var exprs = eval_all(this.expressions, compressor, ignore_side_effects, cached, depth);
4918 if (!exprs) return this;
4919 var malformed = false;
4920 var ret = decode(this.strings[0]);
4921 for (var i = 0; i < exprs.length; i++) {
4922 ret += exprs[i] + decode(this.strings[i + 1]);
4923 }
4924 if (!malformed) return ret;
4925 this._eval = return_this;
4926 return this;
4927
4928 function decode(str) {
4929 str = decode_template(str);
4930 if (typeof str != "string") malformed = true;
4931 return str;
4932 }
4933 });
4934 })(function(node, func) {
4935 node.DEFMETHOD("_eval", func);
4936 });
4937
4938 // method to negate an expression
4939 (function(def) {
4940 function basic_negation(exp) {
4941 return make_node(AST_UnaryPrefix, exp, {
4942 operator: "!",
4943 expression: exp
4944 });
4945 }
4946 function best(orig, alt, first_in_statement) {
4947 var negated = basic_negation(orig);
4948 if (first_in_statement) {
4949 var stat = make_node(AST_SimpleStatement, alt, {
4950 body: alt
4951 });
4952 return best_of_expression(negated, stat) === stat ? alt : negated;
4953 }
4954 return best_of_expression(negated, alt);
4955 }
4956 def(AST_Node, function() {
4957 return basic_negation(this);
4958 });
4959 def(AST_Statement, function() {
4960 throw new Error("Cannot negate a statement");
4961 });
4962 def(AST_Binary, function(compressor, first_in_statement) {
4963 var self = this.clone(), op = this.operator;
4964 if (compressor.option("unsafe_comps")) {
4965 switch (op) {
4966 case "<=" : self.operator = ">" ; return self;
4967 case "<" : self.operator = ">=" ; return self;
4968 case ">=" : self.operator = "<" ; return self;
4969 case ">" : self.operator = "<=" ; return self;
4970 }
4971 }
4972 switch (op) {
4973 case "==" : self.operator = "!="; return self;
4974 case "!=" : self.operator = "=="; return self;
4975 case "===": self.operator = "!=="; return self;
4976 case "!==": self.operator = "==="; return self;
4977 case "&&":
4978 self.operator = "||";
4979 self.left = self.left.negate(compressor, first_in_statement);
4980 self.right = self.right.negate(compressor);
4981 return best(this, self, first_in_statement);
4982 case "||":
4983 self.operator = "&&";
4984 self.left = self.left.negate(compressor, first_in_statement);
4985 self.right = self.right.negate(compressor);
4986 return best(this, self, first_in_statement);
4987 }
4988 return basic_negation(this);
4989 });
4990 def(AST_ClassExpression, function() {
4991 return basic_negation(this);
4992 });
4993 def(AST_Conditional, function(compressor, first_in_statement) {
4994 var self = this.clone();
4995 self.consequent = self.consequent.negate(compressor);
4996 self.alternative = self.alternative.negate(compressor);
4997 return best(this, self, first_in_statement);
4998 });
4999 def(AST_LambdaExpression, function() {
5000 return basic_negation(this);
5001 });
5002 def(AST_Sequence, function(compressor) {
5003 var expressions = this.expressions.slice();
5004 expressions.push(expressions.pop().negate(compressor));
5005 return make_sequence(this, expressions);
5006 });
5007 def(AST_UnaryPrefix, function() {
5008 if (this.operator == "!")
5009 return this.expression;
5010 return basic_negation(this);
5011 });
5012 })(function(node, func) {
5013 node.DEFMETHOD("negate", function(compressor, first_in_statement) {
5014 return func.call(this, compressor, first_in_statement);
5015 });
5016 });
5017
5018 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");
5019 var global_pure_constructors = makePredicate("Map Set WeakMap WeakSet");
5020 AST_Call.DEFMETHOD("is_expr_pure", function(compressor) {
5021 if (compressor.option("unsafe")) {
5022 var expr = this.expression;
5023 if (is_undeclared_ref(expr)) {
5024 if (global_pure_fns[expr.name]) return true;
5025 if (this instanceof AST_New && global_pure_constructors[expr.name]) return true;
5026 }
5027 if (is_static_fn(expr)) return true;
5028 }
5029 return compressor.option("annotations") && this.pure || !compressor.pure_funcs(this);
5030 });
5031 AST_Template.DEFMETHOD("is_expr_pure", function(compressor) {
5032 var tag = this.tag;
5033 if (!tag) return true;
5034 if (compressor.option("unsafe")) {
5035 if (is_undeclared_ref(tag) && global_pure_fns[tag.name]) return true;
5036 if (tag instanceof AST_Dot && is_undeclared_ref(tag.expression)) {
5037 var static_fn = static_fns[tag.expression.name];
5038 return static_fn && (static_fn[tag.property]
5039 || tag.expression.name == "Math" && tag.property == "random");
5040 }
5041 }
5042 return !compressor.pure_funcs(this);
5043 });
5044 AST_Node.DEFMETHOD("is_call_pure", return_false);
5045 AST_Call.DEFMETHOD("is_call_pure", function(compressor) {
5046 if (!compressor.option("unsafe")) return false;
5047 var dot = this.expression;
5048 if (!(dot instanceof AST_Dot)) return false;
5049 var exp = dot.expression;
5050 var map;
5051 var prop = dot.property;
5052 if (exp instanceof AST_Array) {
5053 map = native_fns.Array;
5054 } else if (exp.is_boolean(compressor)) {
5055 map = native_fns.Boolean;
5056 } else if (exp.is_number(compressor)) {
5057 map = native_fns.Number;
5058 } else if (exp instanceof AST_RegExp) {
5059 map = native_fns.RegExp;
5060 } else if (exp.is_string(compressor)) {
5061 map = native_fns.String;
5062 if (prop == "replace") {
5063 var arg = this.args[1];
5064 if (arg && !arg.is_string(compressor)) return false;
5065 }
5066 } else if (!dot.may_throw_on_access(compressor)) {
5067 map = native_fns.Object;
5068 }
5069 return map && map[prop];
5070 });
5071
5072 function spread_side_effects(exp) {
5073 while ((exp = exp.tail_node()) instanceof AST_SymbolRef) {
5074 exp = exp.fixed_value();
5075 if (!exp) return true;
5076 }
5077 return !(exp instanceof AST_Array
5078 || exp.TYPE == "Binary" && !lazy_op[exp.operator]
5079 || exp instanceof AST_Constant
5080 || exp instanceof AST_Lambda
5081 || exp instanceof AST_Object && all(exp.properties, function(prop) {
5082 return !(prop instanceof AST_ObjectGetter || prop instanceof AST_Spread);
5083 })
5084 || exp instanceof AST_ObjectIdentity
5085 || exp instanceof AST_Unary);
5086 }
5087
5088 // determine if expression has side effects
5089 (function(def) {
5090 function any(list, compressor, spread) {
5091 return !all(list, spread ? function(node) {
5092 return node instanceof AST_Spread ? !spread(node, compressor) : !node.has_side_effects(compressor);
5093 } : function(node) {
5094 return !node.has_side_effects(compressor);
5095 });
5096 }
5097 function array_spread(node, compressor) {
5098 return !node.expression.is_string(compressor) || node.expression.has_side_effects(compressor);
5099 }
5100 def(AST_Node, return_true);
5101 def(AST_Array, function(compressor) {
5102 return any(this.elements, compressor, array_spread);
5103 });
5104 def(AST_Assign, function(compressor) {
5105 var lhs = this.left;
5106 if (!(lhs instanceof AST_PropAccess)) return true;
5107 var node = lhs.expression;
5108 return !(node instanceof AST_ObjectIdentity)
5109 || !node.scope.resolve().new
5110 || lhs instanceof AST_Sub && lhs.property.has_side_effects(compressor)
5111 || this.right.has_side_effects(compressor);
5112 });
5113 def(AST_Binary, function(compressor) {
5114 return this.left.has_side_effects(compressor)
5115 || this.right.has_side_effects(compressor)
5116 || this.operator == "in" && !is_object(this.right);
5117 });
5118 def(AST_Block, function(compressor) {
5119 return any(this.body, compressor);
5120 });
5121 def(AST_Call, function(compressor) {
5122 if (!this.is_expr_pure(compressor)
5123 && (!this.is_call_pure(compressor) || this.expression.has_side_effects(compressor))) {
5124 return true;
5125 }
5126 return any(this.args, compressor, array_spread);
5127 });
5128 def(AST_Case, function(compressor) {
5129 return this.expression.has_side_effects(compressor)
5130 || any(this.body, compressor);
5131 });
5132 def(AST_Class, function(compressor) {
5133 var base = this.extends;
5134 if (base) {
5135 if (base instanceof AST_SymbolRef) base = base.fixed_value();
5136 if (!safe_for_extends(base)) return true;
5137 }
5138 return any(this.properties, compressor);
5139 });
5140 def(AST_ClassProperty, function(compressor) {
5141 return this.key instanceof AST_Node && this.key.has_side_effects(compressor)
5142 || this.static && this.value && this.value.has_side_effects(compressor);
5143 });
5144 def(AST_Conditional, function(compressor) {
5145 return this.condition.has_side_effects(compressor)
5146 || this.consequent.has_side_effects(compressor)
5147 || this.alternative.has_side_effects(compressor);
5148 });
5149 def(AST_Constant, return_false);
5150 def(AST_Definitions, function(compressor) {
5151 return any(this.definitions, compressor);
5152 });
5153 def(AST_DestructuredArray, function(compressor) {
5154 return any(this.elements, compressor);
5155 });
5156 def(AST_DestructuredKeyVal, function(compressor) {
5157 return this.key instanceof AST_Node && this.key.has_side_effects(compressor)
5158 || this.value.has_side_effects(compressor);
5159 });
5160 def(AST_DestructuredObject, function(compressor) {
5161 return any(this.properties, compressor);
5162 });
5163 def(AST_Dot, function(compressor) {
5164 return !this.optional && this.expression.may_throw_on_access(compressor)
5165 || this.expression.has_side_effects(compressor);
5166 });
5167 def(AST_EmptyStatement, return_false);
5168 def(AST_If, function(compressor) {
5169 return this.condition.has_side_effects(compressor)
5170 || this.body && this.body.has_side_effects(compressor)
5171 || this.alternative && this.alternative.has_side_effects(compressor);
5172 });
5173 def(AST_LabeledStatement, function(compressor) {
5174 return this.body.has_side_effects(compressor);
5175 });
5176 def(AST_Lambda, return_false);
5177 def(AST_Object, function(compressor) {
5178 return any(this.properties, compressor, function(node, compressor) {
5179 var exp = node.expression;
5180 return spread_side_effects(exp) || exp.has_side_effects(compressor);
5181 });
5182 });
5183 def(AST_ObjectIdentity, return_false);
5184 def(AST_ObjectProperty, function(compressor) {
5185 return this.key instanceof AST_Node && this.key.has_side_effects(compressor)
5186 || this.value.has_side_effects(compressor);
5187 });
5188 def(AST_Sequence, function(compressor) {
5189 return any(this.expressions, compressor);
5190 });
5191 def(AST_SimpleStatement, function(compressor) {
5192 return this.body.has_side_effects(compressor);
5193 });
5194 def(AST_Sub, function(compressor) {
5195 return !this.optional && this.expression.may_throw_on_access(compressor)
5196 || this.expression.has_side_effects(compressor)
5197 || this.property.has_side_effects(compressor);
5198 });
5199 def(AST_Switch, function(compressor) {
5200 return this.expression.has_side_effects(compressor)
5201 || any(this.body, compressor);
5202 });
5203 def(AST_SymbolDeclaration, return_false);
5204 def(AST_SymbolRef, function(compressor) {
5205 return !this.is_declared(compressor) || !can_drop_symbol(this, compressor);
5206 });
5207 def(AST_Template, function(compressor) {
5208 return !this.is_expr_pure(compressor) || any(this.expressions, compressor);
5209 });
5210 def(AST_Try, function(compressor) {
5211 return any(this.body, compressor)
5212 || this.bcatch && this.bcatch.has_side_effects(compressor)
5213 || this.bfinally && this.bfinally.has_side_effects(compressor);
5214 });
5215 def(AST_Unary, function(compressor) {
5216 return unary_side_effects[this.operator]
5217 || this.expression.has_side_effects(compressor);
5218 });
5219 def(AST_VarDef, function() {
5220 return this.value;
5221 });
5222 })(function(node, func) {
5223 node.DEFMETHOD("has_side_effects", func);
5224 });
5225
5226 // determine if expression may throw
5227 (function(def) {
5228 def(AST_Node, return_true);
5229
5230 def(AST_Constant, return_false);
5231 def(AST_Destructured, return_true);
5232 def(AST_EmptyStatement, return_false);
5233 def(AST_Lambda, return_false);
5234 def(AST_ObjectIdentity, return_false);
5235 def(AST_SymbolDeclaration, return_false);
5236
5237 function any(list, compressor) {
5238 for (var i = list.length; --i >= 0;)
5239 if (list[i].may_throw(compressor))
5240 return true;
5241 return false;
5242 }
5243
5244 function call_may_throw(exp, compressor) {
5245 if (exp.may_throw(compressor)) return true;
5246 if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
5247 if (!(exp instanceof AST_Lambda)) return true;
5248 if (any(exp.argnames, compressor)) return true;
5249 if (any(exp.body, compressor)) return true;
5250 return is_arrow(exp) && exp.value && exp.value.may_throw(compressor);
5251 }
5252
5253 def(AST_Array, function(compressor) {
5254 return any(this.elements, compressor);
5255 });
5256 def(AST_Assign, function(compressor) {
5257 if (this.right.may_throw(compressor)) return true;
5258 if (!compressor.has_directive("use strict")
5259 && this.operator == "="
5260 && this.left instanceof AST_SymbolRef) {
5261 return false;
5262 }
5263 return this.left.may_throw(compressor);
5264 });
5265 def(AST_Binary, function(compressor) {
5266 return this.left.may_throw(compressor)
5267 || this.right.may_throw(compressor)
5268 || this.operator == "in" && !is_object(this.right);
5269 });
5270 def(AST_Block, function(compressor) {
5271 return any(this.body, compressor);
5272 });
5273 def(AST_Call, function(compressor) {
5274 if (any(this.args, compressor)) return true;
5275 if (this.is_expr_pure(compressor)) return false;
5276 this.may_throw = return_true;
5277 var ret = call_may_throw(this.expression, compressor);
5278 delete this.may_throw;
5279 return ret;
5280 });
5281 def(AST_Case, function(compressor) {
5282 return this.expression.may_throw(compressor)
5283 || any(this.body, compressor);
5284 });
5285 def(AST_Conditional, function(compressor) {
5286 return this.condition.may_throw(compressor)
5287 || this.consequent.may_throw(compressor)
5288 || this.alternative.may_throw(compressor);
5289 });
5290 def(AST_DefaultValue, function(compressor) {
5291 return this.name.may_throw(compressor)
5292 || this.value && this.value.may_throw(compressor);
5293 });
5294 def(AST_Definitions, function(compressor) {
5295 return any(this.definitions, compressor);
5296 });
5297 def(AST_Dot, function(compressor) {
5298 return !this.optional && this.expression.may_throw_on_access(compressor)
5299 || this.expression.may_throw(compressor);
5300 });
5301 def(AST_If, function(compressor) {
5302 return this.condition.may_throw(compressor)
5303 || this.body && this.body.may_throw(compressor)
5304 || this.alternative && this.alternative.may_throw(compressor);
5305 });
5306 def(AST_LabeledStatement, function(compressor) {
5307 return this.body.may_throw(compressor);
5308 });
5309 def(AST_Object, function(compressor) {
5310 return any(this.properties, compressor);
5311 });
5312 def(AST_ObjectProperty, function(compressor) {
5313 return this.value.may_throw(compressor)
5314 || this.key instanceof AST_Node && this.key.may_throw(compressor);
5315 });
5316 def(AST_Return, function(compressor) {
5317 return this.value && this.value.may_throw(compressor);
5318 });
5319 def(AST_Sequence, function(compressor) {
5320 return any(this.expressions, compressor);
5321 });
5322 def(AST_SimpleStatement, function(compressor) {
5323 return this.body.may_throw(compressor);
5324 });
5325 def(AST_Sub, function(compressor) {
5326 return !this.optional && this.expression.may_throw_on_access(compressor)
5327 || this.expression.may_throw(compressor)
5328 || this.property.may_throw(compressor);
5329 });
5330 def(AST_Switch, function(compressor) {
5331 return this.expression.may_throw(compressor)
5332 || any(this.body, compressor);
5333 });
5334 def(AST_SymbolRef, function(compressor) {
5335 return !this.is_declared(compressor) || !can_drop_symbol(this, compressor);
5336 });
5337 def(AST_Template, function(compressor) {
5338 if (any(this.expressions, compressor)) return true;
5339 if (this.is_expr_pure(compressor)) return false;
5340 if (!this.tag) return false;
5341 this.may_throw = return_true;
5342 var ret = call_may_throw(this.tag, compressor);
5343 delete this.may_throw;
5344 return ret;
5345 });
5346 def(AST_Try, function(compressor) {
5347 return (this.bcatch ? this.bcatch.may_throw(compressor) : any(this.body, compressor))
5348 || this.bfinally && this.bfinally.may_throw(compressor);
5349 });
5350 def(AST_Unary, function(compressor) {
5351 return this.expression.may_throw(compressor)
5352 && !(this.operator == "typeof" && this.expression instanceof AST_SymbolRef);
5353 });
5354 def(AST_VarDef, function(compressor) {
5355 return this.name.may_throw(compressor)
5356 || this.value && this.value.may_throw(compressor);
5357 });
5358 })(function(node, func) {
5359 node.DEFMETHOD("may_throw", func);
5360 });
5361
5362 // determine if expression is constant
5363 (function(def) {
5364 function all_constant(list, scope) {
5365 for (var i = list.length; --i >= 0;)
5366 if (!list[i].is_constant_expression(scope))
5367 return false;
5368 return true;
5369 }
5370 def(AST_Node, return_false);
5371 def(AST_Array, function(scope) {
5372 return all_constant(this.elements, scope);
5373 });
5374 def(AST_Binary, function(scope) {
5375 return this.left.is_constant_expression(scope)
5376 && this.right.is_constant_expression(scope)
5377 && (this.operator != "in" || is_object(this.right));
5378 });
5379 def(AST_Class, function(scope) {
5380 var base = this.extends;
5381 if (base && !safe_for_extends(base)) return false;
5382 return all_constant(this.properties, scope);
5383 });
5384 def(AST_ClassProperty, function(scope) {
5385 return typeof this.key == "string" && (!this.value || this.value.is_constant_expression(scope));
5386 });
5387 def(AST_Constant, return_true);
5388 def(AST_Lambda, function(scope) {
5389 var self = this;
5390 var result = true;
5391 var scopes = [];
5392 self.walk(new TreeWalker(function(node, descend) {
5393 if (!result) return true;
5394 if (node instanceof AST_BlockScope) {
5395 if (node === self) return;
5396 scopes.push(node);
5397 descend();
5398 scopes.pop();
5399 return true;
5400 }
5401 if (node instanceof AST_SymbolRef) {
5402 if (self.inlined || node.redef) {
5403 result = false;
5404 return true;
5405 }
5406 if (self.variables.has(node.name)) return true;
5407 var def = node.definition();
5408 if (member(def.scope, scopes)) return true;
5409 if (scope && !def.redefined()) {
5410 var scope_def = scope.find_variable(node.name);
5411 if (scope_def ? scope_def === def : def.undeclared) {
5412 result = "f";
5413 return true;
5414 }
5415 }
5416 result = false;
5417 return true;
5418 }
5419 if (node instanceof AST_ObjectIdentity) {
5420 if (is_arrow(self) && all(scopes, function(s) {
5421 return !(s instanceof AST_Scope) || is_arrow(s);
5422 })) result = false;
5423 return true;
5424 }
5425 }));
5426 return result;
5427 });
5428 def(AST_Object, function(scope) {
5429 return all_constant(this.properties, scope);
5430 });
5431 def(AST_ObjectProperty, function(scope) {
5432 return typeof this.key == "string" && this.value.is_constant_expression(scope);
5433 });
5434 def(AST_Unary, function(scope) {
5435 return this.expression.is_constant_expression(scope);
5436 });
5437 })(function(node, func) {
5438 node.DEFMETHOD("is_constant_expression", func);
5439 });
5440
5441 // tell me if a statement aborts
5442 function aborts(thing) {
5443 return thing && thing.aborts();
5444 }
5445 (function(def) {
5446 def(AST_Statement, return_null);
5447 def(AST_Jump, return_this);
5448 function block_aborts() {
5449 var n = this.body.length;
5450 return n > 0 && aborts(this.body[n - 1]);
5451 }
5452 def(AST_BlockStatement, block_aborts);
5453 def(AST_SwitchBranch, block_aborts);
5454 def(AST_If, function() {
5455 return this.alternative && aborts(this.body) && aborts(this.alternative) && this;
5456 });
5457 })(function(node, func) {
5458 node.DEFMETHOD("aborts", func);
5459 });
5460
5461 /* -----[ optimizers ]----- */
5462
5463 var directives = makePredicate(["use asm", "use strict"]);
5464 OPT(AST_Directive, function(self, compressor) {
5465 if (compressor.option("directives")
5466 && (!directives[self.value] || compressor.has_directive(self.value) !== self)) {
5467 return make_node(AST_EmptyStatement, self);
5468 }
5469 return self;
5470 });
5471
5472 OPT(AST_Debugger, function(self, compressor) {
5473 if (compressor.option("drop_debugger"))
5474 return make_node(AST_EmptyStatement, self);
5475 return self;
5476 });
5477
5478 OPT(AST_LabeledStatement, function(self, compressor) {
5479 if (compressor.option("dead_code")
5480 && self.body instanceof AST_Break
5481 && compressor.loopcontrol_target(self.body) === self.body) {
5482 return make_node(AST_EmptyStatement, self);
5483 }
5484 return compressor.option("unused") && self.label.references.length == 0 ? self.body : self;
5485 });
5486
5487 OPT(AST_LoopControl, function(self, compressor) {
5488 if (!compressor.option("dead_code")) return self;
5489 var label = self.label;
5490 if (label) {
5491 var lct = compressor.loopcontrol_target(self);
5492 self.label = null;
5493 if (compressor.loopcontrol_target(self) === lct) {
5494 remove(label.thedef.references, self);
5495 } else {
5496 self.label = label;
5497 }
5498 }
5499 return self;
5500 });
5501
5502 OPT(AST_Block, function(self, compressor) {
5503 self.body = tighten_body(self.body, compressor);
5504 return self;
5505 });
5506
5507 function trim_block(node, parent, in_list) {
5508 switch (node.body.length) {
5509 case 0:
5510 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
5511 case 1:
5512 var stat = node.body[0];
5513 if (!safe_to_trim(stat)) return node;
5514 if (parent instanceof AST_IterationStatement && stat instanceof AST_LambdaDefinition) return node;
5515 return stat;
5516 }
5517 return node;
5518 }
5519
5520 OPT(AST_BlockStatement, function(self, compressor) {
5521 self.body = tighten_body(self.body, compressor);
5522 return trim_block(self, compressor.parent());
5523 });
5524
5525 function drop_rest_farg(fn, compressor) {
5526 if (!compressor.option("rests")) return;
5527 if (fn.uses_arguments) return;
5528 if (!(fn.rest instanceof AST_DestructuredArray)) return;
5529 if (!compressor.drop_fargs(fn, compressor.parent())) return;
5530 fn.argnames = fn.argnames.concat(fn.rest.elements);
5531 fn.rest = fn.rest.rest;
5532 }
5533
5534 OPT(AST_Lambda, function(self, compressor) {
5535 drop_rest_farg(self, compressor);
5536 self.body = tighten_body(self.body, compressor);
5537 return self;
5538 });
5539
5540 function opt_arrow(self, compressor) {
5541 if (!compressor.option("arrows")) return self;
5542 drop_rest_farg(self, compressor);
5543 var body = tighten_body(self.value ? [ self.first_statement() ] : self.body, compressor);
5544 switch (body.length) {
5545 case 1:
5546 var stat = body[0];
5547 if (stat instanceof AST_Return) {
5548 self.body.length = 0;
5549 self.value = stat.value;
5550 break;
5551 }
5552 default:
5553 self.body = body;
5554 self.value = null;
5555 break;
5556 }
5557 return self;
5558 }
5559 OPT(AST_Arrow, opt_arrow);
5560 OPT(AST_AsyncArrow, opt_arrow);
5561
5562 OPT(AST_Function, function(self, compressor) {
5563 drop_rest_farg(self, compressor);
5564 self.body = tighten_body(self.body, compressor);
5565 var parent = compressor.parent();
5566 if (compressor.option("inline")) for (var i = 0; i < self.body.length; i++) {
5567 var stat = self.body[i];
5568 if (stat instanceof AST_Directive) continue;
5569 if (stat instanceof AST_Return) {
5570 if (i != self.body.length - 1) break;
5571 var call = stat.value;
5572 if (!call || call.TYPE != "Call") break;
5573 if (call.is_expr_pure(compressor)) break;
5574 var fn = call.expression;
5575 if (fn instanceof AST_SymbolRef) {
5576 if (self.name && self.name.definition() === fn.definition()) break;
5577 fn = fn.fixed_value();
5578 }
5579 if (!(fn instanceof AST_Defun || fn instanceof AST_Function)) break;
5580 if (fn.rest) break;
5581 if (fn.uses_arguments) break;
5582 if (fn === call.expression) {
5583 if (fn.parent_scope !== self) break;
5584 if (!all(fn.enclosed, function(def) {
5585 return def.scope !== self;
5586 })) break;
5587 }
5588 if (fn.name
5589 && (parent instanceof AST_ClassMethod || parent instanceof AST_ObjectMethod)
5590 && parent.value === compressor.self()) break;
5591 if (fn.contains_this()) break;
5592 var len = fn.argnames.length;
5593 if (len > 0 && compressor.option("inline") < 2) break;
5594 if (len > self.argnames.length) break;
5595 if (!all(self.argnames, function(argname) {
5596 return argname instanceof AST_SymbolFunarg;
5597 })) break;
5598 if (!all(call.args, function(arg) {
5599 return !(arg instanceof AST_Spread);
5600 })) break;
5601 for (var j = 0; j < len; j++) {
5602 var arg = call.args[j];
5603 if (!(arg instanceof AST_SymbolRef)) break;
5604 if (arg.definition() !== self.argnames[j].definition()) break;
5605 }
5606 if (j < len) break;
5607 for (; j < call.args.length; j++) {
5608 if (call.args[j].has_side_effects(compressor)) break;
5609 }
5610 if (j < call.args.length) break;
5611 if (len < self.argnames.length && !compressor.drop_fargs(self, parent)) {
5612 if (!compressor.drop_fargs(fn, call)) break;
5613 do {
5614 fn.argnames.push(fn.make_var(AST_SymbolFunarg, fn, "argument_" + len));
5615 } while (++len < self.argnames.length);
5616 }
5617 return call.expression;
5618 }
5619 break;
5620 }
5621 return self;
5622 });
5623
5624 var NO_MERGE = makePredicate("arguments await yield");
5625 AST_Scope.DEFMETHOD("merge_variables", function(compressor) {
5626 if (!compressor.option("merge_vars")) return;
5627 var in_try, root, segment = {}, self = this;
5628 var first = [], last = [], index = 0;
5629 var declarations = new Dictionary();
5630 var references = Object.create(null);
5631 var prev = Object.create(null);
5632 var tw = new TreeWalker(function(node, descend) {
5633 if (node instanceof AST_Assign) {
5634 var lhs = node.left;
5635 var rhs = node.right;
5636 if (lhs instanceof AST_Destructured) {
5637 rhs.walk(tw);
5638 var marker = new TreeWalker(function(node) {
5639 if (node instanceof AST_Destructured) return;
5640 if (node instanceof AST_DefaultValue) {
5641 push();
5642 node.value.walk(tw);
5643 pop();
5644 node.name.walk(marker);
5645 } else if (node instanceof AST_DestructuredKeyVal) {
5646 if (node.key instanceof AST_Node) {
5647 push();
5648 segment.block = node;
5649 node.key.walk(tw);
5650 node.value.walk(marker);
5651 pop();
5652 } else {
5653 node.value.walk(marker);
5654 }
5655 } else if (node instanceof AST_SymbolRef) {
5656 mark(node);
5657 } else {
5658 node.walk(tw);
5659 }
5660 return true;
5661 });
5662 lhs.walk(marker);
5663 return true;
5664 }
5665 if (lazy_op[node.operator.slice(0, -1)]) {
5666 lhs.walk(tw);
5667 push();
5668 rhs.walk(tw);
5669 if (lhs instanceof AST_SymbolRef) mark(lhs);
5670 pop();
5671 return true;
5672 }
5673 if (lhs instanceof AST_SymbolRef) {
5674 if (node.operator != "=") mark(lhs, true);
5675 rhs.walk(tw);
5676 mark(lhs);
5677 return true;
5678 }
5679 return;
5680 }
5681 if (node instanceof AST_Binary) {
5682 if (!lazy_op[node.operator]) return;
5683 node.left.walk(tw);
5684 push();
5685 node.right.walk(tw);
5686 pop();
5687 return true;
5688 }
5689 if (node instanceof AST_Break) {
5690 var target = tw.loopcontrol_target(node);
5691 if (!(target instanceof AST_IterationStatement)) insert(target);
5692 return true;
5693 }
5694 if (node instanceof AST_Call) {
5695 var exp = node.expression;
5696 var tail = exp.tail_node();
5697 if (!is_lambda(tail)) {
5698 descend();
5699 return mark_expression(exp);
5700 }
5701 if (exp !== tail) exp.expressions.slice(0, -1).forEach(function(node) {
5702 node.walk(tw);
5703 });
5704 node.args.forEach(function(arg) {
5705 arg.walk(tw);
5706 });
5707 tail.walk(tw);
5708 return true;
5709 }
5710 if (node instanceof AST_Conditional) {
5711 node.condition.walk(tw);
5712 push();
5713 node.consequent.walk(tw);
5714 pop();
5715 push();
5716 node.alternative.walk(tw);
5717 pop();
5718 return true;
5719 }
5720 if (node instanceof AST_Continue) {
5721 var target = tw.loopcontrol_target(node);
5722 if (target instanceof AST_Do) insert(target);
5723 return true;
5724 }
5725 if (node instanceof AST_Do) {
5726 push();
5727 segment.block = node;
5728 segment.loop = true;
5729 var save = segment;
5730 node.body.walk(tw);
5731 if (segment.inserted === node) segment = save;
5732 node.condition.walk(tw);
5733 pop();
5734 return true;
5735 }
5736 if (node instanceof AST_For) {
5737 if (node.init) node.init.walk(tw);
5738 push();
5739 segment.block = node;
5740 segment.loop = true;
5741 if (node.condition) node.condition.walk(tw);
5742 node.body.walk(tw);
5743 if (node.step) node.step.walk(tw);
5744 pop();
5745 return true;
5746 }
5747 if (node instanceof AST_ForEnumeration) {
5748 node.object.walk(tw);
5749 push();
5750 segment.block = node;
5751 segment.loop = true;
5752 node.init.walk(tw);
5753 node.body.walk(tw);
5754 pop();
5755 return true;
5756 }
5757 if (node instanceof AST_If) {
5758 node.condition.walk(tw);
5759 push();
5760 node.body.walk(tw);
5761 pop();
5762 if (node.alternative) {
5763 push();
5764 node.alternative.walk(tw);
5765 pop();
5766 }
5767 return true;
5768 }
5769 if (node instanceof AST_LabeledStatement) {
5770 push();
5771 segment.block = node;
5772 var save = segment;
5773 node.body.walk(tw);
5774 if (segment.inserted === node) segment = save;
5775 pop();
5776 return true;
5777 }
5778 if (node instanceof AST_Scope) {
5779 push();
5780 segment.block = node;
5781 if (node === self) root = segment;
5782 if (node instanceof AST_Lambda) {
5783 if (node.name) references[node.name.definition().id] = false;
5784 var marker = node.uses_arguments && !tw.has_directive("use strict") ? function(node) {
5785 if (node instanceof AST_SymbolFunarg) references[node.definition().id] = false;
5786 } : function(node) {
5787 if (node instanceof AST_SymbolFunarg) mark(node);
5788 };
5789 var scanner = new TreeWalker(function(ref) {
5790 if (ref instanceof AST_SymbolDeclaration) references[ref.definition().id] = false;
5791 if (!(ref instanceof AST_SymbolRef)) return;
5792 var def = ref.definition();
5793 var ldef = node.variables.get(ref.name);
5794 if (ldef && (ldef === def
5795 || def.undeclared
5796 || node.parent_scope.find_variable(ref.name) === def)) {
5797 references[def.id] = false;
5798 references[ldef.id] = false;
5799 } else {
5800 var save = segment;
5801 pop();
5802 mark(ref, true);
5803 segment = save;
5804 }
5805 return true;
5806 });
5807 node.argnames.forEach(function(argname) {
5808 argname.mark_symbol(marker, scanner);
5809 });
5810 if (node.rest) node.rest.mark_symbol(marker, scanner);
5811 }
5812 walk_lambda(node, tw);
5813 pop();
5814 return true;
5815 }
5816 if (node instanceof AST_Sub) {
5817 var exp = node.expression;
5818 if (node.optional) {
5819 exp.walk(tw);
5820 push();
5821 node.property.walk(tw);
5822 pop();
5823 } else {
5824 descend();
5825 }
5826 return mark_expression(exp);
5827 }
5828 if (node instanceof AST_Switch) {
5829 node.expression.walk(tw);
5830 var save = segment;
5831 node.body.forEach(function(branch) {
5832 if (branch instanceof AST_Default) return;
5833 branch.expression.walk(tw);
5834 if (save === segment) push();
5835 });
5836 segment = save;
5837 node.body.forEach(function(branch) {
5838 push();
5839 segment.block = node;
5840 var save = segment;
5841 walk_body(branch, tw);
5842 if (segment.inserted === node) segment = save;
5843 pop();
5844 });
5845 return true;
5846 }
5847 if (node instanceof AST_SymbolConst || node instanceof AST_SymbolLet) {
5848 references[node.definition().id] = false;
5849 return true;
5850 }
5851 if (node instanceof AST_SymbolRef) {
5852 mark(node, true);
5853 return true;
5854 }
5855 if (node instanceof AST_Try) {
5856 var save_try = in_try;
5857 in_try = node;
5858 var save = segment;
5859 walk_body(node, tw);
5860 segment = save;
5861 if (node.bcatch) {
5862 if (node.bcatch.argname) node.bcatch.argname.mark_symbol(function(node) {
5863 if (node instanceof AST_SymbolCatch) {
5864 var def = node.definition();
5865 references[def.id] = false;
5866 if (def = def.redefined()) references[def.id] = false;
5867 }
5868 }, tw);
5869 if (node.bfinally || (in_try = save_try)) {
5870 walk_body(node.bcatch, tw);
5871 } else {
5872 push();
5873 walk_body(node.bcatch, tw);
5874 pop();
5875 }
5876 }
5877 in_try = save_try;
5878 segment = save;
5879 if (node.bfinally) node.bfinally.walk(tw);
5880 return true;
5881 }
5882 if (node instanceof AST_Unary) {
5883 if (!UNARY_POSTFIX[node.operator]) return;
5884 var sym = node.expression;
5885 if (!(sym instanceof AST_SymbolRef)) return;
5886 mark(sym, true);
5887 return true;
5888 }
5889 if (node instanceof AST_VarDef) {
5890 var assigned = node.value;
5891 if (assigned) {
5892 assigned.walk(tw);
5893 } else {
5894 assigned = segment.block instanceof AST_ForEnumeration && segment.block.init === tw.parent();
5895 }
5896 node.name.mark_symbol(assigned ? function(node) {
5897 if (!(node instanceof AST_SymbolDeclaration)) return;
5898 if (node instanceof AST_SymbolVar) {
5899 mark(node);
5900 } else {
5901 references[node.definition().id] = false;
5902 }
5903 return true;
5904 } : function(node) {
5905 if (!(node instanceof AST_SymbolDeclaration)) return;
5906 var id = node.definition().id;
5907 if (!(node instanceof AST_SymbolVar)) {
5908 references[id] = false;
5909 } else if (!(id in references)) {
5910 declarations.add(id, node);
5911 } else if (references[id]) {
5912 references[id].push(node);
5913 }
5914 return true;
5915 }, tw);
5916 return true;
5917 }
5918 if (node instanceof AST_While) {
5919 push();
5920 segment.block = node;
5921 segment.loop = true;
5922 descend();
5923 pop();
5924 return true;
5925 }
5926
5927 function mark_expression(exp) {
5928 if (compressor.option("ie")) {
5929 var sym = root_expr(exp);
5930 if (sym instanceof AST_SymbolRef) sym.walk(tw);
5931 }
5932 return true;
5933 }
5934 });
5935 tw.directives = Object.create(compressor.directives);
5936 self.walk(tw);
5937 var merged = Object.create(null);
5938 while (first.length && last.length) {
5939 var head = first.pop();
5940 var def = head.definition;
5941 if (!(def.id in prev)) continue;
5942 if (!references[def.id]) continue;
5943 var head_refs = {
5944 start: references[def.id].start,
5945 };
5946 while (def.id in merged) def = merged[def.id];
5947 head_refs.end = references[def.id].end;
5948 var skipped = [];
5949 do {
5950 var tail = last.pop();
5951 if (!tail) continue;
5952 if (tail.index > head.index) continue;
5953 var id = tail.definition.id;
5954 var tail_refs = references[id];
5955 if (!tail_refs) continue;
5956 if (head_refs.start.block !== tail_refs.start.block
5957 || !mergeable(head_refs, tail_refs)
5958 || (head_refs.start.loop || !same_scope(def)) && !mergeable(tail_refs, head_refs)
5959 || compressor.option("webkit") && is_funarg(def) !== is_funarg(tail.definition)
5960 || !all(tail_refs, function(sym) {
5961 return sym.scope.find_variable(def.name) === def;
5962 })) {
5963 skipped.unshift(tail);
5964 continue;
5965 }
5966 var orig = [], refs = [];
5967 tail_refs.forEach(function(sym) {
5968 sym.thedef = def;
5969 sym.name = def.name;
5970 if (sym instanceof AST_SymbolRef) {
5971 refs.push(sym);
5972 } else {
5973 orig.push(sym);
5974 }
5975 });
5976 def.orig = orig.concat(def.orig);
5977 def.references = refs.concat(def.references);
5978 def.fixed = tail.definition.fixed && def.fixed;
5979 merged[id] = def;
5980 break;
5981 } while (last.length);
5982 if (skipped.length) last = last.concat(skipped);
5983 }
5984
5985 function push() {
5986 segment = Object.create(segment);
5987 }
5988
5989 function pop() {
5990 segment = Object.getPrototypeOf(segment);
5991 }
5992
5993 function mark(sym, read) {
5994 var def = sym.definition(), ldef, seg = segment;
5995 if (in_try) {
5996 push();
5997 seg = segment;
5998 pop();
5999 }
6000 if (def.id in references) {
6001 var refs = references[def.id];
6002 if (!refs) return;
6003 if (refs.start.block !== seg.block) return references[def.id] = false;
6004 refs.push(sym);
6005 refs.end = seg;
6006 if (def.id in prev) {
6007 last[prev[def.id]] = null;
6008 } else if (!read) {
6009 return;
6010 }
6011 } else if ((ldef = self.variables.get(def.name)) !== def) {
6012 if (ldef && root === seg) references[ldef.id] = false;
6013 return references[def.id] = false;
6014 } else if (compressor.exposed(def) || NO_MERGE[sym.name]) {
6015 return references[def.id] = false;
6016 } else {
6017 var refs = declarations.get(def.id) || [];
6018 refs.push(sym);
6019 references[def.id] = refs;
6020 if (!read) {
6021 refs.start = seg;
6022 return first.push({
6023 index: index++,
6024 definition: def,
6025 });
6026 }
6027 if (seg.block !== self) return references[def.id] = false;
6028 refs.start = root;
6029 }
6030 prev[def.id] = last.length;
6031 last.push({
6032 index: index++,
6033 definition: def,
6034 });
6035 }
6036
6037 function insert(target) {
6038 var stack = [];
6039 while (true) {
6040 if (HOP(segment, "block")) {
6041 var block = segment.block;
6042 if (block instanceof AST_LabeledStatement) block = block.body;
6043 if (block === target) break;
6044 }
6045 stack.push(segment);
6046 pop();
6047 }
6048 segment.inserted = segment.block;
6049 push();
6050 while (stack.length) {
6051 var seg = stack.pop();
6052 push();
6053 if (HOP(seg, "block")) segment.block = seg.block;
6054 if (HOP(seg, "loop")) segment.loop = seg.loop;
6055 }
6056 }
6057
6058 function must_visit(base, segment) {
6059 return base === segment || base.isPrototypeOf(segment);
6060 }
6061
6062 function mergeable(head, tail) {
6063 return must_visit(head.start, head.end) || must_visit(head.start, tail.start);
6064 }
6065 });
6066
6067 function fill_holes(orig, elements) {
6068 for (var i = elements.length; --i >= 0;) {
6069 if (!elements[i]) elements[i] = make_node(AST_Hole, orig);
6070 }
6071 }
6072
6073 function to_class_expr(defcl, drop_name) {
6074 var cl = make_node(AST_ClassExpression, defcl, defcl);
6075 cl.name = drop_name ? null : make_node(AST_SymbolClass, defcl.name, defcl.name);
6076 return cl;
6077 }
6078
6079 function to_func_expr(defun, drop_name) {
6080 var ctor;
6081 switch (defun.CTOR) {
6082 case AST_AsyncDefun:
6083 ctor = AST_AsyncFunction;
6084 break;
6085 case AST_AsyncGeneratorDefun:
6086 ctor = AST_AsyncGeneratorFunction;
6087 break;
6088 case AST_Defun:
6089 ctor = AST_Function;
6090 break;
6091 case AST_GeneratorDefun:
6092 ctor = AST_GeneratorFunction;
6093 break;
6094 }
6095 var fn = make_node(ctor, defun, defun);
6096 fn.name = drop_name ? null : make_node(AST_SymbolLambda, defun.name, defun.name);
6097 return fn;
6098 }
6099
6100 AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
6101 if (!compressor.option("unused")) return;
6102 var self = this;
6103 var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs;
6104 var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars;
6105 var assign_as_unused = /keep_assign/.test(compressor.option("unused")) ? return_false : function(node, props) {
6106 var sym, nested = false;
6107 if (node instanceof AST_Assign) {
6108 if (node.write_only || node.operator == "=") sym = extract_reference(node.left, props);
6109 } else if (node instanceof AST_Unary) {
6110 if (node.write_only) sym = extract_reference(node.expression, props);
6111 }
6112 if (!(sym instanceof AST_SymbolRef)) return;
6113 var def = sym.definition();
6114 if (export_defaults[def.id]) return;
6115 if (compressor.exposed(def)) return;
6116 if (!can_drop_symbol(sym, compressor, nested)) return;
6117 return sym;
6118
6119 function extract_reference(node, props) {
6120 if (node instanceof AST_PropAccess) {
6121 var expr = node.expression;
6122 if (!expr.may_throw_on_access(compressor, true)) {
6123 nested = true;
6124 if (props && node instanceof AST_Sub) props.unshift(node.property);
6125 return extract_reference(expr, props);
6126 }
6127 } else if (node instanceof AST_Assign && node.operator == "=") {
6128 node.write_only = "p";
6129 var ref = extract_reference(node.right);
6130 if (!props) return ref;
6131 props.assign = node;
6132 return ref instanceof AST_SymbolRef ? ref : node.left;
6133 }
6134 return node;
6135 }
6136 };
6137 var assign_in_use = Object.create(null);
6138 var export_defaults = Object.create(null);
6139 var find_variable = function(name) {
6140 find_variable = compose(self, 0, noop);
6141 return find_variable(name);
6142
6143 function compose(child, level, find) {
6144 var parent = compressor.parent(level);
6145 if (!parent) return find;
6146 var in_arg = parent instanceof AST_Lambda && member(child, parent.argnames);
6147 return compose(parent, level + 1, in_arg ? function(name) {
6148 var def = find(name);
6149 if (def) return def;
6150 def = parent.variables.get(name);
6151 if (def) {
6152 var sym = def.orig[0];
6153 if (sym instanceof AST_SymbolFunarg || sym instanceof AST_SymbolLambda) return def;
6154 }
6155 } : parent.variables ? function(name) {
6156 return find(name) || parent.variables.get(name);
6157 } : find);
6158 }
6159 };
6160 var for_ins = Object.create(null);
6161 var in_use = [];
6162 var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use
6163 var value_read = Object.create(null);
6164 var value_modified = Object.create(null);
6165 var var_defs = Object.create(null);
6166 if (self instanceof AST_Toplevel && compressor.top_retain) {
6167 self.variables.each(function(def) {
6168 if (compressor.top_retain(def) && !(def.id in in_use_ids)) {
6169 in_use_ids[def.id] = true;
6170 in_use.push(def);
6171 }
6172 });
6173 }
6174 var assignments = new Dictionary();
6175 var initializations = new Dictionary();
6176 // pass 1: find out which symbols are directly used in
6177 // this scope (not in nested scopes).
6178 var scope = this;
6179 var tw = new TreeWalker(function(node, descend) {
6180 if (node instanceof AST_Lambda && node.uses_arguments && !tw.has_directive("use strict")) {
6181 node.each_argname(function(argname) {
6182 var def = argname.definition();
6183 if (!(def.id in in_use_ids)) {
6184 in_use_ids[def.id] = true;
6185 in_use.push(def);
6186 }
6187 });
6188 }
6189 if (node === self) return;
6190 if (scope === self) {
6191 if (node instanceof AST_DefClass) {
6192 var def = node.name.definition();
6193 if ((!drop_funcs || def.exported) && !(def.id in in_use_ids)) {
6194 in_use_ids[def.id] = true;
6195 in_use.push(def);
6196 }
6197 if (node.extends) node.extends.walk(tw);
6198 var is_export = false;
6199 if (tw.parent() instanceof AST_ExportDefault) {
6200 is_export = true;
6201 export_defaults[def.id] = true;
6202 }
6203 node.properties.forEach(function(prop) {
6204 if (prop.key instanceof AST_Node) prop.key.walk(tw);
6205 if (!prop.value) return;
6206 if (is_export || prop instanceof AST_ClassField && prop.static) {
6207 var save_scope = scope;
6208 scope = node;
6209 prop.value.walk(tw);
6210 scope = save_scope;
6211 } else {
6212 initializations.add(def.id, prop.value);
6213 }
6214 });
6215 return true;
6216 }
6217 if (node instanceof AST_LambdaDefinition) {
6218 var def = node.name.definition();
6219 if ((!drop_funcs || def.exported) && !(def.id in in_use_ids)) {
6220 in_use_ids[def.id] = true;
6221 in_use.push(def);
6222 }
6223 initializations.add(def.id, node);
6224 if (tw.parent() instanceof AST_ExportDefault) {
6225 export_defaults[def.id] = true;
6226 } else {
6227 return true;
6228 }
6229 }
6230 if (node instanceof AST_Definitions) {
6231 node.definitions.forEach(function(defn) {
6232 var value = defn.value;
6233 var side_effects = value
6234 && (defn.name instanceof AST_Destructured || value.has_side_effects(compressor));
6235 var shared = side_effects && value.tail_node().operator == "=";
6236 defn.name.mark_symbol(function(name) {
6237 if (!(name instanceof AST_SymbolDeclaration)) return;
6238 var def = name.definition();
6239 var_defs[def.id] = (var_defs[def.id] || 0) + 1;
6240 if (node instanceof AST_Var && def.orig[0] instanceof AST_SymbolCatch) {
6241 var redef = def.redefined();
6242 if (redef) var_defs[redef.id] = (var_defs[redef.id] || 0) + 1;
6243 }
6244 if (!(def.id in in_use_ids) && (!drop_vars || def.exported
6245 || (node instanceof AST_Const ? def.redefined() : def.const_redefs)
6246 || !(node instanceof AST_Var || is_safe_lexical(def)))) {
6247 in_use_ids[def.id] = true;
6248 in_use.push(def);
6249 }
6250 if (value) {
6251 if (!side_effects) {
6252 initializations.add(def.id, value);
6253 } else if (shared) {
6254 verify_safe_usage(def, name, value_modified[def.id]);
6255 }
6256 assignments.add(def.id, defn);
6257 }
6258 return true;
6259 }, tw);
6260 if (side_effects) value.walk(tw);
6261 });
6262 return true;
6263 }
6264 if (node instanceof AST_SymbolFunarg) {
6265 var def = node.definition();
6266 var_defs[def.id] = (var_defs[def.id] || 0) + 1;
6267 assignments.add(def.id, node);
6268 return true;
6269 }
6270 if (node instanceof AST_SymbolImport) {
6271 var def = node.definition();
6272 if (!(def.id in in_use_ids) && (!drop_vars || !is_safe_lexical(def))) {
6273 in_use_ids[def.id] = true;
6274 in_use.push(def);
6275 }
6276 return true;
6277 }
6278 }
6279 return scan_ref_scoped(node, descend, true);
6280 });
6281 tw.directives = Object.create(compressor.directives);
6282 self.walk(tw);
6283 var drop_fn_name = compressor.option("keep_fnames") ? return_false : compressor.option("ie") ? function(def) {
6284 return !compressor.exposed(def) && def.references.length == def.replaced;
6285 } : function(def) {
6286 if (!(def.id in in_use_ids)) return true;
6287 if (def.orig.length - def.eliminated < 2) return false;
6288 // function argument will always overshadow its name
6289 if (def.orig[1] instanceof AST_SymbolFunarg) return true;
6290 // retain if referenced within destructured object of argument
6291 return all(def.references, function(ref) {
6292 return !ref.in_arg;
6293 });
6294 };
6295 if (compressor.option("ie")) initializations.each(function(init, id) {
6296 if (id in in_use_ids) return;
6297 init.forEach(function(init) {
6298 init.walk(new TreeWalker(function(node) {
6299 if (node instanceof AST_Function && node.name && !drop_fn_name(node.name.definition())) {
6300 node.walk(tw);
6301 return true;
6302 }
6303 if (node instanceof AST_Scope) return true;
6304 }));
6305 });
6306 });
6307 // pass 2: for every used symbol we need to walk its
6308 // initialization code to figure out if it uses other
6309 // symbols (that may not be in_use).
6310 tw = new TreeWalker(scan_ref_scoped);
6311 for (var i = 0; i < in_use.length; i++) {
6312 var init = initializations.get(in_use[i].id);
6313 if (init) init.forEach(function(init) {
6314 init.walk(tw);
6315 });
6316 }
6317 Object.keys(assign_in_use).forEach(function(id) {
6318 var assigns = assign_in_use[id];
6319 if (!assigns) {
6320 delete assign_in_use[id];
6321 return;
6322 }
6323 assigns = assigns.reduce(function(in_use, assigns) {
6324 assigns.forEach(function(assign) {
6325 push_uniq(in_use, assign);
6326 });
6327 return in_use;
6328 }, []);
6329 var in_use = (assignments.get(id) || []).filter(function(node) {
6330 return find_if(node instanceof AST_Unary ? function(assign) {
6331 return assign === node;
6332 } : function(assign) {
6333 if (assign === node) return true;
6334 if (assign instanceof AST_Unary) return false;
6335 return get_rvalue(assign) === get_rvalue(node);
6336 }, assigns);
6337 });
6338 if (assigns.length == in_use.length) {
6339 assign_in_use[id] = in_use;
6340 } else {
6341 delete assign_in_use[id];
6342 }
6343 });
6344 // pass 3: we should drop declarations not in_use
6345 var trim_defns = [];
6346 var unused_fn_names = [];
6347 var calls_to_drop_args = [];
6348 var fns_with_marked_args = [];
6349 var trimmer = new TreeTransformer(function(node) {
6350 if (node instanceof AST_DefaultValue) return trim_default(trimmer, node);
6351 if (node instanceof AST_Destructured && node.rest) node.rest = node.rest.transform(trimmer);
6352 if (node instanceof AST_DestructuredArray) {
6353 var trim = !node.rest;
6354 for (var i = node.elements.length; --i >= 0;) {
6355 var element = node.elements[i].transform(trimmer);
6356 if (element) {
6357 node.elements[i] = element;
6358 trim = false;
6359 } else if (trim) {
6360 node.elements.pop();
6361 } else {
6362 node.elements[i] = make_node(AST_Hole, node.elements[i]);
6363 }
6364 }
6365 return node;
6366 }
6367 if (node instanceof AST_DestructuredObject) {
6368 var properties = [];
6369 node.properties.forEach(function(prop) {
6370 var retain = false;
6371 if (prop.key instanceof AST_Node) {
6372 prop.key = prop.key.transform(tt);
6373 retain = prop.key.has_side_effects(compressor);
6374 }
6375 if ((retain || node.rest) && is_decl(prop.value)) {
6376 prop.value = prop.value.transform(tt);
6377 properties.push(prop);
6378 } else {
6379 var value = prop.value.transform(trimmer);
6380 if (!value && node.rest) {
6381 if (prop.value instanceof AST_DestructuredArray) {
6382 value = make_node(AST_DestructuredArray, prop.value, { elements: [] });
6383 } else {
6384 value = make_node(AST_DestructuredObject, prop.value, { properties: [] });
6385 }
6386 }
6387 if (value) {
6388 prop.value = value;
6389 properties.push(prop);
6390 }
6391 }
6392 });
6393 node.properties = properties;
6394 return node;
6395 }
6396 if (node instanceof AST_SymbolDeclaration) return node.definition().id in in_use_ids ? node : null;
6397 });
6398 var tt = new TreeTransformer(function(node, descend, in_list) {
6399 var parent = tt.parent();
6400 if (drop_vars) {
6401 var props = [], sym = assign_as_unused(node, props);
6402 if (sym) {
6403 var value;
6404 if (can_drop_lhs(sym, node)) {
6405 if (node instanceof AST_Assign) {
6406 value = get_rhs(node);
6407 if (node.write_only === true) value = value.drop_side_effect_free(compressor);
6408 }
6409 if (!value) value = make_node(AST_Number, node, { value: 0 });
6410 }
6411 if (value) {
6412 if (props.assign) {
6413 var assign = props.assign.drop_side_effect_free(compressor);
6414 if (assign) {
6415 assign.write_only = true;
6416 props.unshift(assign);
6417 }
6418 }
6419 if (!(parent instanceof AST_Sequence)
6420 || parent.tail_node() === node
6421 || value.has_side_effects(compressor)) {
6422 props.push(value);
6423 }
6424 switch (props.length) {
6425 case 0:
6426 return List.skip;
6427 case 1:
6428 return maintain_this_binding(compressor, parent, node, props[0].transform(tt));
6429 default:
6430 return make_sequence(node, props.map(function(prop) {
6431 return prop.transform(tt);
6432 }));
6433 }
6434 }
6435 } else if (node instanceof AST_UnaryPostfix
6436 && node.expression instanceof AST_SymbolRef
6437 && indexOf_assign(node.expression.definition(), node) < 0) {
6438 return make_node(AST_UnaryPrefix, node, {
6439 operator: "+",
6440 expression: node.expression
6441 });
6442 }
6443 }
6444 if (node instanceof AST_Call) calls_to_drop_args.push(node);
6445 if (scope !== self) return;
6446 if (drop_funcs && node !== self && node instanceof AST_DefClass) {
6447 var def = node.name.definition();
6448 if (!(def.id in in_use_ids)) {
6449 log(node.name, "Dropping unused class {name}");
6450 def.eliminated++;
6451 descend(node, tt);
6452 if (parent instanceof AST_ExportDefault) return to_class_expr(node, true);
6453 var trimmed = node.drop_side_effect_free(compressor, true);
6454 if (trimmed === node) trimmed = to_class_expr(node, true);
6455 if (trimmed) return make_node(AST_SimpleStatement, node, { body: trimmed });
6456 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
6457 }
6458 }
6459 if (node instanceof AST_ClassExpression && node.name && drop_fn_name(node.name.definition())) {
6460 unused_fn_names.push(node);
6461 }
6462 if (node instanceof AST_Lambda) {
6463 if (drop_funcs && node !== self && node instanceof AST_LambdaDefinition) {
6464 var def = node.name.definition();
6465 if (!(def.id in in_use_ids)) {
6466 log(node.name, "Dropping unused function {name}");
6467 def.eliminated++;
6468 if (parent instanceof AST_ExportDefault) {
6469 descend_scope();
6470 return to_func_expr(node, true);
6471 }
6472 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
6473 }
6474 }
6475 if (node instanceof AST_LambdaExpression && node.name && drop_fn_name(node.name.definition())) {
6476 unused_fn_names.push(node);
6477 }
6478 if (!(node instanceof AST_Accessor)) {
6479 if (node.rest) {
6480 var rest = node.rest.transform(trimmer);
6481 if (rest instanceof AST_Destructured && !rest.rest
6482 && (!node.uses_arguments || tt.has_directive("use strict"))) {
6483 if (rest instanceof AST_DestructuredArray) {
6484 if (rest.elements.length == 0) rest = null;
6485 } else if (rest.properties.length == 0) {
6486 rest = null;
6487 }
6488 }
6489 node.rest = rest;
6490 }
6491 var argnames = node.argnames;
6492 var trim = compressor.drop_fargs(node, parent) && !node.rest;
6493 var default_length = trim ? -1 : node.length();
6494 for (var i = argnames.length; --i >= 0;) {
6495 var sym = argnames[i];
6496 if (!(sym instanceof AST_SymbolFunarg)) {
6497 var arg = sym.transform(trimmer);
6498 if (arg) {
6499 trim = false;
6500 } else if (trim) {
6501 log(sym.name, "Dropping unused default argument {name}");
6502 argnames.pop();
6503 } else if (i > default_length) {
6504 log(sym.name, "Dropping unused default argument assignment {name}");
6505 sym.name.__unused = true;
6506 argnames[i] = sym.name;
6507 } else {
6508 log(sym.name, "Dropping unused default argument value {name}");
6509 sym.value = make_node(AST_Number, sym, { value: 0 });
6510 }
6511 continue;
6512 }
6513 var def = sym.definition();
6514 if (def.id in in_use_ids) {
6515 trim = false;
6516 if (indexOf_assign(def, sym) < 0) sym.__unused = null;
6517 } else if (trim) {
6518 log(sym, "Dropping unused function argument {name}");
6519 argnames.pop();
6520 } else {
6521 sym.__unused = true;
6522 }
6523 }
6524 fns_with_marked_args.push(node);
6525 }
6526 }
6527 if (node instanceof AST_Catch && node.argname instanceof AST_Destructured) {
6528 node.argname.transform(trimmer);
6529 }
6530 if (node instanceof AST_Definitions && !(parent instanceof AST_ForEnumeration && parent.init === node)) {
6531 // place uninitialized names at the start
6532 var body = [], head = [], tail = [];
6533 // for unused names whose initialization has
6534 // side effects, we can cascade the init. code
6535 // into the next one, or next statement.
6536 var side_effects = [];
6537 var duplicated = 0;
6538 var is_var = node instanceof AST_Var;
6539 node.definitions.forEach(function(def) {
6540 if (def.value) def.value = def.value.transform(tt);
6541 var value = def.value;
6542 if (def.name instanceof AST_Destructured) {
6543 var trimmed = trim_destructured(def.name, value, function(node) {
6544 if (!drop_vars) return node;
6545 if (node.definition().id in in_use_ids) return node;
6546 if (is_catch(node)) return node;
6547 if (is_var && !can_drop_symbol(node)) return node;
6548 return null;
6549 }, true);
6550 if (trimmed.name) {
6551 def = make_node(AST_VarDef, def, {
6552 name: trimmed.name,
6553 value: value = trimmed.value,
6554 });
6555 flush();
6556 } else if (trimmed.value) {
6557 side_effects.push(trimmed.value);
6558 }
6559 return;
6560 }
6561 var sym = def.name.definition();
6562 var drop_sym = is_var ? can_drop_symbol(def.name) : is_safe_lexical(sym);
6563 if (!drop_sym || !drop_vars || sym.id in in_use_ids) {
6564 if (value && indexOf_assign(sym, def) < 0) {
6565 value = value.drop_side_effect_free(compressor);
6566 if (value) {
6567 AST_Node.warn("Side effects in last use of variable {name} [{file}:{line},{col}]", template(def.name));
6568 side_effects.push(value);
6569 }
6570 value = null;
6571 trim_defns.push(def);
6572 }
6573 var old_def;
6574 if (!value && !(node instanceof AST_Let)) {
6575 if (parent instanceof AST_ExportDeclaration) {
6576 flush();
6577 } else if (drop_sym && var_defs[sym.id] > 1) {
6578 AST_Node.info("Dropping declaration of variable {name} [{file}:{line},{col}]", template(def.name));
6579 var_defs[sym.id]--;
6580 sym.eliminated++;
6581 } else {
6582 head.push(def);
6583 }
6584 } else if (compressor.option("functions")
6585 && !compressor.option("ie")
6586 && drop_sym
6587 && var_defs[sym.id] == 1
6588 && sym.assignments == 0
6589 && value instanceof AST_LambdaExpression
6590 && !is_arguments(sym)
6591 && !is_arrow(value)
6592 && assigned_once(value, sym.references)
6593 && can_declare_defun(value)
6594 && (old_def = rename_def(value, def.name.name)) !== false) {
6595 AST_Node.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name));
6596 var ctor;
6597 switch (value.CTOR) {
6598 case AST_AsyncFunction:
6599 ctor = AST_AsyncDefun;
6600 break;
6601 case AST_AsyncGeneratorFunction:
6602 ctor = AST_AsyncGeneratorDefun;
6603 break;
6604 case AST_Function:
6605 ctor = AST_Defun;
6606 break;
6607 case AST_GeneratorFunction:
6608 ctor = AST_GeneratorDefun;
6609 break;
6610 }
6611 var defun = make_node(ctor, def, value);
6612 defun.name = make_node(AST_SymbolDefun, def.name, def.name);
6613 var name_def = def.name.scope.resolve().def_function(defun.name);
6614 if (old_def) old_def.forEach(function(node) {
6615 node.name = name_def.name;
6616 node.thedef = name_def;
6617 node.reference();
6618 });
6619 body.push(defun);
6620 } else {
6621 if (drop_sym
6622 && var_defs[sym.id] > 1
6623 && !(parent instanceof AST_ExportDeclaration)
6624 && sym.orig.indexOf(def.name) > sym.eliminated) {
6625 var_defs[sym.id]--;
6626 duplicated++;
6627 }
6628 flush();
6629 }
6630 } else if (is_catch(def.name)) {
6631 value = value && value.drop_side_effect_free(compressor);
6632 if (value) side_effects.push(value);
6633 if (var_defs[sym.id] > 1) {
6634 AST_Node.warn("Dropping duplicated declaration of variable {name} [{file}:{line},{col}]", template(def.name));
6635 var_defs[sym.id]--;
6636 sym.eliminated++;
6637 } else {
6638 def.value = null;
6639 head.push(def);
6640 }
6641 } else {
6642 value = value && !value.single_use && value.drop_side_effect_free(compressor);
6643 if (value) {
6644 AST_Node.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
6645 side_effects.push(value);
6646 } else {
6647 log(def.name, "Dropping unused variable {name}");
6648 }
6649 sym.eliminated++;
6650 }
6651
6652 function assigned_once(fn, refs) {
6653 if (refs.length == 0) return fn === def.name.fixed_value();
6654 return all(refs, function(ref) {
6655 return fn === ref.fixed_value();
6656 });
6657 }
6658
6659 function can_declare_defun(fn) {
6660 if (!is_var || compressor.has_directive("use strict") || !(fn instanceof AST_Function)) {
6661 return parent instanceof AST_Scope;
6662 }
6663 return parent instanceof AST_Block
6664 || parent instanceof AST_For && parent.init === node
6665 || parent instanceof AST_If;
6666 }
6667
6668 function rename_def(fn, name) {
6669 if (!fn.name) return null;
6670 var def = fn.name.definition();
6671 if (def.orig.length > 1) return null;
6672 if (def.assignments > 0) return false;
6673 if (def.name == name) return def;
6674 var forbidden;
6675 switch (name) {
6676 case "await":
6677 forbidden = is_async;
6678 break;
6679 case "yield":
6680 forbidden = is_generator;
6681 break;
6682 }
6683 return all(def.references, function(ref) {
6684 var scope = ref.scope;
6685 if (scope.find_variable(name) !== sym) return false;
6686 if (forbidden) do {
6687 scope = scope.resolve();
6688 if (forbidden(scope)) return false;
6689 } while (scope !== fn && (scope = scope.parent_scope));
6690 return true;
6691 }) && def;
6692 }
6693
6694 function is_catch(node) {
6695 var sym = node.definition();
6696 return sym.orig[0] instanceof AST_SymbolCatch && sym.scope.resolve() === node.scope.resolve();
6697 }
6698
6699 function flush() {
6700 if (side_effects.length > 0) {
6701 if (tail.length == 0) {
6702 body.push(make_node(AST_SimpleStatement, node, {
6703 body: make_sequence(node, side_effects),
6704 }));
6705 } else if (value) {
6706 side_effects.push(value);
6707 def.value = make_sequence(value, side_effects);
6708 } else {
6709 def.value = make_node(AST_UnaryPrefix, def, {
6710 operator: "void",
6711 expression: make_sequence(def, side_effects),
6712 });
6713 }
6714 side_effects = [];
6715 }
6716 tail.push(def);
6717 }
6718 });
6719 switch (head.length) {
6720 case 0:
6721 if (tail.length == 0) break;
6722 if (tail.length == duplicated) {
6723 [].unshift.apply(side_effects, tail.map(function(def) {
6724 AST_Node.info("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
6725 var sym = def.name.definition();
6726 var ref = make_node(AST_SymbolRef, def.name, def.name);
6727 sym.references.push(ref);
6728 var assign = make_node(AST_Assign, def, {
6729 operator: "=",
6730 left: ref,
6731 right: def.value
6732 });
6733 var index = indexOf_assign(sym, def);
6734 if (index >= 0) assign_in_use[sym.id][index] = assign;
6735 sym.eliminated++;
6736 return assign;
6737 }));
6738 break;
6739 }
6740 case 1:
6741 if (tail.length == 0) {
6742 var id = head[0].name.definition().id;
6743 if (id in for_ins) {
6744 node.definitions = head;
6745 for_ins[id].init = node;
6746 break;
6747 }
6748 }
6749 default:
6750 node.definitions = head.concat(tail);
6751 body.push(node);
6752 }
6753 if (side_effects.length > 0) {
6754 body.push(make_node(AST_SimpleStatement, node, {
6755 body: make_sequence(node, side_effects)
6756 }));
6757 }
6758 return insert_statements(body, node, in_list);
6759 }
6760 if (node instanceof AST_Assign) {
6761 descend(node, tt);
6762 if (!(node.left instanceof AST_Destructured)) return node;
6763 var trimmed = trim_destructured(node.left, node.right, function(node) {
6764 return node;
6765 }, node.write_only === true);
6766 if (trimmed.name) return make_node(AST_Assign, node, {
6767 operator: node.operator,
6768 left: trimmed.name,
6769 right: trimmed.value,
6770 });
6771 if (trimmed.value) return trimmed.value;
6772 if (parent instanceof AST_Sequence && parent.tail_node() !== node) return List.skip;
6773 return make_node(AST_Number, node, { value: 0 });
6774 }
6775 if (node instanceof AST_LabeledStatement && node.body instanceof AST_For) {
6776 // Certain combination of unused name + side effect leads to invalid AST:
6777 // https://github.com/mishoo/UglifyJS/issues/1830
6778 // We fix it at this stage by moving the label inwards, back to the `for`.
6779 descend(node, tt);
6780 if (node.body instanceof AST_BlockStatement) {
6781 var block = node.body;
6782 node.body = block.body.pop();
6783 block.body.push(node);
6784 return in_list ? List.splice(block.body) : block;
6785 }
6786 return node;
6787 }
6788 if (node instanceof AST_Scope) {
6789 descend_scope();
6790 return node;
6791 }
6792 if (node instanceof AST_SymbolImport) {
6793 if (!compressor.option("imports") || node.definition().id in in_use_ids) return node;
6794 return in_list ? List.skip : null;
6795 }
6796
6797 function descend_scope() {
6798 var save_scope = scope;
6799 scope = node;
6800 descend(node, tt);
6801 scope = save_scope;
6802 }
6803 }, function(node, in_list) {
6804 if (node instanceof AST_BlockStatement) return trim_block(node, tt.parent(), in_list);
6805 // Certain combination of unused name + side effect leads to invalid AST:
6806 // https://github.com/mishoo/UglifyJS/issues/44
6807 // https://github.com/mishoo/UglifyJS/issues/1838
6808 // https://github.com/mishoo/UglifyJS/issues/3371
6809 // We fix it at this stage by moving the `var` outside the `for`.
6810 if (node instanceof AST_For) {
6811 var block;
6812 if (node.init instanceof AST_BlockStatement) {
6813 block = node.init;
6814 node.init = block.body.pop();
6815 block.body.push(node);
6816 }
6817 if (node.init instanceof AST_Defun) {
6818 if (!block) {
6819 block = make_node(AST_BlockStatement, node, {
6820 body: [ node ]
6821 });
6822 }
6823 block.body.splice(-1, 0, node.init);
6824 node.init = null;
6825 } else if (node.init instanceof AST_SimpleStatement) {
6826 node.init = node.init.body;
6827 } else if (is_empty(node.init)) {
6828 node.init = null;
6829 }
6830 return !block ? node : in_list ? List.splice(block.body) : block;
6831 }
6832 if (node instanceof AST_ForIn) {
6833 if (!drop_vars || !compressor.option("loops")) return;
6834 if (!is_empty(node.body)) return;
6835 var sym = get_init_symbol(node);
6836 if (!sym) return;
6837 var def = sym.definition();
6838 if (def.id in in_use_ids) return;
6839 log(sym, "Dropping unused loop variable {name}");
6840 if (for_ins[def.id] === node) delete for_ins[def.id];
6841 var body = [];
6842 var value = node.object.drop_side_effect_free(compressor);
6843 if (value) {
6844 AST_Node.warn("Side effects in object of for-in loop [{file}:{line},{col}]", value.start);
6845 body.push(make_node(AST_SimpleStatement, node, {
6846 body: value
6847 }));
6848 }
6849 if (node.init instanceof AST_Definitions && def.orig[0] instanceof AST_SymbolCatch) {
6850 body.push(node.init);
6851 }
6852 return insert_statements(body, node, in_list);
6853 }
6854 if (node instanceof AST_Import) {
6855 if (node.properties && node.properties.length == 0) node.properties = null;
6856 return node;
6857 }
6858 if (node instanceof AST_Sequence) {
6859 if (node.expressions.length > 1) return;
6860 return maintain_this_binding(compressor, tt.parent(), node, node.expressions[0]);
6861 }
6862 });
6863 tt.push(compressor.parent());
6864 self.transform(tt);
6865 if (self instanceof AST_Lambda
6866 && self.body.length == 1
6867 && self.body[0] instanceof AST_Directive
6868 && self.body[0].value == "use strict") {
6869 self.body.length = 0;
6870 }
6871 trim_defns.forEach(function(def) {
6872 def.value = null;
6873 });
6874 unused_fn_names.forEach(function(fn) {
6875 fn.name = null;
6876 });
6877 calls_to_drop_args.forEach(function(call) {
6878 drop_unused_call_args(call, compressor, fns_with_marked_args);
6879 });
6880
6881 function log(sym, text) {
6882 AST_Node[sym.definition().references.length > 0 ? "info" : "warn"](text + " [{file}:{line},{col}]", template(sym));
6883 }
6884
6885 function template(sym) {
6886 return {
6887 name: sym.name,
6888 file: sym.start.file,
6889 line: sym.start.line,
6890 col : sym.start.col,
6891 };
6892 }
6893
6894 function get_rvalue(expr) {
6895 return expr[expr instanceof AST_Assign ? "right" : "value"];
6896 }
6897
6898 function insert_statements(body, orig, in_list) {
6899 switch (body.length) {
6900 case 0:
6901 return in_list ? List.skip : make_node(AST_EmptyStatement, orig);
6902 case 1:
6903 return body[0];
6904 default:
6905 return in_list ? List.splice(body) : make_node(AST_BlockStatement, orig, {
6906 body: body
6907 });
6908 }
6909 }
6910
6911 function track_assigns(def, node) {
6912 if (def.scope.resolve() !== self) return false;
6913 if (!def.fixed || !node.fixed) assign_in_use[def.id] = false;
6914 return assign_in_use[def.id] !== false;
6915 }
6916
6917 function add_assigns(def, node) {
6918 if (!assign_in_use[def.id]) assign_in_use[def.id] = [];
6919 if (node.fixed.assigns) push_uniq(assign_in_use[def.id], node.fixed.assigns);
6920 }
6921
6922 function indexOf_assign(def, node) {
6923 var nodes = assign_in_use[def.id];
6924 return nodes && nodes.indexOf(node);
6925 }
6926
6927 function verify_safe_usage(def, read, modified) {
6928 if (def.id in in_use_ids) return;
6929 if (read && modified) {
6930 in_use_ids[def.id] = read;
6931 in_use.push(def);
6932 } else {
6933 value_read[def.id] = read;
6934 value_modified[def.id] = modified;
6935 }
6936 }
6937
6938 function can_drop_lhs(sym, node) {
6939 var def = sym.definition();
6940 var in_use = in_use_ids[def.id];
6941 if (!in_use) return true;
6942 if (node[node instanceof AST_Assign ? "left" : "expression"] !== sym) return false;
6943 return in_use === sym && def.references.length - def.replaced == 1 || indexOf_assign(def, node) < 0;
6944 }
6945
6946 function get_rhs(assign) {
6947 var rhs = assign.right;
6948 if (!assign.write_only) return rhs;
6949 if (!(rhs instanceof AST_Binary && lazy_op[rhs.operator])) return rhs;
6950 if (!(rhs.left instanceof AST_SymbolRef)) return rhs;
6951 if (!(assign.left instanceof AST_SymbolRef)) return rhs;
6952 var def = assign.left.definition();
6953 if (rhs.left.definition() !== def) return rhs;
6954 if (rhs.right.has_side_effects(compressor)) return rhs;
6955 if (track_assigns(def, rhs.left)) add_assigns(def, rhs.left);
6956 return rhs.right;
6957 }
6958
6959 function get_init_symbol(for_in) {
6960 var init = for_in.init;
6961 if (init instanceof AST_Definitions) {
6962 init = init.definitions[0].name;
6963 return init instanceof AST_SymbolDeclaration && init;
6964 }
6965 while (init instanceof AST_PropAccess) init = init.expression.tail_node();
6966 if (init instanceof AST_SymbolRef) return init;
6967 }
6968
6969 function scan_ref_scoped(node, descend, init) {
6970 if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef) {
6971 var def = node.left.definition();
6972 if (def.scope.resolve() === self) assignments.add(def.id, node);
6973 }
6974 if (node instanceof AST_Unary && node.expression instanceof AST_SymbolRef) {
6975 var def = node.expression.definition();
6976 if (def.scope.resolve() === self) assignments.add(def.id, node);
6977 }
6978 var node_def, props = [], sym = assign_as_unused(node, props);
6979 if (sym && ((node_def = sym.definition()).scope.resolve() === self
6980 || self.variables.get(sym.name) === node_def)
6981 && !(is_arguments(node_def) && !all(self.argnames, function(argname) {
6982 return !argname.match_symbol(function(node) {
6983 if (node instanceof AST_SymbolFunarg) {
6984 var def = node.definition();
6985 return def.references.length > def.replaced;
6986 }
6987 }, true);
6988 }))) {
6989 if (node.write_only === "p" && node.right.may_throw_on_access(compressor, true)) return;
6990 var assign = props.assign;
6991 if (assign) {
6992 assign.write_only = true;
6993 assign.walk(tw);
6994 }
6995 props.forEach(function(prop) {
6996 prop.walk(tw);
6997 });
6998 if (node instanceof AST_Assign) {
6999 var right = get_rhs(node), shared = false;
7000 if (init && node.write_only === true && !right.has_side_effects(compressor)) {
7001 initializations.add(node_def.id, right);
7002 } else {
7003 right.walk(tw);
7004 shared = right.tail_node().operator == "=";
7005 }
7006 if (node.left === sym) {
7007 if (!node.write_only || shared) {
7008 verify_safe_usage(node_def, sym, value_modified[node_def.id]);
7009 }
7010 } else {
7011 var fixed = sym.fixed_value();
7012 if (!fixed || !fixed.is_constant()) {
7013 verify_safe_usage(node_def, value_read[node_def.id], true);
7014 }
7015 }
7016 }
7017 if (track_assigns(node_def, sym) && is_lhs(sym, node) !== sym) add_assigns(node_def, sym);
7018 return true;
7019 }
7020 if (node instanceof AST_ForIn) {
7021 if (node.init instanceof AST_SymbolRef && scope === self) {
7022 var id = node.init.definition().id;
7023 if (!(id in for_ins)) for_ins[id] = node;
7024 }
7025 if (!drop_vars || !compressor.option("loops")) return;
7026 if (!is_empty(node.body)) return;
7027 if (node.init.has_side_effects(compressor)) return;
7028 var sym = get_init_symbol(node);
7029 if (!sym) return;
7030 var def = sym.definition();
7031 if (def.scope.resolve() !== self) {
7032 var d = find_variable(sym.name);
7033 if (d === def || d && d.redefined() === def) return;
7034 }
7035 node.object.walk(tw);
7036 return true;
7037 }
7038 if (node instanceof AST_SymbolRef) {
7039 node_def = node.definition();
7040 if (!(node_def.id in in_use_ids)) {
7041 in_use_ids[node_def.id] = true;
7042 in_use.push(node_def);
7043 }
7044 if (cross_scope(node_def.scope, node.scope)) {
7045 var redef = node_def.redefined();
7046 if (redef && !(redef.id in in_use_ids)) {
7047 in_use_ids[redef.id] = true;
7048 in_use.push(redef);
7049 }
7050 }
7051 if (track_assigns(node_def, node)) add_assigns(node_def, node);
7052 return true;
7053 }
7054 if (node instanceof AST_Scope) {
7055 var save_scope = scope;
7056 scope = node;
7057 descend();
7058 scope = save_scope;
7059 return true;
7060 }
7061 }
7062
7063 function is_decl(node) {
7064 return (node instanceof AST_DefaultValue ? node.name : node) instanceof AST_SymbolDeclaration;
7065 }
7066
7067 function trim_default(trimmer, node) {
7068 node.value = node.value.transform(tt);
7069 var name = node.name.transform(trimmer);
7070 if (!name) {
7071 if (node.name instanceof AST_Destructured) return null;
7072 var value = node.value.drop_side_effect_free(compressor);
7073 if (!value) return null;
7074 log(node.name, "Side effects in default value of unused variable {name}");
7075 node.name.__unused = null;
7076 node.value = value;
7077 }
7078 return node;
7079 }
7080
7081 function trim_destructured(node, value, process, drop) {
7082 var trimmer = new TreeTransformer(function(node) {
7083 if (node instanceof AST_DefaultValue) {
7084 if (compressor.option("default_values") && value && value.is_defined(compressor)) {
7085 node = node.name;
7086 } else {
7087 var save_drop = drop;
7088 drop = false;
7089 var trimmed = trim_default(trimmer, node);
7090 drop = save_drop;
7091 if (!trimmed && drop && value) value = value.drop_side_effect_free(compressor);
7092 return trimmed;
7093 }
7094 }
7095 if (node instanceof AST_DestructuredArray) {
7096 var save_drop = drop;
7097 var save_value = value;
7098 if (value instanceof AST_SymbolRef) {
7099 drop = false;
7100 value = value.fixed_value();
7101 }
7102 var values = value instanceof AST_Array && value.elements;
7103 var elements = [], newValues = drop && [], pos = 0;
7104 node.elements.forEach(function(element, index) {
7105 value = values && values[index];
7106 if (value instanceof AST_Hole) {
7107 value = null;
7108 } else if (value instanceof AST_Spread) {
7109 if (drop) {
7110 newValues.length = pos;
7111 fill_holes(save_value, newValues);
7112 [].push.apply(newValues, values.slice(index));
7113 save_value.elements = newValues;
7114 }
7115 value = values = false;
7116 }
7117 element = element.transform(trimmer);
7118 if (element) elements[pos] = element;
7119 if (drop && value) newValues[pos] = value;
7120 if (element || value || !drop || !values) pos++;
7121 });
7122 value = values && make_node(AST_Array, save_value, {
7123 elements: values.slice(node.elements.length),
7124 });
7125 if (node.rest) {
7126 var was_drop = drop;
7127 drop = false;
7128 node.rest = node.rest.transform(compressor.option("rests") ? trimmer : tt);
7129 drop = was_drop;
7130 if (node.rest) elements.length = pos;
7131 }
7132 if (drop) {
7133 if (value && !node.rest) value = value.drop_side_effect_free(compressor);
7134 if (value instanceof AST_Array) {
7135 value = value.elements;
7136 } else if (value instanceof AST_Sequence) {
7137 value = value.expressions;
7138 } else if (value) {
7139 value = [ value ];
7140 }
7141 if (value && value.length) {
7142 newValues.length = pos;
7143 [].push.apply(newValues, value);
7144 }
7145 }
7146 value = save_value;
7147 drop = save_drop;
7148 if (values && newValues) {
7149 fill_holes(value, newValues);
7150 value.elements = newValues;
7151 }
7152 if (!node.rest && (value instanceof AST_Array
7153 || value && value.is_string(compressor))) switch (elements.length) {
7154 case 0:
7155 if (drop) value = value.drop_side_effect_free(compressor);
7156 return null;
7157 case 1:
7158 if (!drop) break;
7159 var sym = elements[0];
7160 if (!(sym instanceof AST_Symbol)) break;
7161 value = make_node(AST_Sub, node, {
7162 expression: value,
7163 property: make_node(AST_Number, node, { value: 0 }),
7164 });
7165 return sym;
7166 }
7167 fill_holes(node, elements);
7168 node.elements = elements;
7169 return node;
7170 }
7171 if (node instanceof AST_DestructuredObject) {
7172 var save_drop = drop;
7173 var save_value = value;
7174 if (value instanceof AST_SymbolRef) {
7175 drop = false;
7176 value = value.fixed_value();
7177 }
7178 var prop_keys, prop_map;
7179 if (value instanceof AST_Object) {
7180 prop_keys = [];
7181 prop_map = Object.create(null);
7182 value.properties.forEach(function(prop, index) {
7183 if (prop instanceof AST_Spread) return prop_map = false;
7184 var key = prop.key;
7185 if (key instanceof AST_Node) key = key.evaluate(compressor, true);
7186 if (key instanceof AST_Node) {
7187 prop_map = false;
7188 } else if (prop_map && !(prop instanceof AST_ObjectSetter)) {
7189 prop_map[key] = prop;
7190 }
7191 prop_keys[index] = key;
7192 });
7193 }
7194 if (node.rest) {
7195 value = false;
7196 node.rest = node.rest.transform(compressor.option("rests") ? trimmer : tt);
7197 }
7198 var can_drop = Object.create(null);
7199 var drop_keys = drop && Object.create(null);
7200 var properties = [];
7201 node.properties.map(function(prop) {
7202 var key = prop.key;
7203 if (key instanceof AST_Node) {
7204 prop.key = key = key.transform(tt);
7205 key = key.evaluate(compressor, true);
7206 }
7207 if (key instanceof AST_Node) {
7208 drop_keys = false;
7209 } else {
7210 can_drop[key] = !(key in can_drop);
7211 }
7212 return key;
7213 }).forEach(function(key, index) {
7214 var prop = node.properties[index], trimmed;
7215 if (key instanceof AST_Node) {
7216 drop = false;
7217 value = false;
7218 trimmed = prop.value.transform(trimmer) || retain_lhs(prop.value);
7219 } else {
7220 drop = drop_keys && can_drop[key];
7221 var mapped = prop_map && prop_map[key];
7222 if (mapped) {
7223 value = mapped.value;
7224 if (value instanceof AST_Accessor) value = false;
7225 } else {
7226 value = false;
7227 }
7228 trimmed = prop.value.transform(trimmer);
7229 if (!trimmed) {
7230 if (node.rest || retain_key(prop)) trimmed = retain_lhs(prop.value);
7231 if (drop_keys && !(key in drop_keys)) {
7232 if (mapped) {
7233 drop_keys[key] = mapped;
7234 if (value === null) {
7235 prop_map[key] = retain_key(mapped) && make_node(AST_ObjectKeyVal, mapped, {
7236 key: mapped.key,
7237 value: make_node(AST_Number, mapped, { value: 0 }),
7238 });
7239 }
7240 } else {
7241 drop_keys[key] = true;
7242 }
7243 }
7244 } else if (drop_keys) {
7245 drop_keys[key] = false;
7246 }
7247 if (value) mapped.value = value;
7248 }
7249 if (trimmed) {
7250 prop.value = trimmed;
7251 properties.push(prop);
7252 }
7253 });
7254 value = save_value;
7255 drop = save_drop;
7256 if (drop_keys && prop_keys) value.properties = List(value.properties, function(prop, index) {
7257 if (prop instanceof AST_Spread) return prop;
7258 var key = prop_keys[index];
7259 if (key instanceof AST_Node) return prop;
7260 if (key in drop_keys) {
7261 var mapped = drop_keys[key];
7262 if (!mapped) return prop;
7263 if (mapped === prop) return prop_map[key] || List.skip;
7264 } else if (node.rest) {
7265 return prop;
7266 }
7267 var trimmed = prop.value.drop_side_effect_free(compressor);
7268 if (trimmed) {
7269 prop.value = trimmed;
7270 return prop;
7271 }
7272 return retain_key(prop) ? make_node(AST_ObjectKeyVal, prop, {
7273 key: prop.key,
7274 value: make_node(AST_Number, prop, { value: 0 }),
7275 }) : List.skip;
7276 });
7277 if (value && !node.rest) switch (properties.length) {
7278 case 0:
7279 if (value.may_throw_on_access(compressor, true)) break;
7280 if (drop) value = value.drop_side_effect_free(compressor);
7281 return null;
7282 case 1:
7283 if (!drop) break;
7284 var prop = properties[0];
7285 if (prop.key instanceof AST_Node) break;
7286 if (!(prop.value instanceof AST_Symbol)) break;
7287 value = make_node(AST_Sub, node, {
7288 expression: value,
7289 property: make_node_from_constant(prop.key, prop),
7290 });
7291 return prop.value;
7292 }
7293 node.properties = properties;
7294 return node;
7295 }
7296 if (node instanceof AST_Hole) {
7297 node = null;
7298 } else {
7299 node = process(node);
7300 }
7301 if (!node && drop && value) value = value.drop_side_effect_free(compressor);
7302 return node;
7303 });
7304 return {
7305 name: node.transform(trimmer),
7306 value: value,
7307 };
7308
7309 function retain_key(prop) {
7310 return prop.key instanceof AST_Node && prop.key.has_side_effects(compressor);
7311 }
7312
7313 function retain_lhs(node) {
7314 if (node instanceof AST_DefaultValue) return retain_lhs(node.name);
7315 if (node instanceof AST_Destructured) {
7316 if (value === null) {
7317 value = make_node(AST_Number, node, { value: 0 });
7318 } else if (value && (value.tail_node().write_only === true
7319 || value.may_throw_on_access(compressor, true))) {
7320 value = make_node(AST_Array, node, {
7321 elements: value instanceof AST_Sequence ? value.expressions : [ value ],
7322 });
7323 }
7324 return make_node(AST_DestructuredObject, node, { properties: [] });
7325 }
7326 node.__unused = null;
7327 return node;
7328 }
7329 }
7330 });
7331
7332 AST_Scope.DEFMETHOD("hoist_declarations", function(compressor) {
7333 if (compressor.has_directive("use asm")) return;
7334 var hoist_funs = compressor.option("hoist_funs");
7335 var hoist_vars = compressor.option("hoist_vars");
7336 var self = this;
7337 if (hoist_vars) {
7338 // let's count var_decl first, we seem to waste a lot of
7339 // space if we hoist `var` when there's only one.
7340 var var_decl = 0;
7341 self.walk(new TreeWalker(function(node) {
7342 if (var_decl > 1) return true;
7343 if (node instanceof AST_ExportDeclaration) return true;
7344 if (node instanceof AST_Scope && node !== self) return true;
7345 if (node instanceof AST_Var) {
7346 var_decl++;
7347 return true;
7348 }
7349 }));
7350 if (var_decl <= 1) hoist_vars = false;
7351 }
7352 if (!hoist_funs && !hoist_vars) return;
7353 var consts = Object.create(null);
7354 var dirs = [];
7355 var hoisted = [];
7356 var vars = new Dictionary(), vars_found = 0;
7357 var tt = new TreeTransformer(function(node, descend, in_list) {
7358 if (node === self) return;
7359 if (node instanceof AST_Directive) {
7360 dirs.push(node);
7361 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
7362 }
7363 if (node instanceof AST_LambdaDefinition) {
7364 if (!hoist_funs) return node;
7365 var p = tt.parent();
7366 if (p instanceof AST_ExportDeclaration) return node;
7367 if (p instanceof AST_ExportDefault) return node;
7368 if (p !== self && compressor.has_directive("use strict")) return node;
7369 hoisted.push(node);
7370 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
7371 }
7372 if (node instanceof AST_Var) {
7373 if (!hoist_vars) return node;
7374 var p = tt.parent();
7375 if (p instanceof AST_ExportDeclaration) return node;
7376 if (!all(node.definitions, function(defn) {
7377 var sym = defn.name;
7378 return sym instanceof AST_SymbolVar
7379 && !consts[sym.name]
7380 && self.find_variable(sym.name) === sym.definition();
7381 })) return node;
7382 node.definitions.forEach(function(def) {
7383 vars.set(def.name.name, def);
7384 ++vars_found;
7385 });
7386 var seq = node.to_assignments();
7387 if (p instanceof AST_ForEnumeration && p.init === node) {
7388 if (seq) return seq;
7389 var def = node.definitions[0].name;
7390 return make_node(AST_SymbolRef, def, def);
7391 }
7392 if (p instanceof AST_For && p.init === node) return seq;
7393 if (!seq) return in_list ? List.skip : make_node(AST_EmptyStatement, node);
7394 return make_node(AST_SimpleStatement, node, { body: seq });
7395 }
7396 if (node instanceof AST_Scope) return node;
7397 if (node instanceof AST_SymbolConst) {
7398 consts[node.name] = true;
7399 return node;
7400 }
7401 });
7402 self.transform(tt);
7403 if (vars_found > 0) {
7404 // collect only vars which don't show up in self's arguments list
7405 var defs = [];
7406 if (self instanceof AST_Lambda) self.each_argname(function(argname) {
7407 vars.del(argname.name);
7408 });
7409 vars.each(function(def, name) {
7410 def = def.clone();
7411 def.value = null;
7412 defs.push(def);
7413 vars.set(name, def);
7414 });
7415 if (defs.length > 0) {
7416 // try to merge in assignments
7417 insert_vars(self.body);
7418 defs = make_node(AST_Var, self, { definitions: defs });
7419 hoisted.push(defs);
7420 }
7421 }
7422 self.body = dirs.concat(hoisted, self.body);
7423
7424 function insert_vars(body) {
7425 while (body.length) {
7426 var stat = body[0];
7427 if (stat instanceof AST_SimpleStatement) {
7428 var expr = stat.body, sym, assign;
7429 if (expr instanceof AST_Assign
7430 && expr.operator == "="
7431 && (sym = expr.left) instanceof AST_Symbol
7432 && vars.has(sym.name)) {
7433 var def = vars.get(sym.name);
7434 if (def.value) break;
7435 var value = expr.right;
7436 if (value instanceof AST_Sequence) value = value.clone();
7437 def.value = value;
7438 remove(defs, def);
7439 defs.push(def);
7440 body.shift();
7441 continue;
7442 }
7443 if (expr instanceof AST_Sequence
7444 && (assign = expr.expressions[0]) instanceof AST_Assign
7445 && assign.operator == "="
7446 && (sym = assign.left) instanceof AST_Symbol
7447 && vars.has(sym.name)) {
7448 var def = vars.get(sym.name);
7449 if (def.value) break;
7450 def.value = assign.right;
7451 remove(defs, def);
7452 defs.push(def);
7453 stat.body = make_sequence(expr, expr.expressions.slice(1));
7454 continue;
7455 }
7456 }
7457 if (stat instanceof AST_EmptyStatement) {
7458 body.shift();
7459 continue;
7460 }
7461 if (stat instanceof AST_BlockStatement && !insert_vars(stat.body)) {
7462 body.shift();
7463 continue;
7464 }
7465 break;
7466 }
7467 return body.length;
7468 }
7469 });
7470
7471 function scan_local_returns(fn, transform) {
7472 fn.walk(new TreeWalker(function(node) {
7473 if (node instanceof AST_Return) {
7474 transform(node);
7475 return true;
7476 }
7477 if (node instanceof AST_Scope && node !== fn) return true;
7478 }));
7479 }
7480
7481 function map_bool_returns(fn) {
7482 var map = Object.create(null);
7483 scan_local_returns(fn, function(node) {
7484 var value = node.value;
7485 if (value) value = value.tail_node();
7486 if (value instanceof AST_SymbolRef) {
7487 var id = value.definition().id;
7488 map[id] = (map[id] || 0) + 1;
7489 }
7490 });
7491 return map;
7492 }
7493
7494 function all_bool(def, bool_returns, compressor) {
7495 return def.bool_fn + (bool_returns[def.id] || 0) === def.references.length - def.replaced
7496 && !compressor.exposed(def);
7497 }
7498
7499 function process_boolean_returns(fn, compressor) {
7500 scan_local_returns(fn, function(node) {
7501 node.in_bool = true;
7502 var value = node.value;
7503 if (value) {
7504 var ev = fuzzy_eval(compressor, value);
7505 if (!ev) {
7506 value = value.drop_side_effect_free(compressor);
7507 node.value = value ? make_sequence(node.value, [
7508 value,
7509 make_node(AST_Number, node.value, { value: 0 }),
7510 ]) : null;
7511 } else if (!(ev instanceof AST_Node)) {
7512 value = value.drop_side_effect_free(compressor);
7513 node.value = value ? make_sequence(node.value, [
7514 value,
7515 make_node(AST_Number, node.value, { value: 1 }),
7516 ]) : make_node(AST_Number, node.value, { value: 1 });
7517 }
7518 }
7519 });
7520 }
7521
7522 AST_Scope.DEFMETHOD("process_boolean_returns", noop);
7523 AST_Defun.DEFMETHOD("process_boolean_returns", function(compressor) {
7524 if (!compressor.option("booleans")) return;
7525 var bool_returns = map_bool_returns(this);
7526 if (!all_bool(this.name.definition(), bool_returns, compressor)) return;
7527 if (compressor.parent() instanceof AST_ExportDefault) return;
7528 process_boolean_returns(this, compressor);
7529 });
7530 AST_Function.DEFMETHOD("process_boolean_returns", function(compressor) {
7531 if (!compressor.option("booleans")) return;
7532 var bool_returns = map_bool_returns(this);
7533 if (this.name && !all_bool(this.name.definition(), bool_returns, compressor)) return;
7534 var parent = compressor.parent();
7535 if (parent instanceof AST_Assign) {
7536 if (parent.operator != "=") return;
7537 var sym = parent.left;
7538 if (!(sym instanceof AST_SymbolRef)) return;
7539 if (!all_bool(sym.definition(), bool_returns, compressor)) return;
7540 } else if (parent instanceof AST_Call && parent.expression !== this) {
7541 var exp = parent.expression;
7542 if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
7543 if (!(exp instanceof AST_Lambda)) return;
7544 if (exp.uses_arguments || exp.pinned()) return;
7545 var sym = exp.argnames[parent.args.indexOf(this)];
7546 if (sym instanceof AST_DefaultValue) sym = sym.name;
7547 if (sym instanceof AST_SymbolFunarg && !all_bool(sym.definition(), bool_returns, compressor)) return;
7548 } else if (parent.TYPE == "Call") {
7549 compressor.pop();
7550 var in_bool = compressor.in_boolean_context();
7551 compressor.push(this);
7552 if (!in_bool) return;
7553 } else return;
7554 process_boolean_returns(this, compressor);
7555 });
7556
7557 AST_BlockScope.DEFMETHOD("var_names", function() {
7558 var var_names = this._var_names;
7559 if (!var_names) {
7560 this._var_names = var_names = Object.create(null);
7561 this.enclosed.forEach(function(def) {
7562 var_names[def.name] = true;
7563 });
7564 this.variables.each(function(def, name) {
7565 var_names[name] = true;
7566 });
7567 }
7568 return var_names;
7569 });
7570
7571 AST_Scope.DEFMETHOD("make_var", function(type, orig, prefix) {
7572 var scopes = [ this ];
7573 if (orig instanceof AST_SymbolDeclaration) orig.definition().references.forEach(function(ref) {
7574 var s = ref.scope;
7575 if (member(s, scopes)) return;
7576 do {
7577 push_uniq(scopes, s);
7578 s = s.parent_scope;
7579 } while (s && s !== this);
7580 });
7581 prefix = prefix.replace(/(?:^[^a-z_$]|[^a-z0-9_$])/ig, "_");
7582 var name = prefix;
7583 for (var i = 0; !all(scopes, function(scope) {
7584 return !scope.var_names()[name];
7585 }); i++) name = prefix + "$" + i;
7586 var sym = make_node(type, orig, {
7587 name: name,
7588 scope: this,
7589 });
7590 var def = this.def_variable(sym);
7591 scopes.forEach(function(scope) {
7592 scope.enclosed.push(def);
7593 scope.var_names()[name] = true;
7594 });
7595 return sym;
7596 });
7597
7598 AST_Scope.DEFMETHOD("hoist_properties", function(compressor) {
7599 if (!compressor.option("hoist_props") || compressor.has_directive("use asm")) return;
7600 var self = this;
7601 var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false;
7602 var defs_by_id = Object.create(null);
7603 self.transform(new TreeTransformer(function(node, descend) {
7604 if (node instanceof AST_Assign) {
7605 if (node.operator != "=") return;
7606 if (!node.write_only) return;
7607 if (!can_hoist(node.left, node.right, 1)) return;
7608 descend(node, this);
7609 var defs = new Dictionary();
7610 var assignments = [];
7611 var decls = [];
7612 node.right.properties.forEach(function(prop) {
7613 var decl = make_sym(AST_SymbolVar, node.left, prop.key);
7614 decls.push(make_node(AST_VarDef, node, {
7615 name: decl,
7616 value: null,
7617 }));
7618 var sym = make_node(AST_SymbolRef, node, {
7619 name: decl.name,
7620 scope: self,
7621 thedef: decl.definition(),
7622 });
7623 sym.reference();
7624 assignments.push(make_node(AST_Assign, node, {
7625 operator: "=",
7626 left: sym,
7627 right: prop.value,
7628 }));
7629 });
7630 defs_by_id[node.left.definition().id] = defs;
7631 self.body.splice(self.body.indexOf(this.stack[1]) + 1, 0, make_node(AST_Var, node, {
7632 definitions: decls,
7633 }));
7634 return make_sequence(node, assignments);
7635 }
7636 if (node instanceof AST_Scope) return node === self ? undefined : node;
7637 if (node instanceof AST_VarDef) {
7638 if (!can_hoist(node.name, node.value, 0)) return;
7639 descend(node, this);
7640 var defs = new Dictionary();
7641 var var_defs = [];
7642 node.value.properties.forEach(function(prop) {
7643 var_defs.push(make_node(AST_VarDef, node, {
7644 name: make_sym(node.name.CTOR, node.name, prop.key),
7645 value: prop.value,
7646 }));
7647 });
7648 defs_by_id[node.name.definition().id] = defs;
7649 return List.splice(var_defs);
7650 }
7651
7652 function make_sym(type, sym, key) {
7653 var new_var = self.make_var(type, sym, sym.name + "_" + key);
7654 defs.set(key, new_var.definition());
7655 return new_var;
7656 }
7657 }));
7658 self.transform(new TreeTransformer(function(node, descend) {
7659 if (node instanceof AST_PropAccess) {
7660 if (!(node.expression instanceof AST_SymbolRef)) return;
7661 var defs = defs_by_id[node.expression.definition().id];
7662 if (!defs) return;
7663 var def = defs.get(node.get_property());
7664 var sym = make_node(AST_SymbolRef, node, {
7665 name: def.name,
7666 scope: node.expression.scope,
7667 thedef: def,
7668 });
7669 sym.reference();
7670 return sym;
7671 }
7672 if (node instanceof AST_SymbolRef) {
7673 if (!(node.definition().id in defs_by_id)) return;
7674 return make_node(AST_Object, node, { properties: [] });
7675 }
7676 }));
7677
7678 function can_hoist(sym, right, count) {
7679 if (!(sym instanceof AST_Symbol)) return;
7680 var def = sym.definition();
7681 if (def.assignments != count) return;
7682 if (def.direct_access) return;
7683 if (def.escaped.depth == 1) return;
7684 if (def.references.length - def.replaced == count) return;
7685 if (def.single_use) return;
7686 if (top_retain(def)) return;
7687 if (sym.fixed_value() !== right) return;
7688 return right instanceof AST_Object
7689 && right.properties.length > 0
7690 && all(right.properties, can_hoist_property)
7691 && all(def.references, function(ref) {
7692 return ref.fixed_value() === right;
7693 })
7694 && can_drop_symbol(sym, compressor);
7695 }
7696 });
7697
7698 function fn_name_unused(fn, compressor) {
7699 if (!fn.name || !compressor.option("ie")) return true;
7700 var def = fn.name.definition();
7701 if (compressor.exposed(def)) return false;
7702 return all(def.references, function(sym) {
7703 return !(sym instanceof AST_SymbolRef);
7704 });
7705 }
7706
7707 // drop_side_effect_free()
7708 // remove side-effect-free parts which only affects return value
7709 (function(def) {
7710 // Drop side-effect-free elements from an array of expressions.
7711 // Returns an array of expressions with side-effects or null
7712 // if all elements were dropped. Note: original array may be
7713 // returned if nothing changed.
7714 function trim(nodes, compressor, first_in_statement, spread) {
7715 var len = nodes.length;
7716 var ret = [], changed = false;
7717 for (var i = 0; i < len; i++) {
7718 var node = nodes[i];
7719 var trimmed;
7720 if (spread && node instanceof AST_Spread) {
7721 trimmed = spread(node, compressor, first_in_statement);
7722 } else {
7723 trimmed = node.drop_side_effect_free(compressor, first_in_statement);
7724 }
7725 if (trimmed !== node) changed = true;
7726 if (trimmed) {
7727 ret.push(trimmed);
7728 first_in_statement = false;
7729 }
7730 }
7731 return ret.length ? changed ? ret : nodes : null;
7732 }
7733 function array_spread(node, compressor, first_in_statement) {
7734 var exp = node.expression;
7735 if (!exp.is_string(compressor)) return node;
7736 return exp.drop_side_effect_free(compressor, first_in_statement);
7737 }
7738 function convert_spread(node) {
7739 return node instanceof AST_Spread ? make_node(AST_Array, node, {
7740 elements: [ node ]
7741 }) : node;
7742 }
7743 def(AST_Node, return_this);
7744 def(AST_Accessor, return_null);
7745 def(AST_Array, function(compressor, first_in_statement) {
7746 var values = trim(this.elements, compressor, first_in_statement, array_spread);
7747 if (!values) return null;
7748 if (values === this.elements && all(values, function(node) {
7749 return node instanceof AST_Spread;
7750 })) return this;
7751 return make_sequence(this, values.map(convert_spread));
7752 });
7753 def(AST_Assign, function(compressor) {
7754 var left = this.left;
7755 if (left instanceof AST_PropAccess) {
7756 var expr = left.expression;
7757 if (expr.may_throw_on_access(compressor, true)) return this;
7758 if (compressor.has_directive("use strict") && expr.is_constant()) return this;
7759 }
7760 if (left.has_side_effects(compressor)) return this;
7761 var right = this.right;
7762 if (!lazy_op[this.operator.slice(0, -1)]) {
7763 this.write_only = true;
7764 if (root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) {
7765 return right.drop_side_effect_free(compressor);
7766 }
7767 }
7768 return this;
7769 });
7770 def(AST_Await, function(compressor) {
7771 if (!compressor.option("awaits")) return this;
7772 var exp = this.expression;
7773 if (!is_primitive(compressor, exp)) return this;
7774 var node = this.clone();
7775 node.expression = exp.drop_side_effect_free(compressor) || make_node(AST_Number, this, { value: 0 });
7776 return node;
7777 });
7778 def(AST_Binary, function(compressor, first_in_statement) {
7779 var left = this.left;
7780 var right = this.right;
7781 var op = this.operator;
7782 if (op == "in" && !is_object(right)) {
7783 var lhs = left.drop_side_effect_free(compressor, first_in_statement);
7784 if (lhs === left) return this;
7785 var node = this.clone();
7786 node.left = lhs || make_node(AST_Number, left, { value: 0 });
7787 return node;
7788 }
7789 var rhs = right.drop_side_effect_free(compressor, first_in_statement);
7790 if (!rhs) return left.drop_side_effect_free(compressor, first_in_statement);
7791 if (lazy_op[op] && rhs.has_side_effects(compressor)) {
7792 var node = this;
7793 if (rhs !== right) {
7794 node = node.clone();
7795 node.right = rhs.drop_side_effect_free(compressor);
7796 }
7797 if (op == "??") return node;
7798 var negated = make_node(AST_Binary, this, {
7799 operator: op == "&&" ? "||" : "&&",
7800 left: left.negate(compressor, first_in_statement),
7801 right: node.right,
7802 });
7803 return first_in_statement ? best_of_statement(node, negated) : best_of_expression(node, negated);
7804 }
7805 var lhs = left.drop_side_effect_free(compressor, first_in_statement);
7806 if (!lhs) return rhs;
7807 rhs = rhs.drop_side_effect_free(compressor);
7808 if (!rhs) return lhs;
7809 return make_sequence(this, [ lhs, rhs ]);
7810 });
7811 function drop_returns(compressor, exp) {
7812 var arrow = is_arrow(exp);
7813 var async = is_async(exp);
7814 var drop_body = false;
7815 if (arrow && compressor.option("arrows")) {
7816 if (!exp.value) {
7817 drop_body = true;
7818 } else if (!async || is_primitive(compressor, exp.value)) {
7819 exp.value = exp.value.drop_side_effect_free(compressor);
7820 }
7821 } else if (exp instanceof AST_AsyncFunction || exp instanceof AST_Function) {
7822 if (exp.name) {
7823 var def = exp.name.definition();
7824 drop_body = def.references.length == def.replaced;
7825 } else {
7826 drop_body = true;
7827 }
7828 }
7829 if (drop_body) {
7830 exp.process_expression(false, function(node) {
7831 var value = node.value;
7832 if (value) {
7833 if (async && !is_primitive(compressor, value)) return node;
7834 value = value.drop_side_effect_free(compressor, true);
7835 }
7836 if (!value) return make_node(AST_EmptyStatement, node);
7837 return make_node(AST_SimpleStatement, node, { body: value });
7838 });
7839 scan_local_returns(exp, function(node) {
7840 var value = node.value;
7841 if (value) {
7842 if (async && !is_primitive(compressor, value)) return;
7843 node.value = value.drop_side_effect_free(compressor);
7844 }
7845 });
7846 }
7847 if (async && compressor.option("awaits")) {
7848 if (drop_body) exp.process_expression("awaits", function(node) {
7849 var body = node.body;
7850 if (body instanceof AST_Await) {
7851 if (is_primitive(compressor, body.expression)) {
7852 body = body.expression.drop_side_effect_free(compressor, true);
7853 if (!body) return make_node(AST_EmptyStatement, node);
7854 node.body = body;
7855 }
7856 } else if (body instanceof AST_Sequence) {
7857 var exprs = body.expressions;
7858 for (var i = exprs.length; --i >= 0;) {
7859 var tail = exprs[i];
7860 if (!(tail instanceof AST_Await)) break;
7861 if (!is_primitive(compressor, tail.expression)) break;
7862 if (exprs[i] = tail.expression.drop_side_effect_free(compressor)) break;
7863 }
7864 switch (i) {
7865 case -1:
7866 return make_node(AST_EmptyStatement, node);
7867 case 0:
7868 node.body = exprs[0];
7869 break;
7870 default:
7871 exprs.length = i + 1;
7872 break;
7873 }
7874 }
7875 return node;
7876 });
7877 var abort = !drop_body && exp.name || arrow && exp.value && !is_primitive(compressor, exp.value);
7878 var tw = new TreeWalker(function(node) {
7879 if (abort) return true;
7880 if (tw.parent() === exp && node.may_throw(compressor)) return abort = true;
7881 if (node instanceof AST_Await) return abort = true;
7882 if (node instanceof AST_ForAwaitOf) return abort = true;
7883 if (node instanceof AST_Return) {
7884 if (node.value && !is_primitive(compressor, node.value)) return abort = true;
7885 return;
7886 }
7887 if (node instanceof AST_Scope && node !== exp) return true;
7888 });
7889 exp.walk(tw);
7890 if (!abort) {
7891 var ctor;
7892 switch (exp.CTOR) {
7893 case AST_AsyncArrow:
7894 ctor = AST_Arrow;
7895 break;
7896 case AST_AsyncFunction:
7897 ctor = AST_Function;
7898 break;
7899 case AST_AsyncGeneratorFunction:
7900 ctor = AST_GeneratorFunction;
7901 break;
7902 }
7903 return make_node(ctor, exp, exp);
7904 }
7905 }
7906 return drop_body && exp.clone();
7907 }
7908 def(AST_Call, function(compressor, first_in_statement) {
7909 var self = this;
7910 if (self.is_expr_pure(compressor)) {
7911 if (self.pure) AST_Node.warn("Dropping __PURE__ call [{file}:{line},{col}]", self.start);
7912 var args = trim(self.args, compressor, first_in_statement, array_spread);
7913 return args && make_sequence(self, args.map(convert_spread));
7914 }
7915 var exp = self.expression;
7916 if (self.is_call_pure(compressor)) {
7917 var exprs = self.args.slice();
7918 exprs.unshift(exp.expression);
7919 exprs = trim(exprs, compressor, first_in_statement, array_spread);
7920 return exprs && make_sequence(self, exprs.map(convert_spread));
7921 }
7922 if (compressor.option("yields") && is_generator(exp)) {
7923 var call = self.clone();
7924 call.expression = make_node(AST_Function, exp, exp);
7925 call.expression.body = [];
7926 var opt = call.transform(compressor);
7927 if (opt !== call) return opt.drop_side_effect_free(compressor, first_in_statement);
7928 }
7929 var dropped = drop_returns(compressor, exp);
7930 if (dropped) {
7931 // always shallow clone to ensure stripping of negated IIFEs
7932 self = self.clone();
7933 self.expression = dropped;
7934 // avoid extraneous traversal
7935 if (exp._squeezed) self.expression._squeezed = true;
7936 }
7937 if (self instanceof AST_New) {
7938 var fn = exp;
7939 if (fn instanceof AST_SymbolRef) fn = fn.fixed_value();
7940 if (fn instanceof AST_Lambda) {
7941 if (assign_this_only(fn, compressor)) {
7942 var exprs = self.args.slice();
7943 exprs.unshift(exp);
7944 exprs = trim(exprs, compressor, first_in_statement, array_spread);
7945 return exprs && make_sequence(self, exprs.map(convert_spread));
7946 }
7947 if (!fn.contains_this()) self = make_node(AST_Call, self, self);
7948 }
7949 }
7950 self.call_only = true;
7951 return self;
7952 });
7953 function assign_this_only(fn, compressor) {
7954 fn.new = true;
7955 var result = all(fn.body, function(stat) {
7956 return !stat.has_side_effects(compressor);
7957 }) && all(fn.argnames, function(argname) {
7958 return !argname.match_symbol(return_false);
7959 }) && !(fn.rest && fn.rest.match_symbol(return_false));
7960 delete fn.new;
7961 return result;
7962 }
7963 function drop_class(self, compressor, first_in_statement) {
7964 var exprs = [], values = [];
7965 var props = self.properties;
7966 for (var i = 0; i < props.length; i++) {
7967 var prop = props[i];
7968 if (prop.key instanceof AST_Node) exprs.push(prop.key);
7969 if (prop.static && prop.value
7970 && prop instanceof AST_ClassField
7971 && prop.value.has_side_effects(compressor)) {
7972 if (prop.value.contains_this()) return self;
7973 values.push(prop.value);
7974 }
7975 }
7976 var base = self.extends;
7977 if (base) {
7978 if (base instanceof AST_SymbolRef) base = base.fixed_value();
7979 base = !safe_for_extends(base);
7980 if (!base) exprs.unshift(self.extends);
7981 }
7982 exprs = trim(exprs, compressor, first_in_statement);
7983 if (exprs) first_in_statement = false;
7984 values = trim(values, compressor, first_in_statement);
7985 if (!exprs) {
7986 if (!base && !values) return null;
7987 exprs = [];
7988 }
7989 if (base) {
7990 var node = to_class_expr(self, true);
7991 node.properties = [];
7992 if (exprs.length) node.properties.push(make_node(AST_ClassMethod, self, {
7993 key: make_sequence(self, exprs),
7994 value: make_node(AST_Function, self, {
7995 argnames: [],
7996 body: [],
7997 }).init_vars(node),
7998 }));
7999 exprs = [ node ];
8000 }
8001 if (values) exprs.push(make_node(AST_Call, self, {
8002 expression: make_node(AST_Arrow, self, {
8003 argnames: [],
8004 body: [],
8005 value: make_sequence(self, values),
8006 }).init_vars(self.parent_scope),
8007 args: [],
8008 }));
8009 return make_sequence(self, exprs);
8010 }
8011 def(AST_ClassExpression, function(compressor, first_in_statement) {
8012 var self = this;
8013 var name = self.name;
8014 if (name && name.fixed_value() !== self && name.definition().references.length > 0) return self;
8015 return drop_class(self, compressor, first_in_statement);
8016 });
8017 def(AST_Conditional, function(compressor) {
8018 var consequent = this.consequent.drop_side_effect_free(compressor);
8019 var alternative = this.alternative.drop_side_effect_free(compressor);
8020 if (consequent === this.consequent && alternative === this.alternative) return this;
8021 var exprs;
8022 if (compressor.option("ie")) {
8023 exprs = [];
8024 if (consequent instanceof AST_Function) {
8025 exprs.push(consequent);
8026 consequent = null;
8027 }
8028 if (alternative instanceof AST_Function) {
8029 exprs.push(alternative);
8030 alternative = null;
8031 }
8032 }
8033 var node;
8034 if (!consequent) {
8035 node = alternative ? make_node(AST_Binary, this, {
8036 operator: "||",
8037 left: this.condition,
8038 right: alternative
8039 }) : this.condition.drop_side_effect_free(compressor);
8040 } else if (!alternative) {
8041 node = make_node(AST_Binary, this, {
8042 operator: "&&",
8043 left: this.condition,
8044 right: consequent
8045 });
8046 } else {
8047 node = this.clone();
8048 node.consequent = consequent;
8049 node.alternative = alternative;
8050 }
8051 if (!compressor.option("ie")) return node;
8052 if (node) exprs.push(node);
8053 return exprs.length == 0 ? null : make_sequence(this, exprs);
8054 });
8055 def(AST_Constant, return_null);
8056 def(AST_DefClass, function(compressor, first_in_statement) {
8057 return drop_class(this, compressor, first_in_statement);
8058 });
8059 def(AST_Dot, function(compressor, first_in_statement) {
8060 var expr = this.expression;
8061 if (!this.optional && expr.may_throw_on_access(compressor)) return this;
8062 return expr.drop_side_effect_free(compressor, first_in_statement);
8063 });
8064 def(AST_Function, function(compressor) {
8065 return fn_name_unused(this, compressor) ? null : this;
8066 });
8067 def(AST_LambdaExpression, return_null);
8068 def(AST_Object, function(compressor, first_in_statement) {
8069 var exprs = [];
8070 this.properties.forEach(function(prop) {
8071 if (prop instanceof AST_Spread) {
8072 exprs.push(prop);
8073 } else {
8074 if (prop.key instanceof AST_Node) exprs.push(prop.key);
8075 exprs.push(prop.value);
8076 }
8077 });
8078 var values = trim(exprs, compressor, first_in_statement, function(node, compressor, first_in_statement) {
8079 var exp = node.expression;
8080 return spread_side_effects(exp) ? node : exp.drop_side_effect_free(compressor, first_in_statement);
8081 });
8082 if (!values) return null;
8083 if (values === exprs && !all(values, function(node) {
8084 return !(node instanceof AST_Spread);
8085 })) return this;
8086 return make_sequence(this, values.map(function(node) {
8087 return node instanceof AST_Spread ? make_node(AST_Object, node, {
8088 properties: [ node ],
8089 }) : node;
8090 }));
8091 });
8092 def(AST_ObjectIdentity, return_null);
8093 def(AST_Sequence, function(compressor, first_in_statement) {
8094 var expressions = trim(this.expressions, compressor, first_in_statement);
8095 if (!expressions) return null;
8096 var end = expressions.length - 1;
8097 var last = expressions[end];
8098 if (compressor.option("awaits") && end > 0 && last instanceof AST_Await && last.expression.is_constant()) {
8099 expressions = expressions.slice(0, -1);
8100 end--;
8101 last.expression = expressions[end];
8102 expressions[end] = last;
8103 }
8104 var assign, cond, lhs;
8105 if (compressor.option("conditionals")
8106 && end > 0
8107 && (assign = expressions[end - 1]) instanceof AST_Assign
8108 && assign.operator == "="
8109 && (lhs = assign.left) instanceof AST_SymbolRef
8110 && (cond = to_conditional_assignment(compressor, lhs.definition(), assign.right, last))) {
8111 assign = assign.clone();
8112 assign.right = cond;
8113 expressions = expressions.slice(0, -2);
8114 expressions.push(assign.drop_side_effect_free(compressor, first_in_statement));
8115 }
8116 return expressions === this.expressions ? this : make_sequence(this, expressions);
8117 });
8118 def(AST_Sub, function(compressor, first_in_statement) {
8119 var expr = this.expression;
8120 var prop = this.property;
8121 if (expr.may_throw_on_access(compressor)) {
8122 if (!this.optional) return this;
8123 if (prop.has_side_effects(compressor)) {
8124 prop = prop.drop_side_effect_free(compressor);
8125 if (!prop) return expr.drop_side_effect_free(compressor, first_in_statement);
8126 var node = this.clone();
8127 node.property = prop;
8128 return node;
8129 }
8130 }
8131 expr = expr.drop_side_effect_free(compressor, first_in_statement);
8132 if (!expr) return prop.drop_side_effect_free(compressor, first_in_statement);
8133 prop = prop.drop_side_effect_free(compressor);
8134 if (!prop) return expr;
8135 return make_sequence(this, [ expr, prop ]);
8136 });
8137 def(AST_SymbolRef, function(compressor) {
8138 return this.is_declared(compressor) && can_drop_symbol(this, compressor) ? null : this;
8139 });
8140 def(AST_Template, function(compressor, first_in_statement) {
8141 var self = this;
8142 if (self.is_expr_pure(compressor)) {
8143 var expressions = self.expressions;
8144 if (expressions.length == 0) return null;
8145 return make_sequence(self, expressions).drop_side_effect_free(compressor, first_in_statement);
8146 }
8147 var tag = self.tag;
8148 var dropped = drop_returns(compressor, tag);
8149 if (dropped) {
8150 // always shallow clone to signal internal changes
8151 self = self.clone();
8152 self.tag = dropped;
8153 // avoid extraneous traversal
8154 if (tag._squeezed) self.tag._squeezed = true;
8155 }
8156 return self;
8157 });
8158 def(AST_Unary, function(compressor, first_in_statement) {
8159 var exp = this.expression;
8160 if (unary_side_effects[this.operator]) {
8161 this.write_only = !exp.has_side_effects(compressor);
8162 return this;
8163 }
8164 if (this.operator == "typeof" && exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor)) {
8165 return null;
8166 }
8167 var node = exp.drop_side_effect_free(compressor, first_in_statement);
8168 if (first_in_statement && node && is_iife_call(node)) {
8169 if (node === exp && this.operator == "!") return this;
8170 return node.negate(compressor, first_in_statement);
8171 }
8172 return node;
8173 });
8174 })(function(node, func) {
8175 node.DEFMETHOD("drop_side_effect_free", func);
8176 });
8177
8178 OPT(AST_SimpleStatement, function(self, compressor) {
8179 if (compressor.option("side_effects")) {
8180 var body = self.body;
8181 var node = body.drop_side_effect_free(compressor, true);
8182 if (!node) {
8183 AST_Node.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
8184 return make_node(AST_EmptyStatement, self);
8185 }
8186 if (node !== body) {
8187 return make_node(AST_SimpleStatement, self, { body: node });
8188 }
8189 }
8190 return self;
8191 });
8192
8193 OPT(AST_While, function(self, compressor) {
8194 return compressor.option("loops") ? make_node(AST_For, self, self).optimize(compressor) : self;
8195 });
8196
8197 function has_loop_control(loop, parent, type) {
8198 if (!type) type = AST_LoopControl;
8199 var found = false;
8200 var tw = new TreeWalker(function(node) {
8201 if (found || node instanceof AST_Scope) return true;
8202 if (node instanceof type && tw.loopcontrol_target(node) === loop) {
8203 return found = true;
8204 }
8205 });
8206 if (parent instanceof AST_LabeledStatement) tw.push(parent);
8207 tw.push(loop);
8208 loop.body.walk(tw);
8209 return found;
8210 }
8211
8212 OPT(AST_Do, function(self, compressor) {
8213 if (!compressor.option("loops")) return self;
8214 var cond = fuzzy_eval(compressor, self.condition);
8215 if (!(cond instanceof AST_Node)) {
8216 if (cond && !has_loop_control(self, compressor.parent(), AST_Continue)) return make_node(AST_For, self, {
8217 body: make_node(AST_BlockStatement, self.body, {
8218 body: [
8219 self.body,
8220 make_node(AST_SimpleStatement, self.condition, {
8221 body: self.condition
8222 }),
8223 ]
8224 })
8225 }).optimize(compressor);
8226 if (!has_loop_control(self, compressor.parent())) return make_node(AST_BlockStatement, self.body, {
8227 body: [
8228 self.body,
8229 make_node(AST_SimpleStatement, self.condition, {
8230 body: self.condition
8231 }),
8232 ]
8233 }).optimize(compressor);
8234 }
8235 if (self.body instanceof AST_BlockStatement && !has_loop_control(self, compressor.parent(), AST_Continue)) {
8236 var body = self.body.body;
8237 for (var i = body.length; --i >= 0;) {
8238 var stat = body[i];
8239 if (stat instanceof AST_If
8240 && !stat.alternative
8241 && stat.body instanceof AST_Break
8242 && compressor.loopcontrol_target(stat.body) === self) {
8243 if (has_block_scope_refs(stat.condition)) break;
8244 self.condition = make_node(AST_Binary, self, {
8245 operator: "&&",
8246 left: stat.condition.negate(compressor),
8247 right: self.condition,
8248 });
8249 body.splice(i, 1);
8250 } else if (stat instanceof AST_SimpleStatement) {
8251 if (has_block_scope_refs(stat.body)) break;
8252 self.condition = make_sequence(self, [
8253 stat.body,
8254 self.condition,
8255 ]);
8256 body.splice(i, 1);
8257 } else if (!is_declaration(stat, true)) {
8258 break;
8259 }
8260 }
8261 self.body = trim_block(self.body, compressor.parent());
8262 }
8263 if (self.body instanceof AST_EmptyStatement) return make_node(AST_For, self, self).optimize(compressor);
8264 if (self.body instanceof AST_SimpleStatement) return make_node(AST_For, self, {
8265 condition: make_sequence(self.condition, [
8266 self.body.body,
8267 self.condition
8268 ]),
8269 body: make_node(AST_EmptyStatement, self)
8270 }).optimize(compressor);
8271 return self;
8272
8273 function has_block_scope_refs(node) {
8274 var found = false;
8275 node.walk(new TreeWalker(function(node) {
8276 if (found) return true;
8277 if (node instanceof AST_SymbolRef) {
8278 if (!member(node.definition(), self.enclosed)) found = true;
8279 return true;
8280 }
8281 }));
8282 return found;
8283 }
8284 });
8285
8286 function if_break_in_loop(self, compressor) {
8287 var first = first_statement(self.body);
8288 if (compressor.option("dead_code")
8289 && (first instanceof AST_Break
8290 || first instanceof AST_Continue && external_target(first)
8291 || first instanceof AST_Exit)) {
8292 var body = [];
8293 if (is_statement(self.init)) {
8294 body.push(self.init);
8295 } else if (self.init) {
8296 body.push(make_node(AST_SimpleStatement, self.init, {
8297 body: self.init
8298 }));
8299 }
8300 var retain = external_target(first) || first instanceof AST_Exit;
8301 if (self.condition && retain) {
8302 body.push(make_node(AST_If, self, {
8303 condition: self.condition,
8304 body: first,
8305 alternative: null
8306 }));
8307 } else if (self.condition) {
8308 body.push(make_node(AST_SimpleStatement, self.condition, {
8309 body: self.condition
8310 }));
8311 } else if (retain) {
8312 body.push(first);
8313 }
8314 extract_declarations_from_unreachable_code(compressor, self.body, body);
8315 return make_node(AST_BlockStatement, self, {
8316 body: body
8317 });
8318 }
8319 if (first instanceof AST_If) {
8320 var ab = first_statement(first.body);
8321 if (ab instanceof AST_Break && !external_target(ab)) {
8322 if (self.condition) {
8323 self.condition = make_node(AST_Binary, self.condition, {
8324 left: self.condition,
8325 operator: "&&",
8326 right: first.condition.negate(compressor),
8327 });
8328 } else {
8329 self.condition = first.condition.negate(compressor);
8330 }
8331 var body = as_statement_array(first.alternative);
8332 extract_declarations_from_unreachable_code(compressor, first.body, body);
8333 return drop_it(body);
8334 }
8335 ab = first_statement(first.alternative);
8336 if (ab instanceof AST_Break && !external_target(ab)) {
8337 if (self.condition) {
8338 self.condition = make_node(AST_Binary, self.condition, {
8339 left: self.condition,
8340 operator: "&&",
8341 right: first.condition,
8342 });
8343 } else {
8344 self.condition = first.condition;
8345 }
8346 var body = as_statement_array(first.body);
8347 extract_declarations_from_unreachable_code(compressor, first.alternative, body);
8348 return drop_it(body);
8349 }
8350 }
8351 return self;
8352
8353 function first_statement(body) {
8354 return body instanceof AST_BlockStatement ? body.body[0] : body;
8355 }
8356
8357 function external_target(node) {
8358 return compressor.loopcontrol_target(node) !== compressor.self();
8359 }
8360
8361 function drop_it(rest) {
8362 if (self.body instanceof AST_BlockStatement) {
8363 self.body = self.body.clone();
8364 self.body.body = rest.concat(self.body.body.slice(1));
8365 self.body = self.body.transform(compressor);
8366 } else {
8367 self.body = make_node(AST_BlockStatement, self.body, {
8368 body: rest
8369 }).transform(compressor);
8370 }
8371 return if_break_in_loop(self, compressor);
8372 }
8373 }
8374
8375 OPT(AST_For, function(self, compressor) {
8376 if (!compressor.option("loops")) return self;
8377 if (compressor.option("side_effects")) {
8378 if (self.init) self.init = self.init.drop_side_effect_free(compressor);
8379 if (self.step) self.step = self.step.drop_side_effect_free(compressor);
8380 }
8381 if (self.condition) {
8382 var cond = fuzzy_eval(compressor, self.condition);
8383 if (!cond) {
8384 if (compressor.option("dead_code")) {
8385 var body = [];
8386 if (is_statement(self.init)) {
8387 body.push(self.init);
8388 } else if (self.init) {
8389 body.push(make_node(AST_SimpleStatement, self.init, { body: self.init }));
8390 }
8391 body.push(make_node(AST_SimpleStatement, self.condition, { body: self.condition }));
8392 extract_declarations_from_unreachable_code(compressor, self.body, body);
8393 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
8394 }
8395 } else if (!(cond instanceof AST_Node)) {
8396 self.body = make_node(AST_BlockStatement, self.body, {
8397 body: [
8398 make_node(AST_SimpleStatement, self.condition, { body: self.condition }),
8399 self.body,
8400 ],
8401 });
8402 self.condition = null;
8403 }
8404 }
8405 return if_break_in_loop(self, compressor);
8406 });
8407
8408 OPT(AST_ForEnumeration, function(self, compressor) {
8409 if (compressor.option("varify") && is_lexical_definition(self.init)) {
8410 var name = self.init.definitions[0].name;
8411 if ((name instanceof AST_Destructured || name instanceof AST_SymbolLet)
8412 && !name.match_symbol(function(node) {
8413 if (node instanceof AST_SymbolDeclaration) {
8414 var def = node.definition();
8415 return !same_scope(def) || may_overlap(compressor, def);
8416 }
8417 }, true)) {
8418 self.init = to_var(self.init);
8419 }
8420 }
8421 return self;
8422 });
8423
8424 function mark_locally_defined(condition, consequent, alternative) {
8425 if (!(condition instanceof AST_Binary)) return;
8426 if (!(condition.left instanceof AST_String)) {
8427 switch (condition.operator) {
8428 case "&&":
8429 mark_locally_defined(condition.left, consequent);
8430 mark_locally_defined(condition.right, consequent);
8431 break;
8432 case "||":
8433 mark_locally_defined(negate(condition.left), alternative);
8434 mark_locally_defined(negate(condition.right), alternative);
8435 break;
8436 }
8437 return;
8438 }
8439 if (!(condition.right instanceof AST_UnaryPrefix)) return;
8440 if (condition.right.operator != "typeof") return;
8441 var sym = condition.right.expression;
8442 if (!is_undeclared_ref(sym)) return;
8443 var body;
8444 var undef = condition.left.value == "undefined";
8445 switch (condition.operator) {
8446 case "==":
8447 body = undef ? alternative : consequent;
8448 break;
8449 case "!=":
8450 body = undef ? consequent : alternative;
8451 break;
8452 default:
8453 return;
8454 }
8455 if (!body) return;
8456 var def = sym.definition();
8457 var tw = new TreeWalker(function(node) {
8458 if (node instanceof AST_Scope) {
8459 var parent = tw.parent();
8460 if (parent instanceof AST_Call && parent.expression === node) return;
8461 return true;
8462 }
8463 if (node instanceof AST_SymbolRef && node.definition() === def) node.defined = true;
8464 });
8465 body.walk(tw);
8466
8467 function negate(node) {
8468 if (!(node instanceof AST_Binary)) return;
8469 switch (node.operator) {
8470 case "==":
8471 node = node.clone();
8472 node.operator = "!=";
8473 return node;
8474 case "!=":
8475 node = node.clone();
8476 node.operator = "==";
8477 return node;
8478 }
8479 }
8480 }
8481
8482 function fuzzy_eval(compressor, node, nullish) {
8483 if (node.truthy) return true;
8484 if (node.falsy && !nullish) return false;
8485 if (node.is_truthy()) return true;
8486 return node.evaluate(compressor, true);
8487 }
8488
8489 function mark_duplicate_condition(compressor, node) {
8490 var child;
8491 var level = 0;
8492 var negated = false;
8493 var parent = compressor.self();
8494 if (!is_statement(parent)) while (true) {
8495 child = parent;
8496 parent = compressor.parent(level++);
8497 if (parent instanceof AST_Binary) {
8498 var op = parent.operator;
8499 if (!lazy_op[op]) return;
8500 var left = parent.left;
8501 if (left === child) continue;
8502 if (match(left)) switch (op) {
8503 case "&&":
8504 node[negated ? "falsy" : "truthy"] = true;
8505 break;
8506 case "||":
8507 case "??":
8508 node[negated ? "truthy" : "falsy"] = true;
8509 break;
8510 }
8511 } else if (parent instanceof AST_Conditional) {
8512 var cond = parent.condition;
8513 if (cond === child) continue;
8514 if (match(cond)) switch (child) {
8515 case parent.consequent:
8516 node[negated ? "falsy" : "truthy"] = true;
8517 break;
8518 case parent.alternative:
8519 node[negated ? "truthy" : "falsy"] = true;
8520 break;
8521 }
8522 } else if (parent instanceof AST_Exit) {
8523 break;
8524 } else if (parent instanceof AST_If) {
8525 break;
8526 } else if (parent instanceof AST_Sequence) {
8527 if (parent.expressions[0] === child) continue;
8528 } else if (parent instanceof AST_SimpleStatement) {
8529 break;
8530 }
8531 return;
8532 }
8533 while (true) {
8534 child = parent;
8535 parent = compressor.parent(level++);
8536 if (parent instanceof AST_BlockStatement) {
8537 if (parent.body[0] === child) continue;
8538 } else if (parent instanceof AST_If) {
8539 if (match(parent.condition)) switch (child) {
8540 case parent.body:
8541 node[negated ? "falsy" : "truthy"] = true;
8542 break;
8543 case parent.alternative:
8544 node[negated ? "truthy" : "falsy"] = true;
8545 break;
8546 }
8547 }
8548 return;
8549 }
8550
8551 function match(cond) {
8552 if (node.equivalent_to(cond)) return true;
8553 if (!(cond instanceof AST_UnaryPrefix)) return false;
8554 if (cond.operator != "!") return false;
8555 if (!node.equivalent_to(cond.expression)) return false;
8556 negated = true;
8557 return true;
8558 }
8559 }
8560
8561 OPT(AST_If, function(self, compressor) {
8562 if (is_empty(self.alternative)) self.alternative = null;
8563
8564 if (!compressor.option("conditionals")) return self;
8565 if (compressor.option("booleans") && !self.condition.has_side_effects(compressor)) {
8566 mark_duplicate_condition(compressor, self.condition);
8567 }
8568 // if condition can be statically determined, warn and drop
8569 // one of the blocks. note, statically determined implies
8570 // “has no side effects”; also it doesn't work for cases like
8571 // `x && true`, though it probably should.
8572 if (compressor.option("dead_code")) {
8573 var cond = fuzzy_eval(compressor, self.condition);
8574 if (!cond) {
8575 AST_Node.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
8576 var body = [ make_node(AST_SimpleStatement, self.condition, { body: self.condition }) ];
8577 extract_declarations_from_unreachable_code(compressor, self.body, body);
8578 if (self.alternative) body.push(self.alternative);
8579 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
8580 } else if (!(cond instanceof AST_Node)) {
8581 AST_Node.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
8582 var body = [
8583 make_node(AST_SimpleStatement, self.condition, { body: self.condition }),
8584 self.body,
8585 ];
8586 if (self.alternative) extract_declarations_from_unreachable_code(compressor, self.alternative, body);
8587 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
8588 }
8589 }
8590 var negated = self.condition.negate(compressor);
8591 var self_condition_length = self.condition.print_to_string().length;
8592 var negated_length = negated.print_to_string().length;
8593 var negated_is_best = negated_length < self_condition_length;
8594 if (self.alternative && negated_is_best) {
8595 negated_is_best = false; // because we already do the switch here.
8596 // no need to swap values of self_condition_length and negated_length
8597 // here because they are only used in an equality comparison later on.
8598 self.condition = negated;
8599 var tmp = self.body;
8600 self.body = self.alternative || make_node(AST_EmptyStatement, self);
8601 self.alternative = tmp;
8602 }
8603 var body = [], var_defs = [], refs = [];
8604 var body_exprs = sequencesize(self.body, body, var_defs, refs);
8605 var alt_exprs = sequencesize(self.alternative, body, var_defs, refs);
8606 if (body_exprs && alt_exprs) {
8607 if (var_defs.length > 0) body.push(make_node(AST_Var, self, { definitions: var_defs }));
8608 if (body_exprs.length == 0) {
8609 body.push(make_node(AST_SimpleStatement, self.condition, {
8610 body: alt_exprs.length > 0 ? make_node(AST_Binary, self, {
8611 operator : "||",
8612 left : self.condition,
8613 right : make_sequence(self.alternative, alt_exprs)
8614 }).transform(compressor) : self.condition.clone()
8615 }).optimize(compressor));
8616 } else if (alt_exprs.length == 0) {
8617 if (self_condition_length === negated_length && !negated_is_best
8618 && self.condition instanceof AST_Binary && self.condition.operator == "||") {
8619 // although the code length of self.condition and negated are the same,
8620 // negated does not require additional surrounding parentheses.
8621 // see https://github.com/mishoo/UglifyJS/issues/979
8622 negated_is_best = true;
8623 }
8624 body.push(make_node(AST_SimpleStatement, self, {
8625 body: make_node(AST_Binary, self, {
8626 operator : negated_is_best ? "||" : "&&",
8627 left : negated_is_best ? negated : self.condition,
8628 right : make_sequence(self.body, body_exprs)
8629 }).transform(compressor)
8630 }).optimize(compressor));
8631 } else {
8632 body.push(make_node(AST_SimpleStatement, self, {
8633 body: make_node(AST_Conditional, self, {
8634 condition : self.condition,
8635 consequent : make_sequence(self.body, body_exprs),
8636 alternative : make_sequence(self.alternative, alt_exprs)
8637 })
8638 }).optimize(compressor));
8639 }
8640 refs.forEach(function(ref) {
8641 ref.definition().references.push(ref);
8642 });
8643 return make_node(AST_BlockStatement, self, {
8644 body: body
8645 }).optimize(compressor);
8646 }
8647 if (is_empty(self.body)) {
8648 self = make_node(AST_If, self, {
8649 condition: negated,
8650 body: self.alternative,
8651 alternative: null
8652 });
8653 }
8654 if (self.body instanceof AST_Exit
8655 && self.alternative instanceof AST_Exit
8656 && self.body.TYPE == self.alternative.TYPE) {
8657 var exit = make_node(self.body.CTOR, self, {
8658 value: make_node(AST_Conditional, self, {
8659 condition : self.condition,
8660 consequent : self.body.value || make_node(AST_Undefined, self.body).transform(compressor),
8661 alternative : self.alternative.value || make_node(AST_Undefined, self.alternative).transform(compressor)
8662 })
8663 });
8664 if (exit instanceof AST_Return) {
8665 exit.in_bool = self.body.in_bool || self.alternative.in_bool;
8666 }
8667 return exit;
8668 }
8669 if (self.body instanceof AST_If
8670 && !self.body.alternative
8671 && !self.alternative) {
8672 self = make_node(AST_If, self, {
8673 condition: make_node(AST_Binary, self.condition, {
8674 operator: "&&",
8675 left: self.condition,
8676 right: self.body.condition
8677 }),
8678 body: self.body.body,
8679 alternative: null
8680 });
8681 }
8682 if (aborts(self.body)) {
8683 if (self.alternative) {
8684 var alt = self.alternative;
8685 self.alternative = null;
8686 return make_node(AST_BlockStatement, self, {
8687 body: [ self, alt ]
8688 }).optimize(compressor);
8689 }
8690 }
8691 if (aborts(self.alternative)) {
8692 var body = self.body;
8693 self.body = self.alternative;
8694 self.condition = negated_is_best ? negated : self.condition.negate(compressor);
8695 self.alternative = null;
8696 return make_node(AST_BlockStatement, self, {
8697 body: [ self, body ]
8698 }).optimize(compressor);
8699 }
8700 if (compressor.option("typeofs")) mark_locally_defined(self.condition, self.body, self.alternative);
8701 return self;
8702
8703 function sequencesize(stat, defuns, var_defs, refs) {
8704 if (stat == null) return [];
8705 if (stat instanceof AST_BlockStatement) {
8706 var exprs = [];
8707 for (var i = 0; i < stat.body.length; i++) {
8708 var line = stat.body[i];
8709 if (line instanceof AST_LambdaDefinition) {
8710 defuns.push(line);
8711 } else if (line instanceof AST_EmptyStatement) {
8712 continue;
8713 } else if (line instanceof AST_SimpleStatement) {
8714 if (!compressor.option("sequences") && exprs.length > 0) return;
8715 exprs.push(line.body);
8716 } else if (line instanceof AST_Var) {
8717 if (!compressor.option("sequences") && exprs.length > 0) return;
8718 line.remove_initializers(compressor, var_defs);
8719 line.definitions.forEach(process_var_def);
8720 } else {
8721 return;
8722 }
8723 }
8724 return exprs;
8725 }
8726 if (stat instanceof AST_LambdaDefinition) {
8727 defuns.push(stat);
8728 return [];
8729 }
8730 if (stat instanceof AST_EmptyStatement) return [];
8731 if (stat instanceof AST_SimpleStatement) return [ stat.body ];
8732 if (stat instanceof AST_Var) {
8733 var exprs = [];
8734 stat.remove_initializers(compressor, var_defs);
8735 stat.definitions.forEach(process_var_def);
8736 return exprs;
8737 }
8738
8739 function process_var_def(var_def) {
8740 if (!var_def.value) return;
8741 exprs.push(make_node(AST_Assign, var_def, {
8742 operator: "=",
8743 left: var_def.name.convert_symbol(AST_SymbolRef, function(ref) {
8744 refs.push(ref);
8745 }),
8746 right: var_def.value
8747 }));
8748 }
8749 }
8750 });
8751
8752 OPT(AST_Switch, function(self, compressor) {
8753 if (!compressor.option("switches")) return self;
8754 if (!compressor.option("dead_code")) return self;
8755 var body = [];
8756 var branch;
8757 var decl = [];
8758 var default_branch;
8759 var exact_match;
8760 var side_effects = [];
8761 for (var i = 0, len = self.body.length; i < len; i++) {
8762 branch = self.body[i];
8763 if (branch instanceof AST_Default) {
8764 var prev = body[body.length - 1];
8765 if (default_branch || is_break(branch.body[0], compressor) && (!prev || aborts(prev))) {
8766 eliminate_branch(branch, prev);
8767 continue;
8768 } else {
8769 default_branch = branch;
8770 }
8771 } else {
8772 var exp = branch.expression;
8773 var equals = make_node(AST_Binary, self, {
8774 operator: "===",
8775 left: self.expression,
8776 right: exp,
8777 }).evaluate(compressor, true);
8778 if (!equals) {
8779 if (exp.has_side_effects(compressor)) side_effects.push(exp);
8780 eliminate_branch(branch, body[body.length - 1]);
8781 continue;
8782 }
8783 if (!(equals instanceof AST_Node)) {
8784 if (default_branch) {
8785 var default_index = body.indexOf(default_branch);
8786 body.splice(default_index, 1);
8787 eliminate_branch(default_branch, body[default_index - 1]);
8788 default_branch = null;
8789 }
8790 if (exp.has_side_effects(compressor)) {
8791 exact_match = branch;
8792 } else {
8793 default_branch = branch = make_node(AST_Default, branch, branch);
8794 }
8795 while (++i < len) eliminate_branch(self.body[i], branch);
8796 }
8797 }
8798 if (i + 1 >= len || aborts(branch)) {
8799 var prev = body[body.length - 1];
8800 var statements = branch.body;
8801 if (aborts(prev)) switch (prev.body.length - statements.length) {
8802 case 1:
8803 var stat = prev.body[prev.body.length - 1];
8804 if (!is_break(stat, compressor)) break;
8805 statements = statements.concat(stat);
8806 case 0:
8807 var prev_block = make_node(AST_BlockStatement, prev, prev);
8808 var next_block = make_node(AST_BlockStatement, branch, { body: statements });
8809 if (prev_block.equivalent_to(next_block)) prev.body = [];
8810 }
8811 }
8812 if (side_effects.length) {
8813 if (branch instanceof AST_Default) {
8814 body.push(make_node(AST_Case, self, { expression: make_sequence(self, side_effects), body: [] }));
8815 } else {
8816 side_effects.push(branch.expression);
8817 branch.expression = make_sequence(self, side_effects);
8818 }
8819 side_effects = [];
8820 }
8821 body.push(branch);
8822 }
8823 if (side_effects.length && !exact_match) {
8824 body.push(make_node(AST_Case, self, { expression: make_sequence(self, side_effects), body: [] }));
8825 }
8826 while (branch = body[body.length - 1]) {
8827 var stat = branch.body[branch.body.length - 1];
8828 if (is_break(stat, compressor)) branch.body.pop();
8829 if (branch === default_branch) {
8830 if (!has_declarations_only(branch)) break;
8831 } else if (branch.expression.has_side_effects(compressor)) {
8832 break;
8833 } else if (default_branch) {
8834 if (!has_declarations_only(default_branch)) break;
8835 if (body[body.length - 2] !== default_branch) break;
8836 default_branch.body = default_branch.body.concat(branch.body);
8837 branch.body = [];
8838 } else if (!has_declarations_only(branch)) break;
8839 eliminate_branch(branch);
8840 if (body.pop() === default_branch) default_branch = null;
8841 }
8842 if (!branch) {
8843 decl.push(make_node(AST_SimpleStatement, self.expression, { body: self.expression }));
8844 if (side_effects.length) decl.push(make_node(AST_SimpleStatement, self, {
8845 body: make_sequence(self, side_effects),
8846 }));
8847 return make_node(AST_BlockStatement, self, { body: decl }).optimize(compressor);
8848 }
8849 if (branch === default_branch) while (branch = body[body.length - 2]) {
8850 if (branch instanceof AST_Default) break;
8851 if (!has_declarations_only(branch)) break;
8852 var exp = branch.expression;
8853 if (exp.has_side_effects(compressor)) {
8854 var prev = body[body.length - 3];
8855 if (prev && !aborts(prev)) break;
8856 default_branch.body.unshift(make_node(AST_SimpleStatement, self, { body: exp }));
8857 }
8858 eliminate_branch(branch);
8859 body.splice(-2, 1);
8860 }
8861 body[0].body = decl.concat(body[0].body);
8862 self.body = body;
8863 if (compressor.option("conditionals")) switch (body.length) {
8864 case 1:
8865 if (!no_break(body[0])) break;
8866 var exp = body[0].expression;
8867 var statements = body[0].body.slice();
8868 if (body[0] !== default_branch && body[0] !== exact_match) return make_node(AST_If, self, {
8869 condition: make_node(AST_Binary, self, {
8870 operator: "===",
8871 left: self.expression,
8872 right: exp,
8873 }),
8874 body: make_node(AST_BlockStatement, self, {
8875 body: statements,
8876 }),
8877 alternative: null,
8878 }).optimize(compressor);
8879 if (exp) statements.unshift(make_node(AST_SimpleStatement, exp, {
8880 body: exp,
8881 }));
8882 statements.unshift(make_node(AST_SimpleStatement, self.expression, {
8883 body:self.expression,
8884 }));
8885 return make_node(AST_BlockStatement, self, {
8886 body: statements,
8887 }).optimize(compressor);
8888 case 2:
8889 if (!member(default_branch, body) || !no_break(body[1])) break;
8890 var statements = body[0].body.slice();
8891 var exclusive = statements.length && is_break(statements[statements.length - 1], compressor);
8892 if (exclusive) statements.pop();
8893 if (!all(statements, no_break)) break;
8894 var alternative = body[1].body.length && make_node(AST_BlockStatement, body[1], body[1]);
8895 var node = make_node(AST_If, self, {
8896 condition: make_node(AST_Binary, self, body[0] === default_branch ? {
8897 operator: "!==",
8898 left: self.expression,
8899 right: body[1].expression,
8900 } : {
8901 operator: "===",
8902 left: self.expression,
8903 right: body[0].expression,
8904 }),
8905 body: make_node(AST_BlockStatement, body[0], {
8906 body: statements,
8907 }),
8908 alternative: exclusive && alternative || null,
8909 });
8910 if (!exclusive && alternative) node = make_node(AST_BlockStatement, self, {
8911 body: [ node, alternative ],
8912 });
8913 return node.optimize(compressor);
8914 }
8915 return self;
8916
8917 function is_break(node, tw) {
8918 return node instanceof AST_Break && tw.loopcontrol_target(node) === self;
8919 }
8920
8921 function no_break(node) {
8922 var found = false;
8923 var tw = new TreeWalker(function(node) {
8924 if (found
8925 || node instanceof AST_Lambda
8926 || node instanceof AST_SimpleStatement) return true;
8927 if (is_break(node, tw)) found = true;
8928 });
8929 tw.push(self);
8930 node.walk(tw);
8931 return !found;
8932 }
8933
8934 function eliminate_branch(branch, prev) {
8935 if (prev && !aborts(prev)) {
8936 prev.body = prev.body.concat(branch.body);
8937 } else {
8938 extract_declarations_from_unreachable_code(compressor, branch, decl);
8939 }
8940 }
8941 });
8942
8943 OPT(AST_Try, function(self, compressor) {
8944 self.body = tighten_body(self.body, compressor);
8945 if (compressor.option("dead_code")) {
8946 if (has_declarations_only(self)
8947 && !(self.bcatch && self.bcatch.argname && self.bcatch.argname.match_symbol(function(node) {
8948 return node instanceof AST_SymbolCatch && !can_drop_symbol(node);
8949 }, true))) {
8950 var body = [];
8951 if (self.bcatch) {
8952 extract_declarations_from_unreachable_code(compressor, self.bcatch, body);
8953 body.forEach(function(stat) {
8954 if (!(stat instanceof AST_Var)) return;
8955 stat.definitions.forEach(function(var_def) {
8956 var def = var_def.name.definition().redefined();
8957 if (!def) return;
8958 var_def.name = var_def.name.clone();
8959 var_def.name.thedef = def;
8960 });
8961 });
8962 }
8963 body.unshift(make_node(AST_BlockStatement, self, self).optimize(compressor));
8964 if (self.bfinally) {
8965 body.push(make_node(AST_BlockStatement, self.bfinally, self.bfinally).optimize(compressor));
8966 }
8967 return make_node(AST_BlockStatement, self, {
8968 body: body
8969 }).optimize(compressor);
8970 }
8971 if (self.bfinally && has_declarations_only(self.bfinally)) {
8972 var body = make_node(AST_BlockStatement, self.bfinally, self.bfinally).optimize(compressor);
8973 body = self.body.concat(body);
8974 if (!self.bcatch) return make_node(AST_BlockStatement, self, {
8975 body: body
8976 }).optimize(compressor);
8977 self.body = body;
8978 self.bfinally = null;
8979 }
8980 }
8981 return self;
8982 });
8983
8984 function remove_initializers(make_value) {
8985 return function(compressor, defns) {
8986 var dropped = false;
8987 this.definitions.forEach(function(defn) {
8988 if (defn.value) dropped = true;
8989 defn.name.match_symbol(function(node) {
8990 if (node instanceof AST_SymbolDeclaration) defns.push(make_node(AST_VarDef, node, {
8991 name: node,
8992 value: make_value(compressor, node)
8993 }));
8994 }, true);
8995 });
8996 return dropped;
8997 };
8998 }
8999
9000 AST_Const.DEFMETHOD("remove_initializers", remove_initializers(function(compressor, node) {
9001 return make_node(AST_Undefined, node).optimize(compressor);
9002 }));
9003 AST_Let.DEFMETHOD("remove_initializers", remove_initializers(return_null));
9004 AST_Var.DEFMETHOD("remove_initializers", remove_initializers(return_null));
9005
9006 AST_Definitions.DEFMETHOD("to_assignments", function() {
9007 var assignments = this.definitions.reduce(function(a, defn) {
9008 var def = defn.name.definition();
9009 var value = defn.value;
9010 if (value) {
9011 if (value instanceof AST_Sequence) value = value.clone();
9012 var name = make_node(AST_SymbolRef, defn.name, defn.name);
9013 var assign = make_node(AST_Assign, defn, {
9014 operator: "=",
9015 left: name,
9016 right: value,
9017 });
9018 a.push(assign);
9019 name.fixed = function() {
9020 return assign.right;
9021 };
9022 name.fixed.assigns = [ assign ];
9023 def.references.forEach(function(ref) {
9024 var assigns = ref.fixed && ref.fixed.assigns;
9025 if (assigns && assigns[0] === defn) assigns[0] = assign;
9026 });
9027 def.references.push(name);
9028 }
9029 def.eliminated++;
9030 def.single_use = false;
9031 return a;
9032 }, []);
9033 if (assignments.length == 0) return null;
9034 return make_sequence(this, assignments);
9035 });
9036
9037 function is_safe_lexical(def) {
9038 return def.name != "arguments" && def.orig.length < (def.orig[0] instanceof AST_SymbolLambda ? 3 : 2);
9039 }
9040
9041 function may_overlap(compressor, def) {
9042 if (compressor.exposed(def)) return true;
9043 var scope = def.scope.resolve();
9044 for (var s = def.scope; s !== scope;) {
9045 s = s.parent_scope;
9046 if (s.var_names()[def.name]) return true;
9047 }
9048 }
9049
9050 function to_var(stat) {
9051 return make_node(AST_Var, stat, {
9052 definitions: stat.definitions.map(function(defn) {
9053 return make_node(AST_VarDef, defn, {
9054 name: defn.name.convert_symbol(AST_SymbolVar, function(name, node) {
9055 var def = name.definition();
9056 def.orig[def.orig.indexOf(node)] = name;
9057 var scope = def.scope.resolve();
9058 if (def.scope === scope) return;
9059 def.scope = scope;
9060 scope.variables.set(def.name, def);
9061 scope.enclosed.push(def);
9062 scope.var_names()[def.name] = true;
9063 }),
9064 value: defn.value,
9065 });
9066 })
9067 });
9068 }
9069
9070 function can_varify(compressor, sym) {
9071 if (!sym.fixed_value()) return false;
9072 var def = sym.definition();
9073 return is_safe_lexical(def) && same_scope(def) && !may_overlap(compressor, def);
9074 }
9075
9076 function varify(self, compressor) {
9077 return compressor.option("varify") && all(self.definitions, function(defn) {
9078 return !defn.name.match_symbol(function(node) {
9079 if (node instanceof AST_SymbolDeclaration) return !can_varify(compressor, node);
9080 }, true);
9081 }) ? to_var(self) : self;
9082 }
9083
9084 OPT(AST_Const, varify);
9085 OPT(AST_Let, varify);
9086
9087 function trim_optional_chain(node, compressor) {
9088 if (!compressor.option("optional_chains")) return;
9089 if (node.terminal) do {
9090 var expr = node.expression;
9091 if (node.optional) {
9092 var ev = fuzzy_eval(compressor, expr, true);
9093 if (ev == null) return make_node(AST_UnaryPrefix, node, {
9094 operator: "void",
9095 expression: expr,
9096 }).optimize(compressor);
9097 if (!(ev instanceof AST_Node)) node.optional = false;
9098 }
9099 node = expr;
9100 } while ((node.TYPE == "Call" || node instanceof AST_PropAccess) && !node.terminal);
9101 }
9102
9103 function lift_sequence_in_expression(node, compressor) {
9104 var exp = node.expression;
9105 if (!(exp instanceof AST_Sequence)) return node;
9106 var x = exp.expressions.slice();
9107 var e = node.clone();
9108 e.expression = x.pop();
9109 x.push(e);
9110 return make_sequence(node, x);
9111 }
9112
9113 function drop_unused_call_args(call, compressor, fns_with_marked_args) {
9114 var exp = call.expression;
9115 var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
9116 if (!(fn instanceof AST_Lambda)) return;
9117 if (fn.uses_arguments) return;
9118 if (fn.pinned()) return;
9119 if (fns_with_marked_args && fns_with_marked_args.indexOf(fn) < 0) return;
9120 var args = call.args;
9121 if (!all(args, function(arg) {
9122 return !(arg instanceof AST_Spread);
9123 })) return;
9124 var argnames = fn.argnames;
9125 var is_iife = fn === exp && !fn.name;
9126 if (fn.rest) {
9127 if (!(is_iife && compressor.option("rests"))) return;
9128 var insert = argnames.length;
9129 args = args.slice(0, insert);
9130 while (args.length < insert) args.push(make_node(AST_Undefined, call).optimize(compressor));
9131 args.push(make_node(AST_Array, call, { elements: call.args.slice(insert) }));
9132 argnames = argnames.concat(fn.rest);
9133 fn.rest = null;
9134 } else {
9135 args = args.slice();
9136 argnames = argnames.slice();
9137 }
9138 var pos = 0, last = 0;
9139 var drop_defaults = is_iife && compressor.option("default_values");
9140 var drop_fargs = is_iife && compressor.drop_fargs(fn, call) ? function(argname, arg) {
9141 if (!argname) return true;
9142 if (argname instanceof AST_DestructuredArray) {
9143 return argname.elements.length == 0 && !argname.rest && arg instanceof AST_Array;
9144 }
9145 if (argname instanceof AST_DestructuredObject) {
9146 return argname.properties.length == 0 && !argname.rest && arg && !arg.may_throw_on_access(compressor);
9147 }
9148 return argname.__unused;
9149 } : return_false;
9150 var side_effects = [];
9151 for (var i = 0; i < args.length; i++) {
9152 var argname = argnames[i];
9153 if (drop_defaults && argname instanceof AST_DefaultValue && args[i].is_defined(compressor)) {
9154 argnames[i] = argname = argname.name;
9155 }
9156 if (!argname || "__unused" in argname) {
9157 var node = args[i].drop_side_effect_free(compressor);
9158 if (drop_fargs(argname)) {
9159 if (argname) argnames.splice(i, 1);
9160 args.splice(i, 1);
9161 if (node) side_effects.push(node);
9162 i--;
9163 continue;
9164 } else if (node) {
9165 side_effects.push(node);
9166 args[pos++] = make_sequence(call, side_effects);
9167 side_effects = [];
9168 } else if (argname) {
9169 if (side_effects.length) {
9170 args[pos++] = make_sequence(call, side_effects);
9171 side_effects = [];
9172 } else {
9173 args[pos++] = make_node(AST_Number, args[i], {
9174 value: 0
9175 });
9176 continue;
9177 }
9178 }
9179 } else if (drop_fargs(argname, args[i])) {
9180 var node = args[i].drop_side_effect_free(compressor);
9181 argnames.splice(i, 1);
9182 args.splice(i, 1);
9183 if (node) side_effects.push(node);
9184 i--;
9185 continue;
9186 } else {
9187 side_effects.push(args[i]);
9188 args[pos++] = make_sequence(call, side_effects);
9189 side_effects = [];
9190 }
9191 last = pos;
9192 }
9193 for (; i < argnames.length; i++) {
9194 if (drop_fargs(argnames[i])) argnames.splice(i--, 1);
9195 }
9196 fn.argnames = argnames;
9197 args.length = last;
9198 call.args = args;
9199 if (!side_effects.length) return;
9200 var arg = make_sequence(call, side_effects);
9201 args.push(args.length < argnames.length ? make_node(AST_UnaryPrefix, call, {
9202 operator: "void",
9203 expression: arg,
9204 }) : arg);
9205 }
9206
9207 OPT(AST_Call, function(self, compressor) {
9208 var exp = self.expression;
9209 var terminated = trim_optional_chain(self, compressor);
9210 if (terminated) return terminated;
9211 if (compressor.option("sequences")) {
9212 if (exp instanceof AST_PropAccess) {
9213 var seq = lift_sequence_in_expression(exp, compressor);
9214 if (seq !== exp) {
9215 var call = self.clone();
9216 call.expression = seq.expressions.pop();
9217 seq.expressions.push(call);
9218 return seq.optimize(compressor);
9219 }
9220 } else if (!needs_unbinding(compressor, exp.tail_node())) {
9221 var seq = lift_sequence_in_expression(self, compressor);
9222 if (seq !== self) return seq.optimize(compressor);
9223 }
9224 }
9225 if (compressor.option("unused")) drop_unused_call_args(self, compressor);
9226 if (compressor.option("unsafe")) {
9227 if (is_undeclared_ref(exp)) switch (exp.name) {
9228 case "Array":
9229 // Array(n) ---> [ , , ... , ]
9230 if (self.args.length == 1) {
9231 var first = self.args[0];
9232 if (first instanceof AST_Number) try {
9233 var length = first.value;
9234 if (length > 6) break;
9235 var elements = Array(length);
9236 for (var i = 0; i < length; i++) elements[i] = make_node(AST_Hole, self);
9237 return make_node(AST_Array, self, { elements: elements });
9238 } catch (ex) {
9239 AST_Node.warn("Invalid array length: {length} [{file}:{line},{col}]", {
9240 length: length,
9241 file: self.start.file,
9242 line: self.start.line,
9243 col: self.start.col,
9244 });
9245 break;
9246 }
9247 if (!first.is_boolean(compressor) && !first.is_string(compressor)) break;
9248 }
9249 // Array(...) ---> [ ... ]
9250 return make_node(AST_Array, self, { elements: self.args });
9251 case "Object":
9252 // Object() ---> {}
9253 if (self.args.length == 0) return make_node(AST_Object, self, { properties: [] });
9254 break;
9255 case "String":
9256 // String() ---> ""
9257 if (self.args.length == 0) return make_node(AST_String, self, { value: "" });
9258 // String(x) ---> "" + x
9259 if (self.args.length == 1) return make_node(AST_Binary, self, {
9260 operator: "+",
9261 left: make_node(AST_String, self, { value: "" }),
9262 right: self.args[0],
9263 }).optimize(compressor);
9264 break;
9265 case "Number":
9266 // Number() ---> 0
9267 if (self.args.length == 0) return make_node(AST_Number, self, { value: 0 });
9268 // Number(x) ---> +("" + x)
9269 if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
9270 operator: "+",
9271 expression: make_node(AST_Binary, self, {
9272 operator: "+",
9273 left: make_node(AST_String, self, { value: "" }),
9274 right: self.args[0],
9275 }),
9276 }).optimize(compressor);
9277 break;
9278 case "Boolean":
9279 // Boolean() ---> false
9280 if (self.args.length == 0) return make_node(AST_False, self).optimize(compressor);
9281 // Boolean(x) ---> !!x
9282 if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
9283 operator: "!",
9284 expression: make_node(AST_UnaryPrefix, self, {
9285 operator: "!",
9286 expression: self.args[0],
9287 }),
9288 }).optimize(compressor);
9289 break;
9290 case "RegExp":
9291 // attempt to convert RegExp(...) to literal
9292 var params = [];
9293 if (all(self.args, function(arg) {
9294 var value = arg.evaluate(compressor);
9295 params.unshift(value);
9296 return arg !== value;
9297 })) try {
9298 return best_of(compressor, self, make_node(AST_RegExp, self, {
9299 value: RegExp.apply(RegExp, params),
9300 }));
9301 } catch (ex) {
9302 AST_Node.warn("Error converting {expr} [{file}:{line},{col}]", {
9303 expr: self,
9304 file: self.start.file,
9305 line: self.start.line,
9306 col: self.start.col,
9307 });
9308 }
9309 break;
9310 } else if (exp instanceof AST_Dot) switch (exp.property) {
9311 case "toString":
9312 // x.toString() ---> "" + x
9313 var expr = exp.expression;
9314 if (self.args.length == 0 && !(expr.may_throw_on_access(compressor) || expr instanceof AST_Super)) {
9315 return make_node(AST_Binary, self, {
9316 operator: "+",
9317 left: make_node(AST_String, self, { value: "" }),
9318 right: expr,
9319 }).optimize(compressor);
9320 }
9321 break;
9322 case "join":
9323 if (exp.expression instanceof AST_Array && self.args.length < 2) EXIT: {
9324 var separator = self.args[0];
9325 // [].join() ---> ""
9326 // [].join(x) ---> (x, "")
9327 if (exp.expression.elements.length == 0 && !(separator instanceof AST_Spread)) {
9328 return separator ? make_sequence(self, [
9329 separator,
9330 make_node(AST_String, self, { value: "" }),
9331 ]).optimize(compressor) : make_node(AST_String, self, { value: "" });
9332 }
9333 if (separator) {
9334 separator = separator.evaluate(compressor);
9335 if (separator instanceof AST_Node) break EXIT; // not a constant
9336 }
9337 var elements = [];
9338 var consts = [];
9339 for (var i = 0; i < exp.expression.elements.length; i++) {
9340 var el = exp.expression.elements[i];
9341 var value = el.evaluate(compressor);
9342 if (value !== el) {
9343 consts.push(value);
9344 } else if (el instanceof AST_Spread) {
9345 break EXIT;
9346 } else {
9347 if (consts.length > 0) {
9348 elements.push(make_node(AST_String, self, {
9349 value: consts.join(separator),
9350 }));
9351 consts.length = 0;
9352 }
9353 elements.push(el);
9354 }
9355 }
9356 if (consts.length > 0) elements.push(make_node(AST_String, self, {
9357 value: consts.join(separator),
9358 }));
9359 // [ x ].join() ---> "" + x
9360 // [ x ].join(".") ---> "" + x
9361 // [ 1, 2, 3 ].join() ---> "1,2,3"
9362 // [ 1, 2, 3 ].join(".") ---> "1.2.3"
9363 if (elements.length == 1) {
9364 if (elements[0].is_string(compressor)) return elements[0];
9365 return make_node(AST_Binary, elements[0], {
9366 operator: "+",
9367 left: make_node(AST_String, self, { value: "" }),
9368 right: elements[0],
9369 });
9370 }
9371 // [ 1, 2, a, 3 ].join("") ---> "12" + a + "3"
9372 if (separator == "") {
9373 var first;
9374 if (elements[0].is_string(compressor) || elements[1].is_string(compressor)) {
9375 first = elements.shift();
9376 } else {
9377 first = make_node(AST_String, self, { value: "" });
9378 }
9379 return elements.reduce(function(prev, el) {
9380 return make_node(AST_Binary, el, {
9381 operator: "+",
9382 left: prev,
9383 right: el,
9384 });
9385 }, first).optimize(compressor);
9386 }
9387 // [ x, "foo", "bar", y ].join() ---> [ x, "foo,bar", y ].join()
9388 // [ x, "foo", "bar", y ].join("-") ---> [ x, "foo-bar", y ].join("-")
9389 // need this awkward cloning to not affect original element
9390 // best_of will decide which one to get through.
9391 var node = self.clone();
9392 node.expression = node.expression.clone();
9393 node.expression.expression = node.expression.expression.clone();
9394 node.expression.expression.elements = elements;
9395 return best_of(compressor, self, node);
9396 }
9397 break;
9398 case "charAt":
9399 if (self.args.length < 2) {
9400 var node = make_node(AST_Binary, self, {
9401 operator: "||",
9402 left: make_node(AST_Sub, self, {
9403 expression: exp.expression,
9404 property: self.args.length ? make_node(AST_Binary, self.args[0], {
9405 operator: "|",
9406 left: make_node(AST_Number, self, { value: 0 }),
9407 right: self.args[0],
9408 }) : make_node(AST_Number, self, { value: 0 }),
9409 }).optimize(compressor),
9410 right: make_node(AST_String, self, { value: "" }),
9411 });
9412 node.is_string = return_true;
9413 return node.optimize(compressor);
9414 }
9415 break;
9416 case "apply":
9417 if (self.args.length == 2 && self.args[1] instanceof AST_Array) {
9418 var args = self.args[1].elements.slice();
9419 args.unshift(self.args[0]);
9420 return make_node(AST_Call, self, {
9421 expression: make_node(AST_Dot, exp, {
9422 expression: exp.expression,
9423 property: "call",
9424 }),
9425 args: args
9426 }).optimize(compressor);
9427 }
9428 break;
9429 case "call":
9430 var func = exp.expression;
9431 if (func instanceof AST_SymbolRef) {
9432 func = func.fixed_value();
9433 }
9434 if (func instanceof AST_Lambda && !func.contains_this()) {
9435 return (self.args.length ? make_sequence(this, [
9436 self.args[0],
9437 make_node(AST_Call, self, {
9438 expression: exp.expression,
9439 args: self.args.slice(1)
9440 })
9441 ]) : make_node(AST_Call, self, {
9442 expression: exp.expression,
9443 args: []
9444 })).optimize(compressor);
9445 }
9446 break;
9447 }
9448 }
9449 if (compressor.option("unsafe_Function")
9450 && is_undeclared_ref(exp)
9451 && exp.name == "Function") {
9452 // new Function() ---> function(){}
9453 if (self.args.length == 0) return make_node(AST_Function, self, {
9454 argnames: [],
9455 body: []
9456 }).init_vars(exp.scope);
9457 if (all(self.args, function(x) {
9458 return x instanceof AST_String;
9459 })) {
9460 // quite a corner-case, but we can handle it:
9461 // https://github.com/mishoo/UglifyJS/issues/203
9462 // if the code argument is a constant, then we can minify it.
9463 try {
9464 var code = "n(function(" + self.args.slice(0, -1).map(function(arg) {
9465 return arg.value;
9466 }).join() + "){" + self.args[self.args.length - 1].value + "})";
9467 var ast = parse(code);
9468 var mangle = { ie: compressor.option("ie") };
9469 ast.figure_out_scope(mangle);
9470 var comp = new Compressor(compressor.options);
9471 ast = ast.transform(comp);
9472 ast.figure_out_scope(mangle);
9473 ast.compute_char_frequency(mangle);
9474 ast.mangle_names(mangle);
9475 var fun;
9476 ast.walk(new TreeWalker(function(node) {
9477 if (fun) return true;
9478 if (node instanceof AST_Lambda) {
9479 fun = node;
9480 return true;
9481 }
9482 }));
9483 var code = OutputStream();
9484 AST_BlockStatement.prototype._codegen.call(fun, code);
9485 self.args = [
9486 make_node(AST_String, self, {
9487 value: fun.argnames.map(function(arg) {
9488 return arg.print_to_string();
9489 }).join(),
9490 }),
9491 make_node(AST_String, self.args[self.args.length - 1], {
9492 value: code.get().replace(/^\{|\}$/g, "")
9493 })
9494 ];
9495 return self;
9496 } catch (ex) {
9497 if (ex instanceof JS_Parse_Error) {
9498 AST_Node.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
9499 AST_Node.warn(ex.toString());
9500 } else {
9501 throw ex;
9502 }
9503 }
9504 }
9505 }
9506 var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
9507 var parent = compressor.parent(), current = compressor.self();
9508 var is_func = fn instanceof AST_Lambda
9509 && (!is_async(fn) || compressor.option("awaits") && parent instanceof AST_Await)
9510 && (!is_generator(fn) || compressor.option("yields") && current instanceof AST_Yield && current.nested);
9511 var stat = is_func && fn.first_statement();
9512 var has_default = 0, has_destructured = false;
9513 var has_spread = !all(self.args, function(arg) {
9514 return !(arg instanceof AST_Spread);
9515 });
9516 var can_drop = is_func && all(fn.argnames, function(argname, index) {
9517 if (has_default == 1 && self.args[index] instanceof AST_Spread) has_default = 2;
9518 if (argname instanceof AST_DefaultValue) {
9519 if (!has_default) has_default = 1;
9520 var arg = has_default == 1 && self.args[index];
9521 if (arg && !is_undefined(arg)) has_default = 2;
9522 if (has_arg_refs(argname.value)) return false;
9523 argname = argname.name;
9524 }
9525 if (argname instanceof AST_Destructured) {
9526 has_destructured = true;
9527 if (has_arg_refs(argname)) return false;
9528 }
9529 return true;
9530 }) && !(fn.rest instanceof AST_Destructured && has_arg_refs(fn.rest));
9531 var can_inline = can_drop && compressor.option("inline") && !self.is_expr_pure(compressor);
9532 if (can_inline && stat instanceof AST_Return) {
9533 var value = stat.value;
9534 if (exp === fn && !fn.name && (!value || value.is_constant_expression()) && safe_from_await_yield(fn)) {
9535 return make_sequence(self, convert_args(value)).optimize(compressor);
9536 }
9537 }
9538 if (is_func) {
9539 var def, value, var_assigned = false;
9540 if (can_inline
9541 && !fn.uses_arguments
9542 && !fn.pinned()
9543 && !(fn.name && fn instanceof AST_LambdaExpression)
9544 && (exp === fn || !recursive_ref(compressor, def = exp.definition(), fn)
9545 && fn.is_constant_expression(find_scope(compressor)))
9546 && !has_spread
9547 && (value = can_flatten_body(stat))
9548 && !fn.contains_this()) {
9549 var replacing = exp === fn || def.single_use && def.references.length - def.replaced == 1;
9550 if (can_substitute_directly()) {
9551 var args = self.args.slice();
9552 var refs = [];
9553 args.push(value.clone(true).transform(new TreeTransformer(function(node) {
9554 if (node instanceof AST_SymbolRef) {
9555 var def = node.definition();
9556 if (fn.variables.get(node.name) !== def) {
9557 refs.push(node);
9558 return node;
9559 }
9560 var index = resolve_index(def);
9561 var arg = args[index];
9562 if (!arg) return make_node(AST_Undefined, self);
9563 args[index] = null;
9564 var parent = this.parent();
9565 return parent ? maintain_this_binding(compressor, parent, node, arg) : arg;
9566 }
9567 })));
9568 var save_inlined = fn.inlined;
9569 if (exp !== fn) fn.inlined = true;
9570 var node = make_sequence(self, args.filter(function(arg) {
9571 return arg;
9572 })).optimize(compressor);
9573 fn.inlined = save_inlined;
9574 node = maintain_this_binding(compressor, parent, current, node);
9575 if (replacing || best_of_expression(node, self) === node) {
9576 refs.forEach(function(ref) {
9577 ref.scope = exp === fn ? fn.parent_scope : exp.scope;
9578 ref.reference();
9579 var def = ref.definition();
9580 if (replacing) def.replaced++;
9581 def.single_use = false;
9582 });
9583 return node;
9584 } else if (!node.has_side_effects(compressor)) {
9585 self.drop_side_effect_free = function(compressor, first_in_statement) {
9586 var self = this;
9587 var exprs = self.args.slice();
9588 exprs.unshift(self.expression);
9589 return make_sequence(self, exprs).drop_side_effect_free(compressor, first_in_statement);
9590 };
9591 }
9592 }
9593 var arg_used, insert, in_loop, scope;
9594 if (replacing && can_inject_symbols()) {
9595 fn._squeezed = true;
9596 if (exp !== fn) fn.parent_scope = exp.scope;
9597 var node = make_sequence(self, flatten_fn()).optimize(compressor);
9598 return maintain_this_binding(compressor, parent, current, node);
9599 }
9600 }
9601 if (compressor.option("side_effects")
9602 && can_drop
9603 && all(fn.body, is_empty)
9604 && (fn === exp ? fn_name_unused(fn, compressor) : !has_default && !has_destructured && !fn.rest)
9605 && !(is_arrow(fn) && fn.value)
9606 && safe_from_await_yield(fn)) {
9607 return make_sequence(self, convert_args()).optimize(compressor);
9608 }
9609 }
9610 if (compressor.option("drop_console")) {
9611 if (exp instanceof AST_PropAccess) {
9612 var name = exp.expression;
9613 while (name.expression) {
9614 name = name.expression;
9615 }
9616 if (is_undeclared_ref(name) && name.name == "console") {
9617 return make_node(AST_Undefined, self).optimize(compressor);
9618 }
9619 }
9620 }
9621 if (compressor.option("negate_iife") && parent instanceof AST_SimpleStatement && is_iife_call(current)) {
9622 return self.negate(compressor, true);
9623 }
9624 return try_evaluate(compressor, self);
9625
9626 function has_arg_refs(node) {
9627 var found = false;
9628 node.walk(new TreeWalker(function(node) {
9629 if (found) return true;
9630 if (node instanceof AST_SymbolRef && fn.variables.get(node.name) === node.definition()) {
9631 return found = true;
9632 }
9633 }));
9634 return found;
9635 }
9636
9637 function make_void_lhs(orig) {
9638 return make_node(AST_Dot, orig, {
9639 expression: make_node(AST_Array, orig, { elements: [] }),
9640 property: "e",
9641 });
9642 }
9643
9644 function convert_args(value) {
9645 var args = self.args.slice();
9646 var destructured = has_default > 1 || has_destructured || fn.rest;
9647 if (destructured || has_spread) args = [ make_node(AST_Array, self, { elements: args }) ];
9648 if (destructured) {
9649 var tt = new TreeTransformer(function(node, descend) {
9650 if (node instanceof AST_DefaultValue) return make_node(AST_DefaultValue, node, {
9651 name: node.name.transform(tt) || make_void_lhs(node),
9652 value: node.value,
9653 });
9654 if (node instanceof AST_DestructuredArray) {
9655 var elements = [];
9656 node.elements.forEach(function(node, index) {
9657 node = node.transform(tt);
9658 if (node) elements[index] = node;
9659 });
9660 fill_holes(node, elements);
9661 return make_node(AST_DestructuredArray, node, { elements: elements });
9662 }
9663 if (node instanceof AST_DestructuredObject) {
9664 var properties = [], side_effects = [];
9665 node.properties.forEach(function(prop) {
9666 var key = prop.key;
9667 var value = prop.value.transform(tt);
9668 if (value) {
9669 if (side_effects.length) {
9670 if (!(key instanceof AST_Node)) key = make_node_from_constant(key, prop);
9671 side_effects.push(key);
9672 key = make_sequence(node, side_effects);
9673 side_effects = [];
9674 }
9675 properties.push(make_node(AST_DestructuredKeyVal, prop, {
9676 key: key,
9677 value: value,
9678 }));
9679 } else if (key instanceof AST_Node) {
9680 side_effects.push(key);
9681 }
9682 });
9683 if (side_effects.length) properties.push(make_node(AST_DestructuredKeyVal, node, {
9684 key: make_sequence(node, side_effects),
9685 value: make_void_lhs(node),
9686 }));
9687 return make_node(AST_DestructuredObject, node, { properties: properties });
9688 }
9689 if (node instanceof AST_SymbolFunarg) return null;
9690 });
9691 var lhs = [];
9692 fn.argnames.forEach(function(argname, index) {
9693 argname = argname.transform(tt);
9694 if (argname) lhs[index] = argname;
9695 });
9696 var rest = fn.rest && fn.rest.transform(tt);
9697 if (rest) lhs.length = fn.argnames.length;
9698 fill_holes(fn, lhs);
9699 args[0] = make_node(AST_Assign, self, {
9700 operator: "=",
9701 left: make_node(AST_DestructuredArray, fn, {
9702 elements: lhs,
9703 rest: rest,
9704 }),
9705 right: args[0],
9706 });
9707 } else fn.argnames.forEach(function(argname) {
9708 if (argname instanceof AST_DefaultValue) args.push(argname.value);
9709 });
9710 args.push(value || make_node(AST_Undefined, self));
9711 return args;
9712 }
9713
9714 function avoid_await_yield() {
9715 var avoid = [];
9716 var parent_scope = scope || compressor.find_parent(AST_Scope);
9717 if (is_async(parent_scope)) avoid.push("await");
9718 if (is_generator(parent_scope)) avoid.push("yield");
9719 return avoid.length && makePredicate(avoid);
9720 }
9721
9722 function safe_from_await_yield(node) {
9723 var avoid = avoid_await_yield();
9724 if (!avoid) return true;
9725 var safe = true;
9726 var tw = new TreeWalker(function(node) {
9727 if (!safe) return true;
9728 if (node instanceof AST_Scope) {
9729 if (node === fn) return;
9730 if (is_arrow(node)) {
9731 for (var i = 0; safe && i < node.argnames.length; i++) node.argnames[i].walk(tw);
9732 } else if (node instanceof AST_LambdaDefinition && avoid[node.name.name]) {
9733 safe = false;
9734 }
9735 return true;
9736 }
9737 if (node instanceof AST_Symbol && avoid[node.name] && node !== fn.name) safe = false;
9738 });
9739 node.walk(tw);
9740 return safe;
9741 }
9742
9743 function noop_value() {
9744 return self.call_only ? make_node(AST_Number, self, { value: 0 }) : make_node(AST_Undefined, self);
9745 }
9746
9747 function return_value(stat) {
9748 if (!stat) return noop_value();
9749 if (stat instanceof AST_Return) return stat.value || noop_value();
9750 if (stat instanceof AST_SimpleStatement) {
9751 return self.call_only ? stat.body : make_node(AST_UnaryPrefix, stat, {
9752 operator: "void",
9753 expression: stat.body,
9754 });
9755 }
9756 }
9757
9758 function can_flatten_body(stat) {
9759 var len = fn.body.length;
9760 if (len < 2) {
9761 stat = return_value(stat);
9762 if (stat) return stat;
9763 }
9764 if (compressor.option("inline") < 3) return false;
9765 stat = null;
9766 for (var i = 0; i < len; i++) {
9767 var line = fn.body[i];
9768 if (line instanceof AST_Var) {
9769 var assigned = var_assigned || !declarations_only(line);
9770 if (assigned) {
9771 var_assigned = true;
9772 if (stat) return false;
9773 }
9774 } else if (line instanceof AST_AsyncDefun
9775 || line instanceof AST_Defun
9776 || line instanceof AST_EmptyStatement) {
9777 continue;
9778 } else if (stat) {
9779 return false;
9780 } else {
9781 stat = line;
9782 }
9783 }
9784 return return_value(stat);
9785 }
9786
9787 function resolve_index(def) {
9788 for (var i = fn.argnames.length; --i >= 0;) {
9789 if (fn.argnames[i].definition() === def) return i;
9790 }
9791 }
9792
9793 function can_substitute_directly() {
9794 if (has_default || has_destructured || var_assigned || fn.rest) return;
9795 if (compressor.option("inline") < 2 && fn.argnames.length) return;
9796 if (!fn.variables.all(function(def) {
9797 return def.references.length - def.replaced < 2 && def.orig[0] instanceof AST_SymbolFunarg;
9798 })) return;
9799 var abort = false;
9800 var avoid = avoid_await_yield();
9801 var begin;
9802 var in_order = [];
9803 var side_effects = false;
9804 value.walk(new TreeWalker(function(node, descend) {
9805 if (abort) return true;
9806 if (node instanceof AST_Binary && lazy_op[node.operator]
9807 || node instanceof AST_Conditional) {
9808 in_order = null;
9809 return;
9810 }
9811 if (node instanceof AST_Scope) return abort = true;
9812 if (avoid && node instanceof AST_Symbol && avoid[node.name]) return abort = true;
9813 if (node instanceof AST_SymbolRef) {
9814 var def = node.definition();
9815 if (fn.variables.get(node.name) !== def) {
9816 in_order = null;
9817 return;
9818 }
9819 if (def.init instanceof AST_LambdaDefinition) return abort = true;
9820 if (is_lhs(node, this.parent())) return abort = true;
9821 var index = resolve_index(def);
9822 if (!(begin < index)) begin = index;
9823 if (!in_order) return;
9824 if (side_effects) {
9825 in_order = null;
9826 } else {
9827 in_order.push(fn.argnames[index]);
9828 }
9829 return;
9830 }
9831 if (node.has_side_effects(compressor)) {
9832 descend();
9833 side_effects = true;
9834 return true;
9835 }
9836 }));
9837 if (abort) return;
9838 var end = self.args.length;
9839 if (in_order && fn.argnames.length >= end) {
9840 end = fn.argnames.length;
9841 while (end-- > begin && fn.argnames[end] === in_order.pop());
9842 end++;
9843 }
9844 var scope = side_effects && !in_order && compressor.find_parent(AST_Scope);
9845 return end <= begin || all(self.args.slice(begin, end), scope ? function(funarg) {
9846 return funarg.is_constant_expression(scope);
9847 } : function(funarg) {
9848 return !funarg.has_side_effects(compressor);
9849 });
9850 }
9851
9852 function var_exists(defined, name) {
9853 return defined[name] || identifier_atom[name] || scope.var_names()[name];
9854 }
9855
9856 function can_inject_args(defined, used, safe_to_inject) {
9857 var abort = false;
9858 fn.each_argname(function(arg) {
9859 if (abort) return;
9860 if (arg.__unused) return;
9861 if (!safe_to_inject || var_exists(defined, arg.name)) return abort = true;
9862 used[arg.name] = true;
9863 if (in_loop) in_loop.push(arg.definition());
9864 });
9865 return !abort;
9866 }
9867
9868 function can_inject_vars(defined, used, safe_to_inject) {
9869 for (var i = 0; i < fn.body.length; i++) {
9870 var stat = fn.body[i];
9871 if (stat instanceof AST_LambdaDefinition) {
9872 if (!safe_to_inject || var_exists(used, stat.name.name)) return false;
9873 if (!all(stat.enclosed, function(def) {
9874 return def.scope === stat || !defined[def.name];
9875 })) return false;
9876 if (in_loop) in_loop.push(stat.name.definition());
9877 continue;
9878 }
9879 if (!(stat instanceof AST_Var)) continue;
9880 if (!safe_to_inject) return false;
9881 for (var j = stat.definitions.length; --j >= 0;) {
9882 var name = stat.definitions[j].name;
9883 if (var_exists(defined, name.name)) return false;
9884 if (in_loop) in_loop.push(name.definition());
9885 }
9886 }
9887 return true;
9888 }
9889
9890 function can_inject_symbols() {
9891 var defined = Object.create(null);
9892 var level = 0, child;
9893 scope = current;
9894 do {
9895 if (scope.variables) scope.variables.each(function(def) {
9896 defined[def.name] = true;
9897 });
9898 child = scope;
9899 scope = compressor.parent(level++);
9900 if (scope instanceof AST_DWLoop) {
9901 in_loop = [];
9902 } else if (scope instanceof AST_For) {
9903 if (scope.init === child) continue;
9904 in_loop = [];
9905 } else if (scope instanceof AST_ForEnumeration) {
9906 if (scope.init === child) continue;
9907 if (scope.object === child) continue;
9908 in_loop = [];
9909 } else if (scope instanceof AST_SymbolRef) {
9910 if (scope.fixed_value() instanceof AST_Scope) return false;
9911 }
9912 } while (!(scope instanceof AST_Scope));
9913 insert = scope.body.indexOf(child) + 1;
9914 if (!insert) return false;
9915 if (!safe_from_await_yield(fn)) return false;
9916 var safe_to_inject = exp !== fn || fn.parent_scope.resolve() === scope;
9917 if (scope instanceof AST_Toplevel) {
9918 if (compressor.toplevel.vars) {
9919 defined["arguments"] = true;
9920 } else {
9921 safe_to_inject = false;
9922 }
9923 }
9924 var inline = compressor.option("inline");
9925 arg_used = Object.create(defined);
9926 if (!can_inject_args(defined, arg_used, inline >= 2 && safe_to_inject)) return false;
9927 var used = Object.create(arg_used);
9928 if (!can_inject_vars(defined, used, inline >= 3 && safe_to_inject)) return false;
9929 return !in_loop || in_loop.length == 0 || !is_reachable(fn, in_loop);
9930 }
9931
9932 function append_var(decls, expressions, name, value) {
9933 var def = name.definition();
9934 if (!scope.var_names()[name.name]) {
9935 scope.var_names()[name.name] = true;
9936 decls.push(make_node(AST_VarDef, name, {
9937 name: name,
9938 value: null,
9939 }));
9940 }
9941 scope.variables.set(name.name, def);
9942 scope.enclosed.push(def);
9943 if (!value) return;
9944 var sym = make_node(AST_SymbolRef, name, name);
9945 def.references.push(sym);
9946 expressions.push(make_node(AST_Assign, self, {
9947 operator: "=",
9948 left: sym,
9949 right: value,
9950 }));
9951 }
9952
9953 function flatten_args(decls, expressions) {
9954 var len = fn.argnames.length;
9955 for (var i = self.args.length; --i >= len;) {
9956 expressions.push(self.args[i]);
9957 }
9958 var default_args = [];
9959 for (i = len; --i >= 0;) {
9960 var argname = fn.argnames[i];
9961 var name;
9962 if (argname instanceof AST_DefaultValue) {
9963 default_args.push(argname);
9964 name = argname.name;
9965 } else {
9966 name = argname;
9967 }
9968 var value = self.args[i];
9969 if (name.__unused || scope.var_names()[name.name]) {
9970 if (value) expressions.push(value);
9971 } else {
9972 var symbol = make_node(AST_SymbolVar, name, name);
9973 name.definition().orig.push(symbol);
9974 if ("__unused" in name) {
9975 append_var(decls, expressions, symbol);
9976 if (value) expressions.push(value);
9977 } else {
9978 if (!value && in_loop && argname === name) value = make_node(AST_Undefined, self);
9979 append_var(decls, expressions, symbol, value);
9980 }
9981 }
9982 }
9983 decls.reverse();
9984 expressions.reverse();
9985 for (i = default_args.length; --i >= 0;) {
9986 var node = default_args[i];
9987 if ("__unused" in node.name) {
9988 expressions.push(node.value);
9989 } else {
9990 var sym = make_node(AST_SymbolRef, node.name, node.name);
9991 node.name.definition().references.push(sym);
9992 expressions.push(make_node(AST_Assign, node, {
9993 operator: "=",
9994 left: sym,
9995 right: node.value,
9996 }));
9997 }
9998 }
9999 }
10000
10001 function flatten_destructured(decls, expressions) {
10002 expressions.push(make_node(AST_Assign, self, {
10003 operator: "=",
10004 left: make_node(AST_DestructuredArray, self, {
10005 elements: fn.argnames.map(function(argname) {
10006 if (argname.__unused) return make_node(AST_Hole, argname);
10007 return argname.convert_symbol(AST_SymbolRef, process);
10008 }),
10009 rest: fn.rest && fn.rest.convert_symbol(AST_SymbolRef, process),
10010 }),
10011 right: make_node(AST_Array, self, { elements: self.args.slice() }),
10012 }));
10013
10014 function process(ref, name) {
10015 var def = name.definition();
10016 def.references.push(ref);
10017 var symbol = make_node(AST_SymbolVar, name, name);
10018 def.orig.push(symbol);
10019 append_var(decls, expressions, symbol);
10020 }
10021 }
10022
10023 function flatten_var(name) {
10024 var redef = name.definition().redefined();
10025 if (redef) {
10026 name = name.clone();
10027 name.thedef = redef;
10028 }
10029 return name;
10030 }
10031
10032 function flatten_vars(decls, expressions) {
10033 var args = [ insert, 0 ];
10034 var decl_var = [], expr_var = [], expr_loop = [];
10035 for (var i = 0; i < fn.body.length; i++) {
10036 var stat = fn.body[i];
10037 if (stat instanceof AST_LambdaDefinition) {
10038 if (in_loop) {
10039 var name = make_node(AST_SymbolVar, stat.name, flatten_var(stat.name));
10040 name.definition().orig.push(name);
10041 append_var(decls, expressions, name, to_func_expr(stat, true));
10042 } else {
10043 var def = stat.name.definition();
10044 scope.functions.set(def.name, def);
10045 scope.variables.set(def.name, def);
10046 scope.enclosed.push(def);
10047 scope.var_names()[def.name] = true;
10048 args.push(stat);
10049 }
10050 continue;
10051 }
10052 if (!(stat instanceof AST_Var)) continue;
10053 for (var j = 0; j < stat.definitions.length; j++) {
10054 var var_def = stat.definitions[j];
10055 var name = flatten_var(var_def.name);
10056 append_var(decl_var, expr_var, name, var_def.value);
10057 if (in_loop && !HOP(arg_used, name.name)) {
10058 var def = fn.variables.get(name.name);
10059 var sym = make_node(AST_SymbolRef, name, name);
10060 def.references.push(sym);
10061 expr_loop.push(make_node(AST_Assign, var_def, {
10062 operator: "=",
10063 left: sym,
10064 right: make_node(AST_Undefined, name),
10065 }));
10066 }
10067 }
10068 }
10069 [].push.apply(decls, decl_var);
10070 [].push.apply(expressions, expr_loop);
10071 [].push.apply(expressions, expr_var);
10072 return args;
10073 }
10074
10075 function flatten_fn() {
10076 var decls = [];
10077 var expressions = [];
10078 if (has_default > 1 || has_destructured || fn.rest) {
10079 flatten_destructured(decls, expressions);
10080 } else {
10081 flatten_args(decls, expressions);
10082 }
10083 var args = flatten_vars(decls, expressions);
10084 expressions.push(value);
10085 if (decls.length) args.push(make_node(AST_Var, fn, {
10086 definitions: decls
10087 }));
10088 [].splice.apply(scope.body, args);
10089 fn.enclosed.forEach(function(def) {
10090 if (scope.var_names()[def.name]) return;
10091 scope.enclosed.push(def);
10092 scope.var_names()[def.name] = true;
10093 });
10094 return expressions;
10095 }
10096 });
10097
10098 OPT(AST_New, function(self, compressor) {
10099 if (compressor.option("sequences")) {
10100 var seq = lift_sequence_in_expression(self, compressor);
10101 if (seq !== self) return seq.optimize(compressor);
10102 }
10103 if (compressor.option("unused")) drop_unused_call_args(self, compressor);
10104 if (compressor.option("unsafe")) {
10105 var exp = self.expression;
10106 if (is_undeclared_ref(exp)) {
10107 switch (exp.name) {
10108 case "Object":
10109 case "RegExp":
10110 case "Function":
10111 case "Error":
10112 case "Array":
10113 return make_node(AST_Call, self, self).transform(compressor);
10114 }
10115 }
10116 }
10117 return self;
10118 });
10119
10120 // (a = b, x && a = c) ---> a = x ? c : b
10121 // (a = b, x || a = c) ---> a = x ? b : c
10122 function to_conditional_assignment(compressor, def, value, node) {
10123 if (!(node instanceof AST_Binary)) return;
10124 if (!(node.operator == "&&" || node.operator == "||")) return;
10125 if (!(node.right instanceof AST_Assign)) return;
10126 if (node.right.operator != "=") return;
10127 if (!(node.right.left instanceof AST_SymbolRef)) return;
10128 if (node.right.left.definition() !== def) return;
10129 if (value.has_side_effects(compressor)) return;
10130 if (!safe_from_assignment(node.left)) return;
10131 if (!safe_from_assignment(node.right.right)) return;
10132 def.replaced++;
10133 return node.operator == "&&" ? make_node(AST_Conditional, node, {
10134 condition: node.left,
10135 consequent: node.right.right,
10136 alternative: value
10137 }) : make_node(AST_Conditional, node, {
10138 condition: node.left,
10139 consequent: value,
10140 alternative: node.right.right
10141 });
10142
10143 function safe_from_assignment(node) {
10144 if (node.has_side_effects(compressor)) return;
10145 var hit = false;
10146 node.walk(new TreeWalker(function(node) {
10147 if (hit) return true;
10148 if (node instanceof AST_SymbolRef && node.definition() === def) return hit = true;
10149 }));
10150 return !hit;
10151 }
10152 }
10153
10154 OPT(AST_Sequence, function(self, compressor) {
10155 var expressions = filter_for_side_effects();
10156 var end = expressions.length - 1;
10157 merge_assignments();
10158 trim_right_for_undefined();
10159 if (end == 0) {
10160 self = maintain_this_binding(compressor, compressor.parent(), compressor.self(), expressions[0]);
10161 if (!(self instanceof AST_Sequence)) self = self.optimize(compressor);
10162 return self;
10163 }
10164 self.expressions = expressions;
10165 return self;
10166
10167 function filter_for_side_effects() {
10168 if (!compressor.option("side_effects")) return self.expressions;
10169 var expressions = [];
10170 var first = first_in_statement(compressor);
10171 var last = self.expressions.length - 1;
10172 self.expressions.forEach(function(expr, index) {
10173 if (index < last) expr = expr.drop_side_effect_free(compressor, first);
10174 if (expr) {
10175 merge_sequence(expressions, expr);
10176 first = false;
10177 }
10178 });
10179 return expressions;
10180 }
10181
10182 function trim_right_for_undefined() {
10183 if (!compressor.option("side_effects")) return;
10184 while (end > 0 && is_undefined(expressions[end], compressor)) end--;
10185 if (end < expressions.length - 1) {
10186 expressions[end] = make_node(AST_UnaryPrefix, self, {
10187 operator : "void",
10188 expression : expressions[end]
10189 });
10190 expressions.length = end + 1;
10191 }
10192 }
10193
10194 function is_simple_assign(node) {
10195 return node instanceof AST_Assign
10196 && node.operator == "="
10197 && node.left instanceof AST_SymbolRef
10198 && node.left.definition();
10199 }
10200
10201 function merge_assignments() {
10202 for (var i = 1; i < end; i++) {
10203 var prev = expressions[i - 1];
10204 var def = is_simple_assign(prev);
10205 if (!def) continue;
10206 var expr = expressions[i];
10207 if (compressor.option("conditionals")) {
10208 var cond = to_conditional_assignment(compressor, def, prev.right, expr);
10209 if (cond) {
10210 prev.right = cond;
10211 expressions.splice(i--, 1);
10212 end--;
10213 continue;
10214 }
10215 }
10216 if (compressor.option("dead_code")
10217 && is_simple_assign(expr) === def
10218 && expr.right.is_constant_expression(def.scope.resolve())) {
10219 expressions[--i] = prev.right;
10220 }
10221 }
10222 }
10223 });
10224
10225 OPT(AST_UnaryPostfix, function(self, compressor) {
10226 if (compressor.option("sequences")) {
10227 var seq = lift_sequence_in_expression(self, compressor);
10228 if (seq !== self) return seq.optimize(compressor);
10229 }
10230 return try_evaluate(compressor, self);
10231 });
10232
10233 var SIGN_OPS = makePredicate("+ -");
10234 var MULTIPLICATIVE_OPS = makePredicate("* / %");
10235 OPT(AST_UnaryPrefix, function(self, compressor) {
10236 var op = self.operator;
10237 var exp = self.expression;
10238 if (compressor.option("evaluate") && op == "delete" && !may_not_delete(exp)) {
10239 return make_sequence(self, [ exp, make_node(AST_True, self) ]).optimize(compressor);
10240 }
10241 if (compressor.option("sequences") && can_lift()) {
10242 var seq = lift_sequence_in_expression(self, compressor);
10243 if (seq !== self) return seq.optimize(compressor);
10244 }
10245 if (compressor.option("side_effects") && op == "void") {
10246 exp = exp.drop_side_effect_free(compressor);
10247 if (!exp) return make_node(AST_Undefined, self).optimize(compressor);
10248 self.expression = exp;
10249 return self;
10250 }
10251 if (compressor.option("booleans")) {
10252 if (op == "!" && exp.is_truthy()) {
10253 return make_sequence(self, [ exp, make_node(AST_False, self) ]).optimize(compressor);
10254 } else if (compressor.in_boolean_context()) switch (op) {
10255 case "!":
10256 if (exp instanceof AST_UnaryPrefix && exp.operator == "!") {
10257 // !!foo ---> foo, if we're in boolean context
10258 return exp.expression;
10259 }
10260 if (exp instanceof AST_Binary) {
10261 self = best_of(compressor, self, exp.negate(compressor, first_in_statement(compressor)));
10262 }
10263 break;
10264 case "typeof":
10265 // typeof always returns a non-empty string, thus it's
10266 // always true in booleans
10267 AST_Node.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
10268 var exprs = [ make_node(AST_True, self) ];
10269 if (!(exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor))) exprs.unshift(exp);
10270 return make_sequence(self, exprs).optimize(compressor);
10271 }
10272 }
10273 if (op == "-" && exp instanceof AST_Infinity) exp = exp.transform(compressor);
10274 if (compressor.option("evaluate")
10275 && exp instanceof AST_Binary
10276 && SIGN_OPS[op]
10277 && MULTIPLICATIVE_OPS[exp.operator]
10278 && (exp.left.is_constant() || !exp.right.has_side_effects(compressor))) {
10279 return make_node(AST_Binary, self, {
10280 operator: exp.operator,
10281 left: make_node(AST_UnaryPrefix, exp.left, {
10282 operator: op,
10283 expression: exp.left
10284 }),
10285 right: exp.right
10286 });
10287 }
10288 // avoids infinite recursion of numerals
10289 return op == "-" && (exp instanceof AST_Number || exp instanceof AST_Infinity)
10290 ? self : try_evaluate(compressor, self);
10291
10292 function may_not_delete(node) {
10293 return node instanceof AST_Infinity
10294 || node instanceof AST_NaN
10295 || node instanceof AST_NewTarget
10296 || node instanceof AST_PropAccess
10297 || node instanceof AST_SymbolRef
10298 || node instanceof AST_Undefined;
10299 }
10300
10301 function can_lift() {
10302 switch (op) {
10303 case "delete":
10304 return !may_not_delete(exp.tail_node());
10305 case "typeof":
10306 return !is_undeclared_ref(exp.tail_node());
10307 default:
10308 return true;
10309 }
10310 }
10311 });
10312
10313 OPT(AST_Await, function(self, compressor) {
10314 if (!compressor.option("awaits")) return self;
10315 if (compressor.option("sequences")) {
10316 var seq = lift_sequence_in_expression(self, compressor);
10317 if (seq !== self) return seq.optimize(compressor);
10318 }
10319 if (compressor.option("side_effects")) {
10320 var exp = self.expression;
10321 if (exp instanceof AST_Await) return exp.optimize(compressor);
10322 if (exp instanceof AST_UnaryPrefix) {
10323 if (exp.expression instanceof AST_Await) return exp.optimize(compressor);
10324 if (exp.operator == "void") return make_node(AST_UnaryPrefix, self, {
10325 operator: "void",
10326 expression: make_node(AST_Await, self, { expression: exp.expression }),
10327 }).optimize(compressor);
10328 }
10329 for (var level = 0, node = self, parent; parent = compressor.parent(level++); node = parent) {
10330 if (is_arrow(parent)) {
10331 if (parent.value === node) return exp.optimize(compressor);
10332 } else if (parent instanceof AST_Return) {
10333 var drop = true;
10334 do {
10335 node = parent;
10336 parent = compressor.parent(level++);
10337 if (parent instanceof AST_Try && (parent.bfinally || parent.bcatch) !== node) {
10338 drop = false;
10339 break;
10340 }
10341 } while (parent && !(parent instanceof AST_Scope));
10342 if (drop) return exp.optimize(compressor);
10343 } else if (parent instanceof AST_Sequence) {
10344 if (parent.tail_node() === node) continue;
10345 }
10346 break;
10347 }
10348 }
10349 return self;
10350 });
10351
10352 OPT(AST_Yield, function(self, compressor) {
10353 if (!compressor.option("yields")) return self;
10354 if (compressor.option("sequences")) {
10355 var seq = lift_sequence_in_expression(self, compressor);
10356 if (seq !== self) return seq.optimize(compressor);
10357 }
10358 var exp = self.expression;
10359 if (self.nested && exp.TYPE == "Call") {
10360 var inlined = exp.clone().optimize(compressor);
10361 if (inlined.TYPE != "Call") return inlined;
10362 }
10363 return self;
10364 });
10365
10366 AST_Binary.DEFMETHOD("lift_sequences", function(compressor) {
10367 if (this.left instanceof AST_PropAccess) {
10368 if (!(this.left.expression instanceof AST_Sequence)) return this;
10369 var x = this.left.expression.expressions.slice();
10370 var e = this.clone();
10371 e.left = e.left.clone();
10372 e.left.expression = x.pop();
10373 x.push(e);
10374 return make_sequence(this, x);
10375 }
10376 if (this.left instanceof AST_Sequence) {
10377 var x = this.left.expressions.slice();
10378 var e = this.clone();
10379 e.left = x.pop();
10380 x.push(e);
10381 return make_sequence(this, x);
10382 }
10383 if (this.right instanceof AST_Sequence) {
10384 if (this.left.has_side_effects(compressor)) return this;
10385 var assign = this.operator == "=" && this.left instanceof AST_SymbolRef;
10386 var x = this.right.expressions;
10387 var last = x.length - 1;
10388 for (var i = 0; i < last; i++) {
10389 if (!assign && x[i].has_side_effects(compressor)) break;
10390 }
10391 if (i == last) {
10392 x = x.slice();
10393 var e = this.clone();
10394 e.right = x.pop();
10395 x.push(e);
10396 return make_sequence(this, x);
10397 }
10398 if (i > 0) {
10399 var e = this.clone();
10400 e.right = make_sequence(this.right, x.slice(i));
10401 x = x.slice(0, i);
10402 x.push(e);
10403 return make_sequence(this, x);
10404 }
10405 }
10406 return this;
10407 });
10408
10409 var indexFns = makePredicate("indexOf lastIndexOf");
10410 var commutativeOperators = makePredicate("== === != !== * & | ^");
10411 function is_object(node) {
10412 if (node instanceof AST_Assign) return node.operator == "=" && is_object(node.right);
10413 if (node instanceof AST_Sequence) return is_object(node.tail_node());
10414 if (node instanceof AST_SymbolRef) return is_object(node.fixed_value());
10415 return node instanceof AST_Array
10416 || node instanceof AST_Class
10417 || node instanceof AST_Lambda
10418 || node instanceof AST_New
10419 || node instanceof AST_Object;
10420 }
10421
10422 function is_primitive(compressor, node) {
10423 if (node.is_constant()) return true;
10424 if (node instanceof AST_Assign) return node.operator != "=" || is_primitive(compressor, node.right);
10425 if (node instanceof AST_Binary) {
10426 return !lazy_op[node.operator]
10427 || is_primitive(compressor, node.left) && is_primitive(compressor, node.right);
10428 }
10429 if (node instanceof AST_Conditional) {
10430 return is_primitive(compressor, node.consequent) && is_primitive(compressor, node.alternative);
10431 }
10432 if (node instanceof AST_Sequence) return is_primitive(compressor, node.tail_node());
10433 if (node instanceof AST_SymbolRef) {
10434 var fixed = node.fixed_value();
10435 return fixed && is_primitive(compressor, fixed);
10436 }
10437 if (node instanceof AST_Template) return !node.tag || is_raw_tag(compressor, node.tag);
10438 if (node instanceof AST_Unary) return true;
10439 }
10440
10441 function repeatable(compressor, node) {
10442 if (node instanceof AST_Dot) return repeatable(compressor, node.expression);
10443 if (node instanceof AST_Sub) {
10444 return repeatable(compressor, node.expression) && repeatable(compressor, node.property);
10445 }
10446 if (node instanceof AST_Symbol) return true;
10447 return !node.has_side_effects(compressor);
10448 }
10449
10450 OPT(AST_Binary, function(self, compressor) {
10451 function reversible() {
10452 return self.left.is_constant()
10453 || self.right.is_constant()
10454 || !self.left.has_side_effects(compressor)
10455 && !self.right.has_side_effects(compressor);
10456 }
10457 function reverse(op) {
10458 if (reversible()) {
10459 if (op) self.operator = op;
10460 var tmp = self.left;
10461 self.left = self.right;
10462 self.right = tmp;
10463 }
10464 }
10465 function swap_chain() {
10466 var rhs = self.right;
10467 self.left = make_node(AST_Binary, self, {
10468 operator: self.operator,
10469 left: self.left,
10470 right: rhs.left,
10471 start: self.left.start,
10472 end: rhs.left.end
10473 });
10474 self.right = rhs.right;
10475 self.left = self.left.transform(compressor);
10476 }
10477 if (commutativeOperators[self.operator]
10478 && self.right.is_constant()
10479 && !self.left.is_constant()
10480 && !(self.left instanceof AST_Binary
10481 && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
10482 // if right is a constant, whatever side effects the
10483 // left side might have could not influence the
10484 // result. hence, force switch.
10485 reverse();
10486 }
10487 if (compressor.option("sequences")) {
10488 var seq = self.lift_sequences(compressor);
10489 if (seq !== self) return seq.optimize(compressor);
10490 }
10491 if (compressor.option("assignments") && lazy_op[self.operator]) {
10492 var assign = self.right;
10493 // a || (a = x) ---> a = a || x
10494 // a && (a = x) ---> a = a && x
10495 if (self.left instanceof AST_SymbolRef
10496 && assign instanceof AST_Assign
10497 && assign.operator == "="
10498 && self.left.equivalent_to(assign.left)) {
10499 self.right = assign.right;
10500 assign.right = self;
10501 return assign;
10502 }
10503 }
10504 if (compressor.option("comparisons")) switch (self.operator) {
10505 case "===":
10506 case "!==":
10507 if (is_undefined(self.left, compressor) && self.right.is_defined(compressor)) {
10508 AST_Node.warn("Expression always defined [{file}:{line},{col}]", self.start);
10509 return make_sequence(self, [
10510 self.right,
10511 make_node(self.operator == "===" ? AST_False : AST_True, self)
10512 ]).optimize(compressor);
10513 }
10514 var is_strict_comparison = true;
10515 if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
10516 (self.left.is_number(compressor) && self.right.is_number(compressor)) ||
10517 (self.left.is_boolean(compressor) && self.right.is_boolean(compressor)) ||
10518 repeatable(compressor, self.left) && self.left.equivalent_to(self.right)) {
10519 self.operator = self.operator.slice(0, 2);
10520 }
10521 // XXX: intentionally falling down to the next case
10522 case "==":
10523 case "!=":
10524 // void 0 == x ---> null == x
10525 if (!is_strict_comparison && is_undefined(self.left, compressor)) {
10526 self.left = make_node(AST_Null, self.left);
10527 }
10528 // "undefined" == typeof x ---> undefined === x
10529 else if (compressor.option("typeofs")
10530 && self.left instanceof AST_String
10531 && self.left.value == "undefined"
10532 && self.right instanceof AST_UnaryPrefix
10533 && self.right.operator == "typeof") {
10534 var expr = self.right.expression;
10535 if (expr instanceof AST_SymbolRef ? expr.is_declared(compressor)
10536 : !(expr instanceof AST_PropAccess && compressor.option("ie"))) {
10537 self.right = expr;
10538 self.left = make_node(AST_Undefined, self.left).optimize(compressor);
10539 if (self.operator.length == 2) self.operator += "=";
10540 }
10541 }
10542 // obj !== obj ---> false
10543 else if (self.left instanceof AST_SymbolRef
10544 && self.right instanceof AST_SymbolRef
10545 && self.left.definition() === self.right.definition()
10546 && is_object(self.left)) {
10547 return make_node(self.operator[0] == "=" ? AST_True : AST_False, self).optimize(compressor);
10548 }
10549 break;
10550 case "&&":
10551 case "||":
10552 // void 0 !== x && null !== x ---> null != x
10553 // void 0 === x || null === x ---> null == x
10554 var lhs = self.left;
10555 if (lhs.operator == self.operator) {
10556 lhs = lhs.right;
10557 }
10558 if (lhs instanceof AST_Binary
10559 && lhs.operator == (self.operator == "&&" ? "!==" : "===")
10560 && self.right instanceof AST_Binary
10561 && lhs.operator == self.right.operator
10562 && (is_undefined(lhs.left, compressor) && self.right.left instanceof AST_Null
10563 || lhs.left instanceof AST_Null && is_undefined(self.right.left, compressor))
10564 && !lhs.right.has_side_effects(compressor)
10565 && lhs.right.equivalent_to(self.right.right)) {
10566 var combined = make_node(AST_Binary, self, {
10567 operator: lhs.operator.slice(0, -1),
10568 left: make_node(AST_Null, self),
10569 right: lhs.right
10570 });
10571 if (lhs !== self.left) {
10572 combined = make_node(AST_Binary, self, {
10573 operator: self.operator,
10574 left: self.left.left,
10575 right: combined
10576 });
10577 }
10578 return combined;
10579 }
10580 break;
10581 }
10582 var in_bool = false;
10583 var parent = compressor.parent();
10584 if (compressor.option("booleans")) {
10585 var lhs = self.left;
10586 if (lazy_op[self.operator] && !lhs.has_side_effects(compressor)) {
10587 if (lhs.equivalent_to(self.right)) {
10588 return maintain_this_binding(compressor, parent, compressor.self(), lhs).optimize(compressor);
10589 }
10590 mark_duplicate_condition(compressor, lhs);
10591 }
10592 in_bool = compressor.in_boolean_context();
10593 }
10594 if (in_bool) switch (self.operator) {
10595 case "+":
10596 var ll = self.left.evaluate(compressor);
10597 var rr = self.right.evaluate(compressor);
10598 if (ll && typeof ll == "string") {
10599 AST_Node.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
10600 return make_sequence(self, [
10601 self.right,
10602 make_node(AST_True, self)
10603 ]).optimize(compressor);
10604 }
10605 if (rr && typeof rr == "string") {
10606 AST_Node.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
10607 return make_sequence(self, [
10608 self.left,
10609 make_node(AST_True, self)
10610 ]).optimize(compressor);
10611 }
10612 break;
10613 case "==":
10614 if (self.left instanceof AST_String && self.left.value == "" && self.right.is_string(compressor)) {
10615 return make_node(AST_UnaryPrefix, self, {
10616 operator: "!",
10617 expression: self.right
10618 }).optimize(compressor);
10619 }
10620 break;
10621 case "!=":
10622 if (self.left instanceof AST_String && self.left.value == "" && self.right.is_string(compressor)) {
10623 return self.right.optimize(compressor);
10624 }
10625 break;
10626 }
10627 if (compressor.option("comparisons") && self.is_boolean(compressor)) {
10628 if (!(parent instanceof AST_Binary) || parent instanceof AST_Assign) {
10629 var negated = best_of(compressor, self, make_node(AST_UnaryPrefix, self, {
10630 operator: "!",
10631 expression: self.negate(compressor, first_in_statement(compressor))
10632 }));
10633 if (negated !== self) return negated;
10634 }
10635 switch (self.operator) {
10636 case ">": reverse("<"); break;
10637 case ">=": reverse("<="); break;
10638 }
10639 }
10640 // x && (y && z) ---> x && y && z
10641 // x || (y || z) ---> x || y || z
10642 if (compressor.option("conditionals")
10643 && lazy_op[self.operator]
10644 && self.right instanceof AST_Binary
10645 && self.operator == self.right.operator) {
10646 swap_chain();
10647 }
10648 if (compressor.option("strings") && self.operator == "+") {
10649 // "foo" + 42 + "" ---> "foo" + 42
10650 if (self.right instanceof AST_String
10651 && self.right.value == ""
10652 && self.left.is_string(compressor)) {
10653 return self.left.optimize(compressor);
10654 }
10655 // "" + ("foo" + 42) ---> "foo" + 42
10656 if (self.left instanceof AST_String
10657 && self.left.value == ""
10658 && self.right.is_string(compressor)) {
10659 return self.right.optimize(compressor);
10660 }
10661 // "" + 42 + "foo" ---> 42 + "foo"
10662 if (self.left instanceof AST_Binary
10663 && self.left.operator == "+"
10664 && self.left.left instanceof AST_String
10665 && self.left.left.value == ""
10666 && self.right.is_string(compressor)
10667 && (self.left.right.is_constant() || !self.right.has_side_effects(compressor))) {
10668 self.left = self.left.right;
10669 return self.optimize(compressor);
10670 }
10671 // "x" + (y + "z") ---> "x" + y + "z"
10672 // x + ("y" + z) ---> x + "y" + z
10673 if (self.right instanceof AST_Binary
10674 && self.operator == self.right.operator
10675 && (self.left.is_string(compressor) && self.right.is_string(compressor)
10676 || self.right.left.is_string(compressor)
10677 && (self.left.is_constant() || !self.right.right.has_side_effects(compressor)))) {
10678 swap_chain();
10679 }
10680 }
10681 if (compressor.option("evaluate")) {
10682 var associative = true;
10683 switch (self.operator) {
10684 case "&&":
10685 var ll = fuzzy_eval(compressor, self.left);
10686 if (!ll) {
10687 AST_Node.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
10688 return maintain_this_binding(compressor, parent, compressor.self(), self.left).optimize(compressor);
10689 } else if (!(ll instanceof AST_Node)) {
10690 AST_Node.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
10691 return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
10692 }
10693 var rr = self.right.evaluate(compressor);
10694 if (!rr) {
10695 if (in_bool) {
10696 AST_Node.warn("Boolean && always false [{file}:{line},{col}]", self.start);
10697 return make_sequence(self, [
10698 self.left,
10699 make_node(AST_False, self)
10700 ]).optimize(compressor);
10701 } else self.falsy = true;
10702 } else if (!(rr instanceof AST_Node)) {
10703 if (in_bool || parent.operator == "&&" && parent.left === compressor.self()) {
10704 AST_Node.warn("Dropping side-effect-free && [{file}:{line},{col}]", self.start);
10705 return self.left.optimize(compressor);
10706 }
10707 }
10708 // (x || false) && y ---> x ? y : false
10709 if (self.left.operator == "||") {
10710 var lr = fuzzy_eval(compressor, self.left.right);
10711 if (!lr) return make_node(AST_Conditional, self, {
10712 condition: self.left.left,
10713 consequent: self.right,
10714 alternative: self.left.right
10715 }).optimize(compressor);
10716 }
10717 break;
10718 case "??":
10719 var nullish = true;
10720 case "||":
10721 var ll = fuzzy_eval(compressor, self.left, nullish);
10722 if (nullish ? ll == null : !ll) {
10723 AST_Node.warn("Condition left of {operator} always {value} [{file}:{line},{col}]", {
10724 operator: self.operator,
10725 value: nullish ? "nulish" : "false",
10726 file: self.start.file,
10727 line: self.start.line,
10728 col: self.start.col,
10729 });
10730 return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
10731 } else if (!(ll instanceof AST_Node)) {
10732 AST_Node.warn("Condition left of {operator} always {value} [{file}:{line},{col}]", {
10733 operator: self.operator,
10734 value: nullish ? "defined" : "true",
10735 file: self.start.file,
10736 line: self.start.line,
10737 col: self.start.col,
10738 });
10739 return maintain_this_binding(compressor, parent, compressor.self(), self.left).optimize(compressor);
10740 }
10741 var rr = self.right.evaluate(compressor);
10742 if (!rr) {
10743 if (in_bool || parent.operator == "||" && parent.left === compressor.self()) {
10744 AST_Node.warn("Dropping side-effect-free {operator} [{file}:{line},{col}]", {
10745 operator: self.operator,
10746 file: self.start.file,
10747 line: self.start.line,
10748 col: self.start.col,
10749 });
10750 return self.left.optimize(compressor);
10751 }
10752 } else if (!nullish && !(rr instanceof AST_Node)) {
10753 if (in_bool) {
10754 AST_Node.warn("Boolean || always true [{file}:{line},{col}]", self.start);
10755 return make_sequence(self, [
10756 self.left,
10757 make_node(AST_True, self)
10758 ]).optimize(compressor);
10759 } else self.truthy = true;
10760 }
10761 // x && true || y ---> x ? true : y
10762 if (!nullish && self.left.operator == "&&") {
10763 var lr = fuzzy_eval(compressor, self.left.right);
10764 if (lr && !(lr instanceof AST_Node)) return make_node(AST_Conditional, self, {
10765 condition: self.left.left,
10766 consequent: self.left.right,
10767 alternative: self.right
10768 }).optimize(compressor);
10769 }
10770 break;
10771 case "+":
10772 // "foo" + ("bar" + x) ---> "foobar" + x
10773 if (self.left instanceof AST_Constant
10774 && self.right instanceof AST_Binary
10775 && self.right.operator == "+"
10776 && self.right.left instanceof AST_Constant
10777 && self.right.is_string(compressor)) {
10778 self = make_node(AST_Binary, self, {
10779 operator: "+",
10780 left: make_node(AST_String, self.left, {
10781 value: "" + self.left.value + self.right.left.value,
10782 start: self.left.start,
10783 end: self.right.left.end
10784 }),
10785 right: self.right.right
10786 });
10787 }
10788 // (x + "foo") + "bar" ---> x + "foobar"
10789 if (self.right instanceof AST_Constant
10790 && self.left instanceof AST_Binary
10791 && self.left.operator == "+"
10792 && self.left.right instanceof AST_Constant
10793 && self.left.is_string(compressor)) {
10794 self = make_node(AST_Binary, self, {
10795 operator: "+",
10796 left: self.left.left,
10797 right: make_node(AST_String, self.right, {
10798 value: "" + self.left.right.value + self.right.value,
10799 start: self.left.right.start,
10800 end: self.right.end
10801 })
10802 });
10803 }
10804 // a + -b ---> a - b
10805 if (self.right instanceof AST_UnaryPrefix
10806 && self.right.operator == "-"
10807 && self.left.is_number(compressor)) {
10808 self = make_node(AST_Binary, self, {
10809 operator: "-",
10810 left: self.left,
10811 right: self.right.expression
10812 });
10813 break;
10814 }
10815 // -a + b ---> b - a
10816 if (self.left instanceof AST_UnaryPrefix
10817 && self.left.operator == "-"
10818 && reversible()
10819 && self.right.is_number(compressor)) {
10820 self = make_node(AST_Binary, self, {
10821 operator: "-",
10822 left: self.right,
10823 right: self.left.expression
10824 });
10825 break;
10826 }
10827 // (a + b) + 3 ---> 3 + (a + b)
10828 if (compressor.option("unsafe_math")
10829 && self.left instanceof AST_Binary
10830 && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]
10831 && self.right.is_constant()
10832 && (self.right.is_boolean(compressor) || self.right.is_number(compressor))
10833 && self.left.is_number(compressor)
10834 && !self.left.right.is_constant()
10835 && (self.left.left.is_boolean(compressor) || self.left.left.is_number(compressor))) {
10836 self = make_node(AST_Binary, self, {
10837 operator: self.left.operator,
10838 left: make_node(AST_Binary, self, {
10839 operator: self.operator,
10840 left: self.right,
10841 right: self.left.left
10842 }),
10843 right: self.left.right
10844 });
10845 break;
10846 }
10847 case "-":
10848 // a - -b ---> a + b
10849 if (self.right instanceof AST_UnaryPrefix
10850 && self.right.operator == "-"
10851 && self.left.is_number(compressor)
10852 && self.right.expression.is_number(compressor)) {
10853 self = make_node(AST_Binary, self, {
10854 operator: "+",
10855 left: self.left,
10856 right: self.right.expression
10857 });
10858 break;
10859 }
10860 case "*":
10861 case "/":
10862 associative = compressor.option("unsafe_math");
10863 // +a - b ---> a - b
10864 // a - +b ---> a - b
10865 if (self.operator != "+") [ "left", "right" ].forEach(function(operand) {
10866 var node = self[operand];
10867 if (node instanceof AST_UnaryPrefix && node.operator == "+") {
10868 var exp = node.expression;
10869 if (exp.is_boolean(compressor) || exp.is_number(compressor) || exp.is_string(compressor)) {
10870 self[operand] = exp;
10871 }
10872 }
10873 });
10874 case "&":
10875 case "|":
10876 case "^":
10877 // a + +b ---> +b + a
10878 if (self.operator != "-"
10879 && self.operator != "/"
10880 && (self.left.is_boolean(compressor) || self.left.is_number(compressor))
10881 && (self.right.is_boolean(compressor) || self.right.is_number(compressor))
10882 && reversible()
10883 && !(self.left instanceof AST_Binary
10884 && self.left.operator != self.operator
10885 && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
10886 var reversed = make_node(AST_Binary, self, {
10887 operator: self.operator,
10888 left: self.right,
10889 right: self.left
10890 });
10891 if (self.right instanceof AST_Constant
10892 && !(self.left instanceof AST_Constant)) {
10893 self = best_of(compressor, reversed, self);
10894 } else {
10895 self = best_of(compressor, self, reversed);
10896 }
10897 }
10898 if (!associative || !self.is_number(compressor)) break;
10899 // a + (b + c) ---> (a + b) + c
10900 if (self.right instanceof AST_Binary
10901 && self.right.operator != "%"
10902 && PRECEDENCE[self.right.operator] == PRECEDENCE[self.operator]
10903 && self.right.is_number(compressor)
10904 && (self.operator != "+"
10905 || self.right.left.is_boolean(compressor)
10906 || self.right.left.is_number(compressor))
10907 && (self.operator != "-" || !self.left.is_negative_zero())
10908 && (self.right.left.is_constant_expression()
10909 || !self.right.right.has_side_effects(compressor))
10910 && !is_modify_array(self.right.right)) {
10911 self = make_node(AST_Binary, self, {
10912 operator: align(self.operator, self.right.operator),
10913 left: make_node(AST_Binary, self.left, {
10914 operator: self.operator,
10915 left: self.left,
10916 right: self.right.left,
10917 start: self.left.start,
10918 end: self.right.left.end
10919 }),
10920 right: self.right.right
10921 });
10922 if (self.operator == "+"
10923 && !self.right.is_boolean(compressor)
10924 && !self.right.is_number(compressor)) {
10925 self.right = make_node(AST_UnaryPrefix, self.right, {
10926 operator: "+",
10927 expression: self.right
10928 });
10929 }
10930 }
10931 // (2 * n) * 3 ---> 6 * n
10932 // (n + 2) + 3 ---> n + 5
10933 if (self.right instanceof AST_Constant
10934 && self.left instanceof AST_Binary
10935 && self.left.operator != "%"
10936 && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]
10937 && self.left.is_number(compressor)) {
10938 if (self.left.left instanceof AST_Constant) {
10939 var lhs = make_binary(self.left, self.operator, self.left.left, self.right, self.left.left.start, self.right.end);
10940 self = make_binary(self, self.left.operator, try_evaluate(compressor, lhs), self.left.right);
10941 } else if (self.left.right instanceof AST_Constant) {
10942 var op = align(self.left.operator, self.operator);
10943 var rhs = try_evaluate(compressor, make_binary(self.left, op, self.left.right, self.right));
10944 if (rhs.is_constant()
10945 && !(self.left.operator == "-"
10946 && self.right.value != 0
10947 && +rhs.value == 0
10948 && self.left.left.is_negative_zero())) {
10949 self = make_binary(self, self.left.operator, self.left.left, rhs);
10950 }
10951 }
10952 }
10953 break;
10954 }
10955 if (!(parent instanceof AST_UnaryPrefix && parent.operator == "delete")) {
10956 if (self.left instanceof AST_Number && !self.right.is_constant()) switch (self.operator) {
10957 // 0 + n ---> n
10958 case "+":
10959 if (self.left.value == 0) {
10960 if (self.right.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, {
10961 operator: "+",
10962 expression: self.right
10963 }).optimize(compressor);
10964 if (self.right.is_number(compressor) && !self.right.is_negative_zero()) return self.right;
10965 }
10966 break;
10967 // 1 * n ---> n
10968 case "*":
10969 if (self.left.value == 1) {
10970 return self.right.is_number(compressor) ? self.right : make_node(AST_UnaryPrefix, self, {
10971 operator: "+",
10972 expression: self.right
10973 }).optimize(compressor);
10974 }
10975 break;
10976 }
10977 if (self.right instanceof AST_Number && !self.left.is_constant()) switch (self.operator) {
10978 // n + 0 ---> n
10979 case "+":
10980 if (self.right.value == 0) {
10981 if (self.left.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, {
10982 operator: "+",
10983 expression: self.left
10984 }).optimize(compressor);
10985 if (self.left.is_number(compressor) && !self.left.is_negative_zero()) return self.left;
10986 }
10987 break;
10988 // n - 0 ---> n
10989 case "-":
10990 if (self.right.value == 0) {
10991 return self.left.is_number(compressor) ? self.left : make_node(AST_UnaryPrefix, self, {
10992 operator: "+",
10993 expression: self.left
10994 }).optimize(compressor);
10995 }
10996 break;
10997 // n / 1 ---> n
10998 case "/":
10999 if (self.right.value == 1) {
11000 return self.left.is_number(compressor) ? self.left : make_node(AST_UnaryPrefix, self, {
11001 operator: "+",
11002 expression: self.left
11003 }).optimize(compressor);
11004 }
11005 break;
11006 }
11007 }
11008 }
11009 if (compressor.option("typeofs")) switch (self.operator) {
11010 case "&&":
11011 mark_locally_defined(self.left, self.right, null);
11012 break;
11013 case "||":
11014 mark_locally_defined(self.left, null, self.right);
11015 break;
11016 }
11017 if (compressor.option("unsafe")) {
11018 var indexRight = is_indexFn(self.right);
11019 if (in_bool
11020 && indexRight
11021 && (self.operator == "==" || self.operator == "!=")
11022 && self.left instanceof AST_Number
11023 && self.left.value == 0) {
11024 return (self.operator == "==" ? make_node(AST_UnaryPrefix, self, {
11025 operator: "!",
11026 expression: self.right
11027 }) : self.right).optimize(compressor);
11028 }
11029 var indexLeft = is_indexFn(self.left);
11030 if (compressor.option("comparisons") && is_indexOf_match_pattern()) {
11031 var node = make_node(AST_UnaryPrefix, self, {
11032 operator: "!",
11033 expression: make_node(AST_UnaryPrefix, self, {
11034 operator: "~",
11035 expression: indexLeft ? self.left : self.right
11036 })
11037 });
11038 switch (self.operator) {
11039 case "<":
11040 if (indexLeft) break;
11041 case "<=":
11042 case "!=":
11043 node = make_node(AST_UnaryPrefix, self, {
11044 operator: "!",
11045 expression: node
11046 });
11047 break;
11048 }
11049 return node.optimize(compressor);
11050 }
11051 }
11052 return try_evaluate(compressor, self);
11053
11054 function is_modify_array(node) {
11055 var found = false;
11056 node.walk(new TreeWalker(function(node) {
11057 if (found) return true;
11058 if (node instanceof AST_Assign) {
11059 if (node.left instanceof AST_PropAccess) return found = true;
11060 } else if (node instanceof AST_Unary) {
11061 if (unary_side_effects[node.operator] && node.expression instanceof AST_PropAccess) {
11062 return found = true;
11063 }
11064 }
11065 }));
11066 return found;
11067 }
11068
11069 function align(ref, op) {
11070 switch (ref) {
11071 case "-":
11072 return op == "+" ? "-" : "+";
11073 case "/":
11074 return op == "*" ? "/" : "*";
11075 default:
11076 return op;
11077 }
11078 }
11079
11080 function make_binary(orig, op, left, right, start, end) {
11081 if (op == "+") {
11082 if (!left.is_boolean(compressor) && !left.is_number(compressor)) {
11083 left = make_node(AST_UnaryPrefix, left, {
11084 operator: "+",
11085 expression: left
11086 });
11087 }
11088 if (!right.is_boolean(compressor) && !right.is_number(compressor)) {
11089 right = make_node(AST_UnaryPrefix, right, {
11090 operator: "+",
11091 expression: right
11092 });
11093 }
11094 }
11095 return make_node(AST_Binary, orig, {
11096 operator: op,
11097 left: left,
11098 right: right,
11099 start: start,
11100 end: end
11101 });
11102 }
11103
11104 function is_indexFn(node) {
11105 return node.TYPE == "Call"
11106 && node.expression instanceof AST_Dot
11107 && indexFns[node.expression.property];
11108 }
11109
11110 function is_indexOf_match_pattern() {
11111 switch (self.operator) {
11112 case "<=":
11113 // 0 <= array.indexOf(string) ---> !!~array.indexOf(string)
11114 return indexRight && self.left instanceof AST_Number && self.left.value == 0;
11115 case "<":
11116 // array.indexOf(string) < 0 ---> !~array.indexOf(string)
11117 if (indexLeft && self.right instanceof AST_Number && self.right.value == 0) return true;
11118 // -1 < array.indexOf(string) ---> !!~array.indexOf(string)
11119 case "==":
11120 case "!=":
11121 // -1 == array.indexOf(string) ---> !~array.indexOf(string)
11122 // -1 != array.indexOf(string) ---> !!~array.indexOf(string)
11123 if (!indexRight) return false;
11124 return self.left instanceof AST_Number && self.left.value == -1
11125 || self.left instanceof AST_UnaryPrefix && self.left.operator == "-"
11126 && self.left.expression instanceof AST_Number && self.left.expression.value == 1;
11127 }
11128 }
11129 });
11130
11131 OPT(AST_SymbolExport, function(self) {
11132 return self;
11133 });
11134
11135 function recursive_ref(compressor, def, fn) {
11136 var level = 0, node = compressor.self();
11137 do {
11138 if (node === fn) return node;
11139 if (is_lambda(node) && node.name && node.name.definition() === def) return node;
11140 } while (node = compressor.parent(level++));
11141 }
11142
11143 function same_scope(def) {
11144 var scope = def.scope.resolve();
11145 return all(def.references, function(ref) {
11146 return scope === ref.scope.resolve();
11147 });
11148 }
11149
11150 OPT(AST_SymbolRef, function(self, compressor) {
11151 if (!compressor.option("ie")
11152 && is_undeclared_ref(self)
11153 // testing against `self.scope.uses_with` is an optimization
11154 && !(self.scope.resolve().uses_with && compressor.find_parent(AST_With))) {
11155 switch (self.name) {
11156 case "undefined":
11157 return make_node(AST_Undefined, self).optimize(compressor);
11158 case "NaN":
11159 return make_node(AST_NaN, self).optimize(compressor);
11160 case "Infinity":
11161 return make_node(AST_Infinity, self).optimize(compressor);
11162 }
11163 }
11164 var parent = compressor.parent();
11165 if (compressor.option("reduce_vars") && is_lhs(compressor.self(), parent) !== compressor.self()) {
11166 var def = self.definition();
11167 var fixed = self.fixed_value();
11168 var single_use = def.single_use && !(parent instanceof AST_Call && parent.is_expr_pure(compressor));
11169 if (single_use) {
11170 if (is_lambda(fixed)) {
11171 if ((def.scope !== self.scope.resolve() || def.in_loop)
11172 && (!compressor.option("reduce_funcs") || def.escaped.depth == 1 || fixed.inlined)) {
11173 single_use = false;
11174 } else if (recursive_ref(compressor, def, fixed)) {
11175 single_use = false;
11176 } else if (fixed.name && fixed.name.definition() !== def) {
11177 single_use = false;
11178 } else if (fixed.parent_scope !== self.scope || is_funarg(def)) {
11179 single_use = fixed.is_constant_expression(self.scope);
11180 if (single_use == "f") {
11181 var scope = self.scope;
11182 do {
11183 if (scope instanceof AST_LambdaDefinition || scope instanceof AST_LambdaExpression) {
11184 scope.inlined = true;
11185 }
11186 } while (scope = scope.parent_scope);
11187 }
11188 } else if (fixed.name && (fixed.name.name == "await" && is_async(fixed)
11189 || fixed.name.name == "yield" && is_generator(fixed))) {
11190 single_use = false;
11191 } else if (fixed.has_side_effects(compressor)) {
11192 single_use = false;
11193 } else if (compressor.option("ie") && fixed instanceof AST_Class) {
11194 single_use = false;
11195 }
11196 if (single_use) fixed.parent_scope = self.scope;
11197 } else if (!fixed
11198 || def.recursive_refs > 0
11199 || !fixed.is_constant_expression()
11200 || fixed.drop_side_effect_free(compressor)) {
11201 single_use = false;
11202 }
11203 }
11204 if (single_use) {
11205 def.single_use = false;
11206 fixed._squeezed = true;
11207 fixed.single_use = true;
11208 if (fixed instanceof AST_DefClass) fixed = to_class_expr(fixed);
11209 if (fixed instanceof AST_LambdaDefinition) fixed = to_func_expr(fixed);
11210 if (is_lambda(fixed)) {
11211 var scope = self.scope.resolve();
11212 fixed.enclosed.forEach(function(def) {
11213 if (fixed.variables.has(def.name)) return;
11214 if (scope.var_names()[def.name]) return;
11215 scope.enclosed.push(def);
11216 scope.var_names()[def.name] = true;
11217 });
11218 }
11219 var value;
11220 if (def.recursive_refs > 0) {
11221 value = fixed.clone(true);
11222 var defun_def = value.name.definition();
11223 var lambda_def = value.variables.get(value.name.name);
11224 var name = lambda_def && lambda_def.orig[0];
11225 var def_fn_name, symbol_type;
11226 if (value instanceof AST_Class) {
11227 def_fn_name = "def_function";
11228 symbol_type = AST_SymbolClass;
11229 } else {
11230 def_fn_name = "def_variable";
11231 symbol_type = AST_SymbolLambda;
11232 }
11233 if (!(name instanceof symbol_type)) {
11234 name = make_node(symbol_type, value.name, value.name);
11235 name.scope = value;
11236 value.name = name;
11237 lambda_def = value[def_fn_name](name);
11238 lambda_def.recursive_refs = def.recursive_refs;
11239 }
11240 value.walk(new TreeWalker(function(node) {
11241 if (node instanceof AST_SymbolDeclaration) {
11242 if (node !== name) {
11243 var def = node.definition();
11244 def.orig.push(node);
11245 def.eliminated++;
11246 }
11247 return;
11248 }
11249 if (!(node instanceof AST_SymbolRef)) return;
11250 var def = node.definition();
11251 if (def === defun_def) {
11252 node.thedef = def = lambda_def;
11253 } else {
11254 def.single_use = false;
11255 var fn = node.fixed_value();
11256 if (is_lambda(fn)
11257 && fn.name
11258 && fn.name.definition() === def
11259 && def.scope === fn.name.scope
11260 && fixed.variables.get(fn.name.name) === def) {
11261 fn.name = fn.name.clone();
11262 node.thedef = def = value.variables.get(fn.name.name) || value[def_fn_name](fn.name);
11263 }
11264 }
11265 def.references.push(node);
11266 }));
11267 } else {
11268 if (fixed instanceof AST_Scope) {
11269 compressor.push(fixed);
11270 value = fixed.optimize(compressor);
11271 compressor.pop();
11272 } else {
11273 value = fixed.optimize(compressor);
11274 }
11275 value = value.transform(new TreeTransformer(function(node, descend) {
11276 if (node instanceof AST_Scope) return node;
11277 node = node.clone();
11278 descend(node, this);
11279 return node;
11280 }));
11281 }
11282 def.replaced++;
11283 return value;
11284 }
11285 var local = self.fixed !== def.fixed;
11286 if (fixed && (local || def.should_replace !== false)) {
11287 var ev, init;
11288 if (fixed instanceof AST_This) {
11289 if (!is_funarg(def) && same_scope(def)) init = fixed;
11290 } else if ((ev = fixed.evaluate(compressor, true)) !== fixed
11291 && typeof ev != "function"
11292 && (ev === null
11293 || typeof ev != "object"
11294 || compressor.option("unsafe_regexp")
11295 && ev instanceof RegExp && !def.cross_loop && same_scope(def))) {
11296 init = make_node_from_constant(ev, fixed);
11297 }
11298 if (init) {
11299 if (!local && def.should_replace === undefined) {
11300 var value_length = init.optimize(compressor).print_to_string().length;
11301 if (!has_symbol_ref(fixed)) {
11302 value_length = Math.min(value_length, fixed.print_to_string().length);
11303 }
11304 var name_length = def.name.length;
11305 if (compressor.option("unused") && !compressor.exposed(def)) {
11306 var referenced = def.references.length - def.replaced;
11307 name_length += (name_length + 2 + value_length) / (referenced - def.assignments);
11308 }
11309 var delta = value_length - Math.floor(name_length);
11310 def.should_replace = delta < compressor.eval_threshold;
11311 }
11312 if (local || def.should_replace) {
11313 var value;
11314 if (has_symbol_ref(fixed)) {
11315 value = init.optimize(compressor);
11316 if (value === init) value = value.clone(true);
11317 } else {
11318 value = best_of_expression(init.optimize(compressor), fixed);
11319 if (value === init || value === fixed) value = value.clone(true);
11320 }
11321 def.replaced++;
11322 return value;
11323 }
11324 }
11325 }
11326 }
11327 return self;
11328
11329 function has_symbol_ref(value) {
11330 var found;
11331 value.walk(new TreeWalker(function(node) {
11332 if (node instanceof AST_SymbolRef) found = true;
11333 if (found) return true;
11334 }));
11335 return found;
11336 }
11337 });
11338
11339 function is_raw_tag(compressor, tag) {
11340 return compressor.option("unsafe")
11341 && tag instanceof AST_Dot
11342 && tag.property == "raw"
11343 && is_undeclared_ref(tag.expression)
11344 && tag.expression.name == "String";
11345 }
11346
11347 function decode_template(str) {
11348 var malformed = false;
11349 str = str.replace(/\\(u\{[^{}]*\}?|u[\s\S]{0,4}|x[\s\S]{0,2}|[0-9]+|[\s\S])/g, function(match, seq) {
11350 var ch = decode_escape_sequence(seq);
11351 if (typeof ch == "string") return ch;
11352 malformed = true;
11353 });
11354 if (!malformed) return str;
11355 }
11356
11357 OPT(AST_Template, function(self, compressor) {
11358 if (!compressor.option("templates")) return self;
11359 var tag = self.tag;
11360 if (!tag || is_raw_tag(compressor, tag)) {
11361 var exprs = [];
11362 var strs = [];
11363 for (var i = 0, status; i < self.strings.length; i++) {
11364 var str = self.strings[i];
11365 if (!tag) {
11366 var trimmed = decode_template(str);
11367 if (trimmed) str = escape_literal(trimmed);
11368 }
11369 if (i > 0) {
11370 var node = self.expressions[i - 1];
11371 var value = should_join(node);
11372 if (value) {
11373 var prev = strs[strs.length - 1];
11374 var joined = prev + value + str;
11375 var decoded;
11376 if (tag || typeof (decoded = decode_template(joined)) == status) {
11377 strs[strs.length - 1] = decoded ? escape_literal(decoded) : joined;
11378 continue;
11379 }
11380 }
11381 exprs.push(node);
11382 }
11383 strs.push(str);
11384 if (!tag) status = typeof trimmed;
11385 }
11386 if (!tag && strs.length > 1) {
11387 if (strs[strs.length - 1] == "") return make_node(AST_Binary, self, {
11388 operator: "+",
11389 left: make_node(AST_Template, self, {
11390 expressions: exprs.slice(0, -1),
11391 strings: strs.slice(0, -1),
11392 tag: tag,
11393 }).transform(compressor),
11394 right: exprs[exprs.length - 1],
11395 }).optimize(compressor);
11396 if (strs[0] == "") {
11397 var left = make_node(AST_Binary, self, {
11398 operator: "+",
11399 left: make_node(AST_String, self, { value: "" }),
11400 right: exprs[0],
11401 });
11402 for (var i = 1; strs[i] == "" && i < exprs.length; i++) {
11403 left = make_node(AST_Binary, self, {
11404 operator: "+",
11405 left: left,
11406 right: exprs[i],
11407 });
11408 }
11409 return best_of(compressor, self, make_node(AST_Binary, self, {
11410 operator: "+",
11411 left: left.transform(compressor),
11412 right: make_node(AST_Template, self, {
11413 expressions: exprs.slice(i),
11414 strings: strs.slice(i),
11415 tag: tag,
11416 }).transform(compressor),
11417 }).optimize(compressor));
11418 }
11419 }
11420 self.expressions = exprs;
11421 self.strings = strs;
11422 }
11423 return try_evaluate(compressor, self);
11424
11425 function escape_literal(str) {
11426 return str.replace(/\r|\\|`|\${/g, function(s) {
11427 return "\\" + (s == "\r" ? "r" : s);
11428 });
11429 }
11430
11431 function should_join(node) {
11432 var ev = node.evaluate(compressor);
11433 if (ev === node) return;
11434 if (tag && /\r|\\|`/.test(ev)) return;
11435 ev = escape_literal("" + ev);
11436 if (ev.length > node.print_to_string().length + "${}".length) return;
11437 return ev;
11438 }
11439 });
11440
11441 function is_atomic(lhs, self) {
11442 return lhs instanceof AST_SymbolRef || lhs.TYPE === self.TYPE;
11443 }
11444
11445 OPT(AST_Undefined, function(self, compressor) {
11446 if (compressor.option("unsafe_undefined")) {
11447 var undef = find_scope(compressor).find_variable("undefined");
11448 if (undef) {
11449 var ref = make_node(AST_SymbolRef, self, {
11450 name : "undefined",
11451 scope : undef.scope,
11452 thedef : undef
11453 });
11454 ref.is_undefined = true;
11455 return ref;
11456 }
11457 }
11458 var lhs = is_lhs(compressor.self(), compressor.parent());
11459 if (lhs && is_atomic(lhs, self)) return self;
11460 return make_node(AST_UnaryPrefix, self, {
11461 operator: "void",
11462 expression: make_node(AST_Number, self, {
11463 value: 0
11464 })
11465 });
11466 });
11467
11468 OPT(AST_Infinity, function(self, compressor) {
11469 var lhs = is_lhs(compressor.self(), compressor.parent());
11470 if (lhs && is_atomic(lhs, self)) return self;
11471 if (compressor.option("keep_infinity") && !lhs && !find_scope(compressor).find_variable("Infinity")) {
11472 return self;
11473 }
11474 return make_node(AST_Binary, self, {
11475 operator: "/",
11476 left: make_node(AST_Number, self, {
11477 value: 1
11478 }),
11479 right: make_node(AST_Number, self, {
11480 value: 0
11481 })
11482 });
11483 });
11484
11485 OPT(AST_NaN, function(self, compressor) {
11486 var lhs = is_lhs(compressor.self(), compressor.parent());
11487 if (lhs && is_atomic(lhs, self)) return self;
11488 if (!lhs && !find_scope(compressor).find_variable("NaN")) return self;
11489 return make_node(AST_Binary, self, {
11490 operator: "/",
11491 left: make_node(AST_Number, self, {
11492 value: 0
11493 }),
11494 right: make_node(AST_Number, self, {
11495 value: 0
11496 })
11497 });
11498 });
11499
11500 function is_reachable(self, defs) {
11501 var reachable = false;
11502 var find_ref = new TreeWalker(function(node) {
11503 if (reachable) return true;
11504 if (node instanceof AST_SymbolRef && member(node.definition(), defs)) {
11505 return reachable = true;
11506 }
11507 });
11508 var scan_scope = new TreeWalker(function(node) {
11509 if (reachable) return true;
11510 if (node instanceof AST_Lambda && node !== self) {
11511 if (!(node.name || is_async(node) || is_generator(node))) {
11512 var parent = scan_scope.parent();
11513 if (parent instanceof AST_Call && parent.expression === node) return;
11514 }
11515 node.walk(find_ref);
11516 return true;
11517 }
11518 });
11519 self.walk(scan_scope);
11520 return reachable;
11521 }
11522
11523 var ASSIGN_OPS = makePredicate("+ - * / % >> << >>> | ^ &");
11524 var ASSIGN_OPS_COMMUTATIVE = makePredicate("* | ^ &");
11525 OPT(AST_Assign, function(self, compressor) {
11526 if (compressor.option("dead_code")) {
11527 if (self.left instanceof AST_PropAccess) {
11528 if (self.operator == "=") {
11529 if (self.__drop) {
11530 var exprs = [ self.left.expression ];
11531 if (self.left instanceof AST_Sub) exprs.push(self.left.property);
11532 exprs.push(self.right);
11533 return make_sequence(self, exprs).optimize(compressor);
11534 }
11535 if (self.left.equivalent_to(self.right) && !self.left.has_side_effects(compressor)) {
11536 return self.right;
11537 }
11538 var exp = self.left.expression;
11539 if (exp instanceof AST_Lambda
11540 || !compressor.has_directive("use strict")
11541 && exp instanceof AST_Constant
11542 && !exp.may_throw_on_access(compressor)) {
11543 return self.left instanceof AST_Dot ? self.right : make_sequence(self, [
11544 self.left.property,
11545 self.right
11546 ]).optimize(compressor);
11547 }
11548 }
11549 } else if (self.left instanceof AST_SymbolRef && can_drop_symbol(self.left, compressor)) {
11550 var parent;
11551 if (self.operator == "=" && self.left.equivalent_to(self.right)
11552 && !((parent = compressor.parent()) instanceof AST_UnaryPrefix && parent.operator == "delete")) {
11553 return self.right;
11554 }
11555 if (self.left.is_immutable()) return strip_assignment();
11556 var def = self.left.definition();
11557 var scope = def.scope.resolve();
11558 var local = scope === compressor.find_parent(AST_Lambda);
11559 var level = 0, node;
11560 parent = compressor.self();
11561 if (!(scope.uses_arguments && is_funarg(def)) || compressor.has_directive("use strict")) do {
11562 node = parent;
11563 parent = compressor.parent(level++);
11564 if (parent instanceof AST_Assign) {
11565 if (parent.left instanceof AST_SymbolRef && parent.left.definition() === def) {
11566 if (in_try(level, parent)) break;
11567 return strip_assignment(def);
11568 }
11569 if (parent.left.match_symbol(function(node) {
11570 if (node instanceof AST_PropAccess) return true;
11571 })) break;
11572 continue;
11573 }
11574 if (parent instanceof AST_Exit) {
11575 if (!local) break;
11576 if (in_try(level, parent)) break;
11577 if (is_reachable(scope, [ def ])) break;
11578 return strip_assignment(def);
11579 }
11580 if (parent instanceof AST_SimpleStatement) {
11581 if (!local) break;
11582 if (is_reachable(scope, [ def ])) break;
11583 var stat;
11584 do {
11585 stat = parent;
11586 parent = compressor.parent(level++);
11587 if (parent === scope && is_last_statement(parent.body, stat)) return strip_assignment(def);
11588 } while (is_tail_block(stat, parent));
11589 break;
11590 }
11591 if (parent instanceof AST_VarDef) {
11592 if (!(parent.name instanceof AST_SymbolDeclaration)) continue;
11593 if (parent.name.definition() !== def) continue;
11594 if (in_try(level, parent)) break;
11595 return strip_assignment(def);
11596 }
11597 } while (is_tail(node, parent));
11598 }
11599 }
11600 if (compressor.option("sequences")) {
11601 var seq = self.lift_sequences(compressor);
11602 if (seq !== self) return seq.optimize(compressor);
11603 }
11604 if (compressor.option("assignments")) {
11605 if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) {
11606 // x = expr1 OP expr2
11607 if (self.right.left instanceof AST_SymbolRef
11608 && self.right.left.name == self.left.name
11609 && ASSIGN_OPS[self.right.operator]) {
11610 // x = x - 2 ---> x -= 2
11611 return make_node(AST_Assign, self, {
11612 operator: self.right.operator + "=",
11613 left: self.left,
11614 right: self.right.right,
11615 });
11616 }
11617 if (self.right.right instanceof AST_SymbolRef
11618 && self.right.right.name == self.left.name
11619 && ASSIGN_OPS_COMMUTATIVE[self.right.operator]
11620 && !self.right.left.has_side_effects(compressor)) {
11621 // x = 2 & x ---> x &= 2
11622 return make_node(AST_Assign, self, {
11623 operator: self.right.operator + "=",
11624 left: self.left,
11625 right: self.right.left,
11626 });
11627 }
11628 }
11629 if ((self.operator == "-=" || self.operator == "+="
11630 && (self.left.is_boolean(compressor) || self.left.is_number(compressor)))
11631 && self.right instanceof AST_Number
11632 && self.right.value == 1) {
11633 var op = self.operator.slice(0, -1);
11634 return make_node(AST_UnaryPrefix, self, {
11635 operator: op + op,
11636 expression: self.left
11637 });
11638 }
11639 }
11640 return try_evaluate(compressor, self);
11641
11642 function is_tail(node, parent) {
11643 if (parent instanceof AST_Binary) {
11644 return parent.right === node || parent.right.is_constant_expression(scope);
11645 }
11646 if (parent instanceof AST_Conditional) {
11647 return parent.condition !== node
11648 || parent.consequent.is_constant_expression(scope)
11649 && parent.alternative.is_constant_expression(scope);
11650 }
11651 if (parent instanceof AST_Sequence) {
11652 var exprs = parent.expressions;
11653 var stop = exprs.indexOf(node);
11654 if (stop < 0) return false;
11655 for (var i = exprs.length; --i > stop;) {
11656 if (!exprs[i].is_constant_expression(scope)) return false;
11657 }
11658 return true;
11659 }
11660 if (parent instanceof AST_UnaryPrefix) return true;
11661 }
11662
11663 function is_tail_block(stat, parent) {
11664 if (parent instanceof AST_BlockStatement) return is_last_statement(parent.body, stat);
11665 if (parent instanceof AST_Catch) return is_last_statement(parent.body, stat);
11666 if (parent instanceof AST_Finally) return is_last_statement(parent.body, stat);
11667 if (parent instanceof AST_If) return parent.body === stat || parent.alternative === stat;
11668 if (parent instanceof AST_Try) return parent.bfinally ? parent.bfinally === stat : parent.bcatch === stat;
11669 }
11670
11671 function in_try(level, node) {
11672 var right = self.right;
11673 self.right = make_node(AST_Null, right);
11674 var may_throw = node.may_throw(compressor);
11675 self.right = right;
11676 for (var parent; parent = compressor.parent(level++); node = parent) {
11677 if (parent === scope) return false;
11678 if (parent instanceof AST_Try) {
11679 if (parent.bfinally && parent.bfinally !== node) return true;
11680 if (may_throw && parent.bcatch && parent.bcatch !== node) return true;
11681 }
11682 }
11683 }
11684
11685 function strip_assignment(def) {
11686 if (def) def.fixed = false;
11687 return (self.operator != "=" ? make_node(AST_Binary, self, {
11688 operator: self.operator.slice(0, -1),
11689 left: self.left,
11690 right: self.right,
11691 }) : maintain_this_binding(compressor, compressor.parent(), self, self.right)).optimize(compressor);
11692 }
11693 });
11694
11695 OPT(AST_Conditional, function(self, compressor) {
11696 if (compressor.option("sequences") && self.condition instanceof AST_Sequence) {
11697 var expressions = self.condition.expressions.slice();
11698 self.condition = expressions.pop();
11699 expressions.push(self);
11700 return make_sequence(self, expressions);
11701 }
11702 if (!compressor.option("conditionals")) return self;
11703 var condition = self.condition;
11704 if (compressor.option("booleans") && !condition.has_side_effects(compressor)) {
11705 mark_duplicate_condition(compressor, condition);
11706 }
11707 condition = fuzzy_eval(compressor, condition);
11708 if (!condition) {
11709 AST_Node.warn("Condition always false [{file}:{line},{col}]", self.start);
11710 return make_sequence(self, [ self.condition, self.alternative ]).optimize(compressor);
11711 } else if (!(condition instanceof AST_Node)) {
11712 AST_Node.warn("Condition always true [{file}:{line},{col}]", self.start);
11713 return make_sequence(self, [ self.condition, self.consequent ]).optimize(compressor);
11714 }
11715 var negated = condition.negate(compressor, first_in_statement(compressor));
11716 if (best_of(compressor, condition, negated) === negated) {
11717 self = make_node(AST_Conditional, self, {
11718 condition: negated,
11719 consequent: self.alternative,
11720 alternative: self.consequent
11721 });
11722 negated = condition;
11723 condition = self.condition;
11724 }
11725 var consequent = self.consequent;
11726 var alternative = self.alternative;
11727 if (repeatable(compressor, condition)) {
11728 // x ? x : y ---> x || y
11729 if (condition.equivalent_to(consequent)) return make_node(AST_Binary, self, {
11730 operator: "||",
11731 left: condition,
11732 right: alternative,
11733 }).optimize(compressor);
11734 // x ? y : x ---> x && y
11735 if (condition.equivalent_to(alternative)) return make_node(AST_Binary, self, {
11736 operator: "&&",
11737 left: condition,
11738 right: consequent,
11739 }).optimize(compressor);
11740 }
11741 // if (foo) exp = something; else exp = something_else;
11742 // |
11743 // v
11744 // exp = foo ? something : something_else;
11745 var seq_tail = consequent.tail_node();
11746 if (seq_tail instanceof AST_Assign) {
11747 var is_eq = seq_tail.operator == "=";
11748 var alt_tail = is_eq ? alternative.tail_node() : alternative;
11749 if ((is_eq || consequent === seq_tail)
11750 && alt_tail instanceof AST_Assign
11751 && seq_tail.operator == alt_tail.operator
11752 && seq_tail.left.equivalent_to(alt_tail.left)
11753 && (is_eq && seq_tail.left instanceof AST_SymbolRef
11754 || !condition.has_side_effects(compressor)
11755 && can_shift_lhs_of_tail(consequent)
11756 && can_shift_lhs_of_tail(alternative))) {
11757 return make_node(AST_Assign, self, {
11758 operator: seq_tail.operator,
11759 left: seq_tail.left,
11760 right: make_node(AST_Conditional, self, {
11761 condition: condition,
11762 consequent: pop_lhs(consequent),
11763 alternative: pop_lhs(alternative)
11764 })
11765 });
11766 }
11767 }
11768 // x ? y : y ---> x, y
11769 if (consequent.equivalent_to(alternative)) return make_sequence(self, [
11770 condition,
11771 consequent
11772 ]).optimize(compressor);
11773 // x ? y.p : z.p ---> (x ? y : z).p
11774 // x ? y(a) : z(a) ---> (x ? y : z)(a)
11775 // x ? y.f(a) : z.f(a) ---> (x ? y : z).f(a)
11776 var combined = combine_tail(consequent, alternative, true);
11777 if (combined) return combined;
11778 // x ? y(a) : y(b) ---> y(x ? a : b)
11779 var arg_index;
11780 if (consequent instanceof AST_Call
11781 && alternative.TYPE == consequent.TYPE
11782 && (arg_index = arg_diff(consequent, alternative)) >= 0
11783 && consequent.expression.equivalent_to(alternative.expression)
11784 && !condition.has_side_effects(compressor)
11785 && !consequent.expression.has_side_effects(compressor)) {
11786 var node = consequent.clone();
11787 var arg = consequent.args[arg_index];
11788 node.args[arg_index] = arg instanceof AST_Spread ? make_node(AST_Spread, self, {
11789 expression: make_node(AST_Conditional, self, {
11790 condition: condition,
11791 consequent: arg.expression,
11792 alternative: alternative.args[arg_index].expression,
11793 }),
11794 }) : make_node(AST_Conditional, self, {
11795 condition: condition,
11796 consequent: arg,
11797 alternative: alternative.args[arg_index],
11798 });
11799 return node;
11800 }
11801 // x ? (y ? a : b) : b ---> x && y ? a : b
11802 if (consequent instanceof AST_Conditional
11803 && consequent.alternative.equivalent_to(alternative)) {
11804 return make_node(AST_Conditional, self, {
11805 condition: make_node(AST_Binary, self, {
11806 left: condition,
11807 operator: "&&",
11808 right: consequent.condition
11809 }),
11810 consequent: consequent.consequent,
11811 alternative: alternative
11812 });
11813 }
11814 // x ? (y ? a : b) : a ---> !x || y ? a : b
11815 if (consequent instanceof AST_Conditional
11816 && consequent.consequent.equivalent_to(alternative)) {
11817 return make_node(AST_Conditional, self, {
11818 condition: make_node(AST_Binary, self, {
11819 left: negated,
11820 operator: "||",
11821 right: consequent.condition
11822 }),
11823 consequent: alternative,
11824 alternative: consequent.alternative
11825 });
11826 }
11827 // x ? a : (y ? a : b) ---> x || y ? a : b
11828 if (alternative instanceof AST_Conditional
11829 && consequent.equivalent_to(alternative.consequent)) {
11830 return make_node(AST_Conditional, self, {
11831 condition: make_node(AST_Binary, self, {
11832 left: condition,
11833 operator: "||",
11834 right: alternative.condition
11835 }),
11836 consequent: consequent,
11837 alternative: alternative.alternative
11838 });
11839 }
11840 // x ? b : (y ? a : b) ---> !x && y ? a : b
11841 if (alternative instanceof AST_Conditional
11842 && consequent.equivalent_to(alternative.alternative)) {
11843 return make_node(AST_Conditional, self, {
11844 condition: make_node(AST_Binary, self, {
11845 left: negated,
11846 operator: "&&",
11847 right: alternative.condition
11848 }),
11849 consequent: alternative.consequent,
11850 alternative: consequent
11851 });
11852 }
11853 // x ? (a, c) : (b, c) ---> x ? a : b, c
11854 if ((consequent instanceof AST_Sequence || alternative instanceof AST_Sequence)
11855 && consequent.tail_node().equivalent_to(alternative.tail_node())) {
11856 return make_sequence(self, [
11857 make_node(AST_Conditional, self, {
11858 condition: condition,
11859 consequent: pop_seq(consequent),
11860 alternative: pop_seq(alternative)
11861 }),
11862 consequent.tail_node()
11863 ]).optimize(compressor);
11864 }
11865 // x ? y && a : a ---> (!x || y) && a
11866 if (consequent instanceof AST_Binary
11867 && consequent.operator == "&&"
11868 && consequent.right.equivalent_to(alternative)) {
11869 return make_node(AST_Binary, self, {
11870 operator: "&&",
11871 left: make_node(AST_Binary, self, {
11872 operator: "||",
11873 left: negated,
11874 right: consequent.left
11875 }),
11876 right: alternative
11877 }).optimize(compressor);
11878 }
11879 // x ? y || a : a ---> x && y || a
11880 if (consequent instanceof AST_Binary
11881 && consequent.operator == "||"
11882 && consequent.right.equivalent_to(alternative)) {
11883 return make_node(AST_Binary, self, {
11884 operator: "||",
11885 left: make_node(AST_Binary, self, {
11886 operator: "&&",
11887 left: condition,
11888 right: consequent.left
11889 }),
11890 right: alternative
11891 }).optimize(compressor);
11892 }
11893 // x ? a : y && a ---> (x || y) && a
11894 if (alternative instanceof AST_Binary
11895 && alternative.operator == "&&"
11896 && alternative.right.equivalent_to(consequent)) {
11897 return make_node(AST_Binary, self, {
11898 operator: "&&",
11899 left: make_node(AST_Binary, self, {
11900 operator: "||",
11901 left: condition,
11902 right: alternative.left
11903 }),
11904 right: consequent
11905 }).optimize(compressor);
11906 }
11907 // x ? a : y || a ---> !x && y || a
11908 if (alternative instanceof AST_Binary
11909 && alternative.operator == "||"
11910 && alternative.right.equivalent_to(consequent)) {
11911 return make_node(AST_Binary, self, {
11912 operator: "||",
11913 left: make_node(AST_Binary, self, {
11914 operator: "&&",
11915 left: negated,
11916 right: alternative.left
11917 }),
11918 right: consequent
11919 }).optimize(compressor);
11920 }
11921 var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
11922 if (is_true(consequent)) {
11923 if (is_false(alternative)) {
11924 // c ? true : false ---> !!c
11925 return booleanize(condition);
11926 }
11927 // c ? true : x ---> !!c || x
11928 return make_node(AST_Binary, self, {
11929 operator: "||",
11930 left: booleanize(condition),
11931 right: alternative
11932 });
11933 }
11934 if (is_false(consequent)) {
11935 if (is_true(alternative)) {
11936 // c ? false : true ---> !c
11937 return booleanize(condition.negate(compressor));
11938 }
11939 // c ? false : x ---> !c && x
11940 return make_node(AST_Binary, self, {
11941 operator: "&&",
11942 left: booleanize(condition.negate(compressor)),
11943 right: alternative
11944 });
11945 }
11946 if (is_true(alternative)) {
11947 // c ? x : true ---> !c || x
11948 return make_node(AST_Binary, self, {
11949 operator: "||",
11950 left: booleanize(condition.negate(compressor)),
11951 right: consequent
11952 });
11953 }
11954 if (is_false(alternative)) {
11955 // c ? x : false ---> !!c && x
11956 return make_node(AST_Binary, self, {
11957 operator: "&&",
11958 left: booleanize(condition),
11959 right: consequent
11960 });
11961 }
11962 if (compressor.option("typeofs")) mark_locally_defined(condition, consequent, alternative);
11963 return self;
11964
11965 function booleanize(node) {
11966 if (node.is_boolean(compressor)) return node;
11967 // !!expression
11968 return make_node(AST_UnaryPrefix, node, {
11969 operator: "!",
11970 expression: node.negate(compressor)
11971 });
11972 }
11973
11974 // AST_True or !0
11975 function is_true(node) {
11976 return node instanceof AST_True
11977 || in_bool
11978 && node instanceof AST_Constant
11979 && node.value
11980 || (node instanceof AST_UnaryPrefix
11981 && node.operator == "!"
11982 && node.expression instanceof AST_Constant
11983 && !node.expression.value);
11984 }
11985 // AST_False or !1 or void 0
11986 function is_false(node) {
11987 return node instanceof AST_False
11988 || in_bool
11989 && (node instanceof AST_Constant
11990 && !node.value
11991 || node instanceof AST_UnaryPrefix
11992 && node.operator == "void"
11993 && !node.expression.has_side_effects(compressor))
11994 || (node instanceof AST_UnaryPrefix
11995 && node.operator == "!"
11996 && node.expression instanceof AST_Constant
11997 && node.expression.value);
11998 }
11999
12000 function arg_diff(consequent, alternative) {
12001 var a = consequent.args;
12002 var b = alternative.args;
12003 var len = a.length;
12004 if (len != b.length) return -2;
12005 for (var i = 0; i < len; i++) {
12006 if (!a[i].equivalent_to(b[i])) {
12007 if (a[i] instanceof AST_Spread !== b[i] instanceof AST_Spread) return -3;
12008 for (var j = i + 1; j < len; j++) {
12009 if (!a[j].equivalent_to(b[j])) return -2;
12010 }
12011 return i;
12012 }
12013 }
12014 return -1;
12015 }
12016
12017 function is_tail_equivalent(consequent, alternative) {
12018 if (consequent.TYPE != alternative.TYPE) return;
12019 if (consequent.optional != alternative.optional) return;
12020 if (consequent instanceof AST_Call) {
12021 if (arg_diff(consequent, alternative) != -1) return;
12022 return consequent.TYPE != "Call"
12023 || !(consequent.expression instanceof AST_PropAccess
12024 || alternative.expression instanceof AST_PropAccess)
12025 || is_tail_equivalent(consequent.expression, alternative.expression);
12026 }
12027 if (!(consequent instanceof AST_PropAccess)) return;
12028 var p = consequent.property;
12029 var q = alternative.property;
12030 return (p instanceof AST_Node ? p.equivalent_to(q) : p == q)
12031 && !(consequent.expression instanceof AST_Super || alternative.expression instanceof AST_Super);
12032 }
12033
12034 function combine_tail(consequent, alternative, top) {
12035 if (!is_tail_equivalent(consequent, alternative)) return !top && make_node(AST_Conditional, self, {
12036 condition: condition,
12037 consequent: consequent,
12038 alternative: alternative,
12039 });
12040 var node = consequent.clone();
12041 node.expression = combine_tail(consequent.expression, alternative.expression);
12042 return node;
12043 }
12044
12045 function can_shift_lhs_of_tail(node) {
12046 return node === node.tail_node() || all(node.expressions.slice(0, -1), function(expr) {
12047 return !expr.has_side_effects(compressor);
12048 });
12049 }
12050
12051 function pop_lhs(node) {
12052 if (!(node instanceof AST_Sequence)) return node.right;
12053 var exprs = node.expressions.slice();
12054 exprs.push(exprs.pop().right);
12055 return make_sequence(node, exprs);
12056 }
12057
12058 function pop_seq(node) {
12059 if (!(node instanceof AST_Sequence)) return make_node(AST_Number, node, {
12060 value: 0
12061 });
12062 return make_sequence(node, node.expressions.slice(0, -1));
12063 }
12064 });
12065
12066 OPT(AST_Boolean, function(self, compressor) {
12067 if (!compressor.option("booleans")) return self;
12068 if (compressor.in_boolean_context()) return make_node(AST_Number, self, {
12069 value: +self.value
12070 });
12071 var p = compressor.parent();
12072 if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) {
12073 AST_Node.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
12074 operator : p.operator,
12075 value : self.value,
12076 file : p.start.file,
12077 line : p.start.line,
12078 col : p.start.col,
12079 });
12080 return make_node(AST_Number, self, {
12081 value: +self.value
12082 });
12083 }
12084 return make_node(AST_UnaryPrefix, self, {
12085 operator: "!",
12086 expression: make_node(AST_Number, self, {
12087 value: 1 - self.value
12088 })
12089 });
12090 });
12091
12092 OPT(AST_Spread, function(self, compressor) {
12093 var exp = self.expression;
12094 if (compressor.option("spreads") && exp instanceof AST_Array && !(compressor.parent() instanceof AST_Object)) {
12095 return List.splice(exp.elements.map(function(node) {
12096 return node instanceof AST_Hole ? make_node(AST_Undefined, node).optimize(compressor) : node;
12097 }));
12098 }
12099 return self;
12100 });
12101
12102 function safe_to_flatten(value, compressor) {
12103 if (!value) return false;
12104 var parent = compressor.parent();
12105 if (parent.TYPE != "Call") return true;
12106 if (parent.expression !== compressor.self()) return true;
12107 if (value instanceof AST_SymbolRef) {
12108 value = value.fixed_value();
12109 if (!value) return false;
12110 }
12111 return value instanceof AST_Lambda && !value.contains_this();
12112 }
12113
12114 OPT(AST_Sub, function(self, compressor) {
12115 var expr = self.expression;
12116 var prop = self.property;
12117 var terminated = trim_optional_chain(self, compressor);
12118 if (terminated) return terminated;
12119 if (compressor.option("properties")) {
12120 var key = prop.evaluate(compressor);
12121 if (key !== prop) {
12122 if (typeof key == "string") {
12123 if (key == "undefined") {
12124 key = undefined;
12125 } else {
12126 var value = parseFloat(key);
12127 if (value.toString() == key) {
12128 key = value;
12129 }
12130 }
12131 }
12132 prop = self.property = best_of_expression(prop, make_node_from_constant(key, prop).transform(compressor));
12133 var property = "" + key;
12134 if (is_identifier_string(property)
12135 && property.length <= prop.print_to_string().length + 1) {
12136 return make_node(AST_Dot, self, {
12137 optional: self.optional,
12138 expression: expr,
12139 property: property,
12140 }).optimize(compressor);
12141 }
12142 }
12143 }
12144 var parent = compressor.parent();
12145 var assigned = is_lhs(compressor.self(), parent);
12146 var def, fn, fn_parent, index;
12147 if (compressor.option("arguments")
12148 && expr instanceof AST_SymbolRef
12149 && is_arguments(def = expr.definition())
12150 && !expr.in_arg
12151 && prop instanceof AST_Number
12152 && Math.floor(index = prop.value) == index
12153 && (fn = def.scope) === find_lambda()
12154 && fn.uses_arguments < (assigned ? 2 : 3)) {
12155 if (parent instanceof AST_UnaryPrefix && parent.operator == "delete") {
12156 if (!def.deleted) def.deleted = [];
12157 def.deleted[index] = true;
12158 }
12159 var argname = fn.argnames[index];
12160 if (def.deleted && def.deleted[index]) {
12161 argname = null;
12162 } else if (argname) {
12163 var arg_def;
12164 if (!(argname instanceof AST_SymbolFunarg)
12165 || argname.name == "await"
12166 || expr.scope.find_variable(argname.name) !== (arg_def = argname.definition())) {
12167 argname = null;
12168 } else if (compressor.has_directive("use strict")
12169 || fn.name
12170 || fn.rest
12171 || !(fn_parent instanceof AST_Call
12172 && index < fn_parent.args.length
12173 && all(fn_parent.args.slice(0, index + 1), function(arg) {
12174 return !(arg instanceof AST_Spread);
12175 }))
12176 || !all(fn.argnames, function(argname) {
12177 return argname instanceof AST_SymbolFunarg;
12178 })) {
12179 if (has_reassigned() || arg_def.assignments || arg_def.orig.length > 1) argname = null;
12180 }
12181 } else if ((assigned || !has_reassigned())
12182 && index < fn.argnames.length + 5
12183 && compressor.drop_fargs(fn, fn_parent)
12184 && !fn.rest) {
12185 while (index >= fn.argnames.length) {
12186 argname = fn.make_var(AST_SymbolFunarg, fn, "argument_" + fn.argnames.length);
12187 fn.argnames.push(argname);
12188 }
12189 }
12190 if (argname && find_if(function(node) {
12191 return node.name === argname.name;
12192 }, fn.argnames) === argname) {
12193 if (assigned) def.reassigned--;
12194 var sym = make_node(AST_SymbolRef, self, argname);
12195 sym.reference();
12196 delete argname.__unused;
12197 return sym;
12198 }
12199 }
12200 if (assigned) return self;
12201 if (compressor.option("sequences")
12202 && parent.TYPE != "Call"
12203 && !(parent instanceof AST_ForEnumeration && parent.init === self)) {
12204 var seq = lift_sequence_in_expression(self, compressor);
12205 if (seq !== self) return seq.optimize(compressor);
12206 }
12207 if (key !== prop) {
12208 var sub = self.flatten_object(property, compressor);
12209 if (sub) {
12210 expr = self.expression = sub.expression;
12211 prop = self.property = sub.property;
12212 }
12213 }
12214 var elements;
12215 if (compressor.option("properties")
12216 && compressor.option("side_effects")
12217 && prop instanceof AST_Number
12218 && expr instanceof AST_Array
12219 && all(elements = expr.elements, function(value) {
12220 return !(value instanceof AST_Spread);
12221 })) {
12222 var index = prop.value;
12223 var retValue = elements[index];
12224 if (safe_to_flatten(retValue, compressor)) {
12225 var is_hole = retValue instanceof AST_Hole;
12226 var flatten = !is_hole;
12227 var values = [];
12228 for (var i = elements.length; --i > index;) {
12229 var value = elements[i].drop_side_effect_free(compressor);
12230 if (value) {
12231 values.unshift(value);
12232 if (flatten && value.has_side_effects(compressor)) flatten = false;
12233 }
12234 }
12235 if (!flatten) values.unshift(retValue);
12236 while (--i >= 0) {
12237 var value = elements[i].drop_side_effect_free(compressor);
12238 if (value) {
12239 values.unshift(value);
12240 } else if (is_hole) {
12241 values.unshift(make_node(AST_Hole, elements[i]));
12242 } else {
12243 index--;
12244 }
12245 }
12246 if (flatten) {
12247 values.push(retValue);
12248 return make_sequence(self, values).optimize(compressor);
12249 } else return make_node(AST_Sub, self, {
12250 expression: make_node(AST_Array, expr, { elements: values }),
12251 property: make_node(AST_Number, prop, { value: index }),
12252 });
12253 }
12254 }
12255 return try_evaluate(compressor, self);
12256
12257 function find_lambda() {
12258 var i = 0, p;
12259 while (p = compressor.parent(i++)) {
12260 if (p instanceof AST_Lambda) {
12261 if (p instanceof AST_Accessor) return;
12262 if (is_arrow(p)) continue;
12263 fn_parent = compressor.parent(i);
12264 return p;
12265 }
12266 }
12267 }
12268
12269 function has_reassigned() {
12270 return !compressor.option("reduce_vars") || def.reassigned;
12271 }
12272 });
12273
12274 AST_Arrow.DEFMETHOD("contains_super", return_false);
12275 AST_AsyncArrow.DEFMETHOD("contains_super", return_false);
12276 AST_Lambda.DEFMETHOD("contains_super", function() {
12277 var result;
12278 var self = this;
12279 self.walk(new TreeWalker(function(node) {
12280 if (result) return true;
12281 if (node instanceof AST_Super) return result = true;
12282 if (node !== self && node instanceof AST_Scope && !is_arrow(node)) return true;
12283 }));
12284 return result;
12285 });
12286 AST_LambdaDefinition.DEFMETHOD("contains_super", return_false);
12287 AST_Scope.DEFMETHOD("contains_super", return_false);
12288
12289 AST_Arrow.DEFMETHOD("contains_this", return_false);
12290 AST_AsyncArrow.DEFMETHOD("contains_this", return_false);
12291 AST_Node.DEFMETHOD("contains_this", function() {
12292 var result;
12293 var self = this;
12294 self.walk(new TreeWalker(function(node) {
12295 if (result) return true;
12296 if (node instanceof AST_This) return result = true;
12297 if (node !== self && node instanceof AST_Scope && !is_arrow(node)) return true;
12298 }));
12299 return result;
12300 });
12301
12302 function can_hoist_property(prop) {
12303 return prop instanceof AST_ObjectKeyVal
12304 && typeof prop.key == "string"
12305 && !(prop instanceof AST_ObjectMethod && prop.value.contains_super());
12306 }
12307
12308 AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) {
12309 if (!compressor.option("properties")) return;
12310 if (key === "__proto__") return;
12311 var expr = this.expression;
12312 if (expr instanceof AST_Object) {
12313 var props = expr.properties;
12314 for (var i = props.length; --i >= 0;) {
12315 var prop = props[i];
12316 if (prop.key !== key) continue;
12317 if (!all(props, can_hoist_property)) break;
12318 if (!safe_to_flatten(prop.value, compressor)) break;
12319 props = props.map(function(prop) {
12320 return prop.value;
12321 });
12322 if (prop instanceof AST_ObjectMethod
12323 && prop.value instanceof AST_Function
12324 && !(compressor.parent() instanceof AST_Call)) {
12325 if (prop.value.uses_arguments) break;
12326 props[i] = make_node(AST_Arrow, prop.value, prop.value);
12327 }
12328 return make_node(AST_Sub, this, {
12329 expression: make_node(AST_Array, expr, { elements: props }),
12330 property: make_node(AST_Number, this, { value: i }),
12331 });
12332 }
12333 }
12334 });
12335
12336 OPT(AST_Dot, function(self, compressor) {
12337 if (self.property == "arguments" || self.property == "caller") {
12338 AST_Node.warn("Function.prototype.{prop} not supported [{file}:{line},{col}]", {
12339 prop: self.property,
12340 file: self.start.file,
12341 line: self.start.line,
12342 col: self.start.col,
12343 });
12344 }
12345 var parent = compressor.parent();
12346 if (is_lhs(compressor.self(), parent)) return self;
12347 var terminated = trim_optional_chain(self, compressor);
12348 if (terminated) return terminated;
12349 if (compressor.option("sequences")
12350 && parent.TYPE != "Call"
12351 && !(parent instanceof AST_ForEnumeration && parent.init === self)) {
12352 var seq = lift_sequence_in_expression(self, compressor);
12353 if (seq !== self) return seq.optimize(compressor);
12354 }
12355 if (compressor.option("unsafe_proto")
12356 && self.expression instanceof AST_Dot
12357 && self.expression.property == "prototype") {
12358 var exp = self.expression.expression;
12359 if (is_undeclared_ref(exp)) switch (exp.name) {
12360 case "Array":
12361 self.expression = make_node(AST_Array, self.expression, {
12362 elements: []
12363 });
12364 break;
12365 case "Function":
12366 self.expression = make_node(AST_Function, self.expression, {
12367 argnames: [],
12368 body: []
12369 }).init_vars(exp.scope);
12370 break;
12371 case "Number":
12372 self.expression = make_node(AST_Number, self.expression, {
12373 value: 0
12374 });
12375 break;
12376 case "Object":
12377 self.expression = make_node(AST_Object, self.expression, {
12378 properties: []
12379 });
12380 break;
12381 case "RegExp":
12382 self.expression = make_node(AST_RegExp, self.expression, {
12383 value: /t/
12384 });
12385 break;
12386 case "String":
12387 self.expression = make_node(AST_String, self.expression, {
12388 value: ""
12389 });
12390 break;
12391 }
12392 }
12393 var sub = self.flatten_object(self.property, compressor);
12394 if (sub) return sub.optimize(compressor);
12395 return try_evaluate(compressor, self);
12396 });
12397
12398 OPT(AST_DestructuredArray, function(self, compressor) {
12399 if (compressor.option("rests") && self.rest instanceof AST_DestructuredArray) {
12400 return make_node(AST_DestructuredArray, self, {
12401 elements: self.elements.concat(self.rest.elements),
12402 rest: self.rest.rest,
12403 });
12404 }
12405 return self;
12406 });
12407
12408 OPT(AST_DestructuredKeyVal, function(self, compressor) {
12409 if (compressor.option("objects")) {
12410 var key = self.key;
12411 if (key instanceof AST_Node) {
12412 key = key.evaluate(compressor);
12413 if (key !== self.key) self.key = "" + key;
12414 }
12415 }
12416 return self;
12417 });
12418
12419 OPT(AST_Object, function(self, compressor) {
12420 if (!compressor.option("objects")) return self;
12421 var changed = false;
12422 var found = false;
12423 var generated = false;
12424 var keep_duplicate = compressor.has_directive("use strict");
12425 var keys = new Dictionary();
12426 var values = [];
12427 self.properties.forEach(function(prop) {
12428 if (!(prop instanceof AST_Spread)) return process(prop);
12429 found = true;
12430 var exp = prop.expression;
12431 if (compressor.option("spreads") && exp instanceof AST_Object && all(exp.properties, function(prop) {
12432 if (prop instanceof AST_ObjectGetter) return false;
12433 if (prop instanceof AST_Spread) return false;
12434 if (prop.key !== "__proto__") return true;
12435 if (prop instanceof AST_ObjectSetter) return true;
12436 return !prop.value.has_side_effects(compressor);
12437 })) {
12438 changed = true;
12439 exp.properties.forEach(function(prop) {
12440 var key = prop.key;
12441 var setter = prop instanceof AST_ObjectSetter;
12442 if (key === "__proto__") {
12443 if (!setter) return;
12444 key = make_node_from_constant(key, prop);
12445 }
12446 process(setter ? make_node(AST_ObjectKeyVal, prop, {
12447 key: key,
12448 value: make_node(AST_Undefined, prop).optimize(compressor),
12449 }) : prop);
12450 });
12451 } else {
12452 generated = true;
12453 flush();
12454 values.push(prop);
12455 }
12456 });
12457 flush();
12458 if (!changed) return self;
12459 if (found && generated && values.length == 1) {
12460 var value = values[0];
12461 if (value instanceof AST_ObjectProperty && value.key instanceof AST_Number) {
12462 value.key = "" + value.key.value;
12463 }
12464 }
12465 return make_node(AST_Object, self, { properties: values });
12466
12467 function flush() {
12468 keys.each(function(props) {
12469 if (props.length == 1) return values.push(props[0]);
12470 changed = true;
12471 var tail = keep_duplicate && !generated && props.pop();
12472 values.push(props.length == 1 ? props[0] : make_node(AST_ObjectKeyVal, self, {
12473 key: props[0].key,
12474 value: make_sequence(self, props.map(function(prop) {
12475 return prop.value;
12476 }))
12477 }));
12478 if (tail) values.push(tail);
12479 });
12480 keys = new Dictionary();
12481 }
12482
12483 function process(prop) {
12484 var key = prop.key;
12485 if (key instanceof AST_Node) {
12486 found = true;
12487 key = key.evaluate(compressor);
12488 if (key === prop.key || key === "__proto__") {
12489 generated = true;
12490 } else {
12491 key = prop.key = "" + key;
12492 }
12493 }
12494 if (can_hoist_property(prop)) {
12495 if (prop.value.has_side_effects(compressor)) flush();
12496 keys.add(key, prop);
12497 } else {
12498 flush();
12499 values.push(prop);
12500 }
12501 if (found && !generated && typeof key == "string" && RE_POSITIVE_INTEGER.test(key)) {
12502 generated = true;
12503 if (keys.has(key)) prop = keys.get(key)[0];
12504 prop.key = make_node(AST_Number, prop, { value: +key });
12505 }
12506 }
12507 });
12508
12509 OPT(AST_Return, function(self, compressor) {
12510 if (compressor.option("side_effects")
12511 && self.value
12512 && is_undefined(self.value, compressor)
12513 && !in_async_generator(compressor.find_parent(AST_Scope))) {
12514 self.value = null;
12515 }
12516 return self;
12517 });
12518})(function(node, optimizer) {
12519 node.DEFMETHOD("optimize", function(compressor) {
12520 var self = this;
12521 if (self._optimized) return self;
12522 if (compressor.has_directive("use asm")) return self;
12523 var opt = optimizer(self, compressor);
12524 opt._optimized = true;
12525 return opt;
12526 });
12527});