fix corner case in `hoist_vars` (#5188)
[UglifyJS.git] / lib / compress.js
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
46 function 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
188 Compressor.prototype = new TreeTransformer;
189 merge(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 (level == 0 && value && value.is_constant()) return;
393         if (parent instanceof AST_Array) return is_modified(compressor, tw, parent, parent, level + 1);
394         if (parent instanceof AST_Assign) switch (parent.operator) {
395           case "=":
396             return is_modified(compressor, tw, parent, value, level + 1, immutable, recursive);
397           case "&&=":
398           case "||=":
399           case "??=":
400             return is_modified(compressor, tw, parent, parent, level + 1);
401           default:
402             return;
403         }
404         if (parent instanceof AST_Binary) {
405             if (!lazy_op[parent.operator]) return;
406             return is_modified(compressor, tw, parent, parent, level + 1);
407         }
408         if (parent instanceof AST_Call) {
409             return !immutable
410                 && parent.expression === node
411                 && !parent.is_expr_pure(compressor)
412                 && (!(value instanceof AST_LambdaExpression) || !(parent instanceof AST_New) && value.contains_this());
413         }
414         if (parent instanceof AST_Conditional) {
415             if (parent.condition === node) return;
416             return is_modified(compressor, tw, parent, parent, level + 1);
417         }
418         if (parent instanceof AST_ForEnumeration) return parent.init === node;
419         if (parent instanceof AST_ObjectKeyVal) {
420             if (parent.value !== node) return;
421             var obj = tw.parent(level + 1);
422             return is_modified(compressor, tw, obj, obj, level + 2);
423         }
424         if (parent instanceof AST_PropAccess) {
425             if (parent.expression !== node) return;
426             var prop = read_property(value, parent);
427             return (!immutable || recursive) && is_modified(compressor, tw, parent, prop, level + 1);
428         }
429         if (parent instanceof AST_Sequence) {
430             if (parent.tail_node() !== node) return;
431             return is_modified(compressor, tw, parent, value, level + 1, immutable, recursive);
432         }
433     }
434
435     function is_lambda(node) {
436         return node instanceof AST_Class || node instanceof AST_Lambda;
437     }
438
439     function safe_for_extends(node) {
440         return node instanceof AST_Class || node instanceof AST_Defun || node instanceof AST_Function;
441     }
442
443     function is_arguments(def) {
444         return def.name == "arguments" && def.scope.uses_arguments;
445     }
446
447     function is_funarg(def) {
448         return def.orig[0] instanceof AST_SymbolFunarg || def.orig[1] instanceof AST_SymbolFunarg;
449     }
450
451     function cross_scope(def, sym) {
452         do {
453             if (def === sym) return false;
454             if (sym instanceof AST_Scope) return true;
455         } while (sym = sym.parent_scope);
456     }
457
458     function can_drop_symbol(ref, compressor, keep_lambda) {
459         var def = ref.definition();
460         if (ref.in_arg && is_funarg(def)) return false;
461         return all(def.orig, function(sym) {
462             if (sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet) {
463                 return compressor && can_varify(compressor, sym);
464             }
465             return !(keep_lambda && sym instanceof AST_SymbolLambda);
466         });
467     }
468
469     function has_escaped(d, scope, node, parent) {
470         if (parent instanceof AST_Assign) return parent.operator == "=" && parent.right === node;
471         if (parent instanceof AST_Call) return parent.expression !== node || parent instanceof AST_New;
472         if (parent instanceof AST_Exit) return parent.value === node && scope.resolve() !== d.scope.resolve();
473         if (parent instanceof AST_VarDef) return parent.value === node;
474     }
475
476     var RE_POSITIVE_INTEGER = /^(0|[1-9][0-9]*)$/;
477     (function(def) {
478         def(AST_Node, noop);
479
480         function reset_def(tw, compressor, def) {
481             def.assignments = 0;
482             def.bool_fn = 0;
483             def.cross_loop = false;
484             def.direct_access = false;
485             def.escaped = [];
486             def.fixed = !def.const_redefs
487                 && !def.scope.pinned()
488                 && !compressor.exposed(def)
489                 && !(def.init instanceof AST_LambdaExpression && def.init !== def.scope)
490                 && def.init;
491             def.reassigned = 0;
492             def.recursive_refs = 0;
493             def.references = [];
494             def.should_replace = undefined;
495             def.single_use = undefined;
496         }
497
498         function reset_block_variables(tw, compressor, scope) {
499             scope.variables.each(function(def) {
500                 reset_def(tw, compressor, def);
501             });
502         }
503
504         function reset_variables(tw, compressor, scope) {
505             scope.fn_defs = [];
506             scope.variables.each(function(def) {
507                 reset_def(tw, compressor, def);
508                 var init = def.init;
509                 if (init instanceof AST_LambdaDefinition) {
510                     scope.fn_defs.push(init);
511                     init.safe_ids = null;
512                 }
513                 if (def.fixed === null) {
514                     def.safe_ids = tw.safe_ids;
515                     mark(tw, def);
516                 } else if (def.fixed) {
517                     tw.loop_ids[def.id] = tw.in_loop;
518                     mark(tw, def);
519                 }
520             });
521             scope.may_call_this = function() {
522                 scope.may_call_this = scope.contains_this() ? return_true : return_false;
523             };
524             if (scope.uses_arguments) scope.each_argname(function(node) {
525                 node.definition().last_ref = false;
526             });
527             if (compressor.option("ie")) scope.variables.each(function(def) {
528                 var d = def.orig[0].definition();
529                 if (d !== def) d.fixed = false;
530             });
531         }
532
533         function walk_fn_def(tw, fn) {
534             var was_scanning = tw.fn_scanning;
535             tw.fn_scanning = fn;
536             fn.walk(tw);
537             tw.fn_scanning = was_scanning;
538         }
539
540         function revisit_fn_def(tw, fn) {
541             fn.enclosed.forEach(function(d) {
542                 if (fn.variables.get(d.name) === d) return;
543                 if (safe_to_read(tw, d)) return;
544                 d.single_use = false;
545                 var fixed = d.fixed;
546                 if (typeof fixed == "function") fixed = fixed();
547                 if (fixed instanceof AST_Lambda && HOP(fixed, "safe_ids")) return;
548                 d.fixed = false;
549             });
550         }
551
552         function mark_fn_def(tw, def, fn) {
553             if (!HOP(fn, "safe_ids")) return;
554             var marker = fn.safe_ids;
555             if (marker === false) return;
556             if (fn.parent_scope.resolve().may_call_this === return_true) {
557                 if (member(fn, tw.fn_visited)) revisit_fn_def(tw, fn);
558             } else if (marker) {
559                 var visited = member(fn, tw.fn_visited);
560                 if (marker === tw.safe_ids) {
561                     if (!visited) walk_fn_def(tw, fn);
562                 } else if (visited) {
563                     revisit_fn_def(tw, fn);
564                 } else {
565                     fn.safe_ids = false;
566                 }
567             } else if (tw.fn_scanning && tw.fn_scanning !== def.scope.resolve()) {
568                 fn.safe_ids = false;
569             } else {
570                 fn.safe_ids = tw.safe_ids;
571                 walk_fn_def(tw, fn);
572             }
573         }
574
575         function pop_scope(tw, scope) {
576             var fn_defs = scope.fn_defs;
577             var tangled = scope.may_call_this === return_true ? fn_defs : fn_defs.filter(function(fn) {
578                 if (fn.safe_ids === false) return true;
579                 fn.safe_ids = tw.safe_ids;
580                 walk_fn_def(tw, fn);
581                 return false;
582             });
583             pop(tw);
584             tangled.forEach(function(fn) {
585                 fn.safe_ids = tw.safe_ids;
586                 walk_fn_def(tw, fn);
587             });
588             fn_defs.forEach(function(fn) {
589                 delete fn.safe_ids;
590             });
591             delete scope.fn_defs;
592             delete scope.may_call_this;
593         }
594
595         function push(tw) {
596             tw.safe_ids = Object.create(tw.safe_ids);
597         }
598
599         function pop(tw) {
600             tw.safe_ids = Object.getPrototypeOf(tw.safe_ids);
601         }
602
603         function mark(tw, def) {
604             tw.safe_ids[def.id] = {};
605         }
606
607         function push_ref(def, ref) {
608             def.references.push(ref);
609             if (def.last_ref !== false) def.last_ref = ref;
610         }
611
612         function safe_to_read(tw, def) {
613             if (def.single_use == "m") return false;
614             var safe = tw.safe_ids[def.id];
615             if (safe) {
616                 var in_order = HOP(tw.safe_ids, def.id);
617                 if (!in_order) safe.read = safe.read && safe.read !== tw.safe_ids ? true : tw.safe_ids;
618                 if (def.fixed == null) {
619                     if (is_arguments(def)) return false;
620                     if (def.global && def.name == "arguments") return false;
621                     tw.loop_ids[def.id] = null;
622                     def.fixed = make_node(AST_Undefined, def.orig[0]);
623                     if (in_order) delete def.safe_ids;
624                     return true;
625                 }
626                 return !safe.assign || safe.assign === tw.safe_ids;
627             }
628             return def.fixed instanceof AST_LambdaDefinition;
629         }
630
631         function safe_to_assign(tw, def, declare) {
632             if (!declare) {
633                 if (is_funarg(def) && def.scope.uses_arguments && !tw.has_directive("use strict")) return false;
634                 if (!all(def.orig, function(sym) {
635                     return !(sym instanceof AST_SymbolConst);
636                 })) return false;
637             }
638             if (def.fixed === undefined) return declare || all(def.orig, function(sym) {
639                 return !(sym instanceof AST_SymbolLet);
640             });
641             if (def.fixed === false || def.fixed === 0) return false;
642             var safe = tw.safe_ids[def.id];
643             if (def.safe_ids) {
644                 def.safe_ids[def.id] = false;
645                 delete def.safe_ids;
646                 return def.fixed === null || HOP(tw.safe_ids, def.id) && !safe.read;
647             }
648             if (!HOP(tw.safe_ids, def.id)) {
649                 if (!safe) return false;
650                 if (safe.read) {
651                     var scope = tw.find_parent(AST_BlockScope);
652                     if (scope instanceof AST_Class) return false;
653                     if (def.scope.resolve() !== scope.resolve()) return false;
654                 }
655                 safe.assign = safe.assign && safe.assign !== tw.safe_ids ? true : tw.safe_ids;
656             }
657             if (def.fixed != null && safe.read) {
658                 if (safe.read !== tw.safe_ids) return false;
659                 if (tw.loop_ids[def.id] !== tw.in_loop) return false;
660             }
661             return safe_to_read(tw, def) && all(def.orig, function(sym) {
662                 return !(sym instanceof AST_SymbolLambda);
663             });
664         }
665
666         function make_ref(ref, fixed) {
667             var node = make_node(AST_SymbolRef, ref, ref);
668             node.fixed = fixed || make_node(AST_Undefined, ref);
669             return node;
670         }
671
672         function ref_once(compressor, def) {
673             return compressor.option("unused")
674                 && !def.scope.pinned()
675                 && def.single_use !== false
676                 && def.references.length - def.recursive_refs == 1
677                 && !(is_funarg(def) && def.scope.uses_arguments);
678         }
679
680         function is_immutable(value) {
681             if (!value) return false;
682             if (value instanceof AST_Assign) {
683                 var op = value.operator;
684                 return op == "=" ? is_immutable(value.right) : !lazy_op[op.slice(0, -1)];
685             }
686             if (value instanceof AST_Sequence) return is_immutable(value.tail_node());
687             return value.is_constant() || is_lambda(value) || value instanceof AST_ObjectIdentity;
688         }
689
690         function value_in_use(node, parent) {
691             if (parent instanceof AST_Array) return true;
692             if (parent instanceof AST_Binary) return lazy_op[parent.operator];
693             if (parent instanceof AST_Conditional) return parent.condition !== node;
694             if (parent instanceof AST_Sequence) return parent.tail_node() === node;
695             if (parent instanceof AST_Spread) return true;
696         }
697
698         function mark_escaped(tw, d, scope, node, value, level, depth) {
699             var parent = tw.parent(level);
700             if (value && value.is_constant()) return;
701             if (has_escaped(d, scope, node, parent)) {
702                 d.escaped.push(parent);
703                 if (depth > 1 && !(value && value.is_constant_expression(scope))) depth = 1;
704                 if (!d.escaped.depth || d.escaped.depth > depth) d.escaped.depth = depth;
705                 if (d.scope.resolve() !== scope.resolve()) d.escaped.cross_scope = true;
706                 if (d.fixed) d.fixed.escaped = d.escaped;
707                 return;
708             } else if (value_in_use(node, parent)) {
709                 mark_escaped(tw, d, scope, parent, parent, level + 1, depth);
710             } else if (parent instanceof AST_ObjectKeyVal && parent.value === node) {
711                 var obj = tw.parent(level + 1);
712                 mark_escaped(tw, d, scope, obj, obj, level + 2, depth);
713             } else if (parent instanceof AST_PropAccess && parent.expression === node) {
714                 value = read_property(value, parent);
715                 mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1);
716                 if (value) return;
717             }
718             if (level > 0) return;
719             if (parent instanceof AST_Call && parent.expression === node) return;
720             if (parent instanceof AST_Sequence && parent.tail_node() !== node) return;
721             if (parent instanceof AST_SimpleStatement) return;
722             if (parent instanceof AST_Unary && !unary_side_effects[parent.operator]) return;
723             d.direct_access = true;
724             if (d.fixed) d.fixed.direct_access = true;
725         }
726
727         function mark_assignment_to_arguments(node) {
728             if (!(node instanceof AST_Sub)) return;
729             var expr = node.expression;
730             if (!(expr instanceof AST_SymbolRef)) return;
731             var def = expr.definition();
732             if (!is_arguments(def)) return;
733             var key = node.property;
734             if (key.is_constant()) key = key.value;
735             if (!(key instanceof AST_Node) && !RE_POSITIVE_INTEGER.test(key)) return;
736             def.reassigned++;
737             (key instanceof AST_Node ? def.scope.argnames : [ def.scope.argnames[key] ]).forEach(function(argname) {
738                 if (argname instanceof AST_SymbolFunarg) argname.definition().fixed = false;
739             });
740         }
741
742         function scan_declaration(tw, compressor, lhs, fixed, visit) {
743             var scanner = new TreeWalker(function(node) {
744                 if (node instanceof AST_DefaultValue) {
745                     reset_flags(node);
746                     push(tw);
747                     node.value.walk(tw);
748                     pop(tw);
749                     var save = fixed;
750                     if (save) fixed = function() {
751                         var value = save();
752                         var ev;
753                         if (is_undefined(value, compressor)
754                             || (ev = fuzzy_eval(compressor, value, true)) === undefined) {
755                             return make_sequence(node, [ value, node.value ]);
756                         }
757                         return ev instanceof AST_Node ? node : value;
758                     };
759                     node.name.walk(scanner);
760                     fixed = save;
761                     return true;
762                 }
763                 if (node instanceof AST_DestructuredArray) {
764                     reset_flags(node);
765                     var save = fixed;
766                     node.elements.forEach(function(node, index) {
767                         if (node instanceof AST_Hole) return reset_flags(node);
768                         if (save) fixed = function() {
769                             return make_node(AST_Sub, node, {
770                                 expression: save(),
771                                 property: make_node(AST_Number, node, { value: index }),
772                             });
773                         };
774                         node.walk(scanner);
775                     });
776                     if (node.rest) {
777                         var fixed_node;
778                         if (save) fixed = compressor.option("rests") && function() {
779                             var value = save();
780                             if (!(value instanceof AST_Array)) return node;
781                             if (!fixed_node) fixed_node = make_node(AST_Array, node);
782                             fixed_node.elements = value.elements.slice(node.elements.length);
783                             return fixed_node;
784                         };
785                         node.rest.walk(scanner);
786                     }
787                     fixed = save;
788                     return true;
789                 }
790                 if (node instanceof AST_DestructuredObject) {
791                     reset_flags(node);
792                     var save = fixed;
793                     node.properties.forEach(function(node) {
794                         reset_flags(node);
795                         if (node.key instanceof AST_Node) {
796                             push(tw);
797                             node.key.walk(tw);
798                             pop(tw);
799                         }
800                         if (save) fixed = function() {
801                             var key = node.key;
802                             var type = AST_Sub;
803                             if (typeof key == "string") {
804                                 if (is_identifier_string(key)) {
805                                     type = AST_Dot;
806                                 } else {
807                                     key = make_node_from_constant(key, node);
808                                 }
809                             }
810                             return make_node(type, node, {
811                                 expression: save(),
812                                 property: key
813                             });
814                         };
815                         node.value.walk(scanner);
816                     });
817                     if (node.rest) {
818                         fixed = false;
819                         node.rest.walk(scanner);
820                     }
821                     fixed = save;
822                     return true;
823                 }
824                 visit(node, fixed, function() {
825                     var save_len = tw.stack.length;
826                     for (var i = 0, len = scanner.stack.length - 1; i < len; i++) {
827                         tw.stack.push(scanner.stack[i]);
828                     }
829                     node.walk(tw);
830                     tw.stack.length = save_len;
831                 });
832                 return true;
833             });
834             lhs.walk(scanner);
835         }
836
837         function reduce_iife(tw, descend, compressor) {
838             var fn = this;
839             fn.inlined = false;
840             var iife = tw.parent();
841             var hit = is_async(fn) || is_generator(fn);
842             var aborts = false;
843             fn.walk(new TreeWalker(function(node) {
844                 if (hit) return aborts = true;
845                 if (node instanceof AST_Return) return hit = true;
846                 if (node instanceof AST_Scope && node !== fn) return true;
847             }));
848             if (aborts) push(tw);
849             reset_variables(tw, compressor, fn);
850             // Virtually turn IIFE parameters into variable definitions:
851             //   (function(a,b) {...})(c,d) ---> (function() {var a=c,b=d; ...})()
852             // So existing transformation rules can work on them.
853             var safe = !fn.uses_arguments || tw.has_directive("use strict");
854             fn.argnames.forEach(function(argname, i) {
855                 var value = iife.args[i];
856                 scan_declaration(tw, compressor, argname, function() {
857                     var j = fn.argnames.indexOf(argname);
858                     var arg = j < 0 ? value : iife.args[j];
859                     if (arg instanceof AST_Sequence && arg.expressions.length < 2) arg = arg.expressions[0];
860                     return arg || make_node(AST_Undefined, iife);
861                 }, visit);
862             });
863             var rest = fn.rest, fixed_node;
864             if (rest) scan_declaration(tw, compressor, rest, compressor.option("rests") && function() {
865                 if (fn.rest !== rest) return rest;
866                 if (!fixed_node) fixed_node = make_node(AST_Array, fn);
867                 fixed_node.elements = iife.args.slice(fn.argnames.length);
868                 return fixed_node;
869             }, visit);
870             walk_lambda(fn, tw);
871             var safe_ids = tw.safe_ids;
872             pop_scope(tw, fn);
873             if (!aborts) tw.safe_ids = safe_ids;
874             return true;
875
876             function visit(node, fixed) {
877                 var d = node.definition();
878                 if (fixed && safe && d.fixed === undefined) {
879                     mark(tw, d);
880                     tw.loop_ids[d.id] = tw.in_loop;
881                     d.fixed = fixed;
882                     d.fixed.assigns = [ node ];
883                 } else {
884                     d.fixed = false;
885                 }
886             }
887         }
888
889         def(AST_Assign, function(tw, descend, compressor) {
890             var node = this;
891             var left = node.left;
892             var right = node.right;
893             var ld = left instanceof AST_SymbolRef && left.definition();
894             var scan = ld || left instanceof AST_Destructured;
895             switch (node.operator) {
896               case "=":
897                 if (left.equivalent_to(right) && !left.has_side_effects(compressor)) {
898                     right.walk(tw);
899                     walk_prop(left);
900                     node.__drop = true;
901                     return true;
902                 }
903                 if (ld && right instanceof AST_LambdaExpression) {
904                     walk_assign();
905                     right.parent_scope.resolve().fn_defs.push(right);
906                     right.safe_ids = null;
907                     if (!ld.fixed || !node.write_only) mark_fn_def(tw, ld, right);
908                     return true;
909                 }
910                 if (scan) {
911                     right.walk(tw);
912                     walk_assign();
913                     return true;
914                 }
915                 mark_assignment_to_arguments(left);
916                 return;
917               case "&&=":
918               case "||=":
919               case "??=":
920                 var lazy = true;
921               default:
922                 if (!scan) {
923                     mark_assignment_to_arguments(left);
924                     return walk_lazy();
925                 }
926                 ld.assignments++;
927                 var fixed = ld.fixed;
928                 if (is_modified(compressor, tw, node, node, 0)) {
929                     ld.fixed = false;
930                     return walk_lazy();
931                 }
932                 var safe = safe_to_read(tw, ld);
933                 if (lazy) push(tw);
934                 right.walk(tw);
935                 if (lazy) pop(tw);
936                 if (safe && !left.in_arg && safe_to_assign(tw, ld)) {
937                     push_ref(ld, left);
938                     mark(tw, ld);
939                     if (ld.single_use) ld.single_use = false;
940                     left.fixed = ld.fixed = function() {
941                         return make_node(AST_Binary, node, {
942                             operator: node.operator.slice(0, -1),
943                             left: make_ref(left, fixed),
944                             right: node.right,
945                         });
946                     };
947                     left.fixed.assigns = !fixed || !fixed.assigns ? [] : fixed.assigns.slice();
948                     left.fixed.assigns.push(node);
949                 } else {
950                     left.walk(tw);
951                     ld.fixed = false;
952                 }
953                 return true;
954             }
955
956             function walk_prop(lhs) {
957                 if (lhs instanceof AST_Dot) {
958                     walk_prop(lhs.expression);
959                 } else if (lhs instanceof AST_Sub) {
960                     walk_prop(lhs.expression);
961                     lhs.property.walk(tw);
962                 } else if (lhs instanceof AST_SymbolRef) {
963                     var d = lhs.definition();
964                     push_ref(d, lhs);
965                     if (d.fixed) {
966                         lhs.fixed = d.fixed;
967                         if (lhs.fixed.assigns) {
968                             lhs.fixed.assigns.push(node);
969                         } else {
970                             lhs.fixed.assigns = [ node ];
971                         }
972                     }
973                 } else {
974                     lhs.walk(tw);
975                 }
976             }
977
978             function walk_assign() {
979                 var recursive = ld && recursive_ref(tw, ld);
980                 var modified = is_modified(compressor, tw, node, right, 0, is_immutable(right), recursive);
981                 scan_declaration(tw, compressor, left, function() {
982                     return node.right;
983                 }, function(sym, fixed, walk) {
984                     if (!(sym instanceof AST_SymbolRef)) {
985                         mark_assignment_to_arguments(sym);
986                         walk();
987                         return;
988                     }
989                     var d = sym.definition();
990                     d.assignments++;
991                     if (!fixed || sym.in_arg || !safe_to_assign(tw, d)) {
992                         walk();
993                         d.fixed = false;
994                     } else if (modified) {
995                         walk();
996                         d.fixed = 0;
997                     } else {
998                         push_ref(d, sym);
999                         mark(tw, d);
1000                         if (left instanceof AST_Destructured
1001                             || d.orig.length == 1 && d.orig[0] instanceof AST_SymbolDefun) {
1002                             d.single_use = false;
1003                         }
1004                         tw.loop_ids[d.id] = tw.in_loop;
1005                         sym.fixed = d.fixed = fixed;
1006                         sym.fixed.assigns = [ node ];
1007                         mark_escaped(tw, d, sym.scope, node, right, 0, 1);
1008                     }
1009                 });
1010             }
1011
1012             function walk_lazy() {
1013                 if (!lazy) return;
1014                 left.walk(tw);
1015                 push(tw);
1016                 right.walk(tw);
1017                 pop(tw);
1018                 return true;
1019             }
1020         });
1021         def(AST_Binary, function(tw) {
1022             if (!lazy_op[this.operator]) return;
1023             this.left.walk(tw);
1024             push(tw);
1025             this.right.walk(tw);
1026             pop(tw);
1027             return true;
1028         });
1029         def(AST_BlockScope, function(tw, descend, compressor) {
1030             reset_block_variables(tw, compressor, this);
1031         });
1032         def(AST_Call, function(tw, descend) {
1033             var node = this;
1034             var exp = node.expression;
1035             if (exp instanceof AST_LambdaExpression) {
1036                 var iife = is_iife_single(node);
1037                 node.args.forEach(function(arg) {
1038                     arg.walk(tw);
1039                     if (arg instanceof AST_Spread) iife = false;
1040                 });
1041                 if (iife) exp.reduce_vars = reduce_iife;
1042                 exp.walk(tw);
1043                 if (iife) delete exp.reduce_vars;
1044                 return true;
1045             }
1046             if (node.TYPE == "Call" && tw.in_boolean_context()) {
1047                 if (exp instanceof AST_SymbolRef) {
1048                     exp.definition().bool_fn++;
1049                 } else if (exp instanceof AST_Assign && exp.operator == "=" && exp.left instanceof AST_SymbolRef) {
1050                     exp.left.definition().bool_fn++;
1051                 }
1052             }
1053             exp.walk(tw);
1054             var optional = node.optional;
1055             if (optional) push(tw);
1056             node.args.forEach(function(arg) {
1057                 arg.walk(tw);
1058             });
1059             if (optional) pop(tw);
1060             var fixed = exp instanceof AST_SymbolRef && exp.fixed_value();
1061             if (fixed instanceof AST_Lambda) {
1062                 mark_fn_def(tw, exp.definition(), fixed);
1063             } else {
1064                 tw.find_parent(AST_Scope).may_call_this();
1065             }
1066             return true;
1067         });
1068         def(AST_Class, function(tw, descend, compressor) {
1069             var node = this;
1070             reset_block_variables(tw, compressor, node);
1071             if (node.extends) node.extends.walk(tw);
1072             var props = node.properties.filter(function(prop) {
1073                 reset_flags(prop);
1074                 if (prop.key instanceof AST_Node) prop.key.walk(tw);
1075                 return prop.value;
1076             });
1077             if (node.name) {
1078                 var d = node.name.definition();
1079                 var parent = tw.parent();
1080                 if (parent instanceof AST_ExportDeclaration || parent instanceof AST_ExportDefault) d.single_use = false;
1081                 if (safe_to_assign(tw, d, true)) {
1082                     mark(tw, d);
1083                     tw.loop_ids[d.id] = tw.in_loop;
1084                     d.fixed = function() {
1085                         return node;
1086                     };
1087                     d.fixed.assigns = [ node ];
1088                     if (!is_safe_lexical(d)) d.single_use = false;
1089                 } else {
1090                     d.fixed = false;
1091                 }
1092             }
1093             props.forEach(function(prop) {
1094                 if (!prop.static || prop instanceof AST_ClassField && prop.value.contains_this()) {
1095                     push(tw);
1096                     prop.value.walk(tw);
1097                     pop(tw);
1098                 } else {
1099                     prop.value.walk(tw);
1100                 }
1101             });
1102             return true;
1103         });
1104         def(AST_Conditional, function(tw) {
1105             this.condition.walk(tw);
1106             push(tw);
1107             this.consequent.walk(tw);
1108             pop(tw);
1109             push(tw);
1110             this.alternative.walk(tw);
1111             pop(tw);
1112             return true;
1113         });
1114         def(AST_DefaultValue, function(tw) {
1115             this.name.walk(tw);
1116             push(tw);
1117             this.value.walk(tw);
1118             pop(tw);
1119             return true;
1120         });
1121         def(AST_Do, function(tw) {
1122             var save_loop = tw.in_loop;
1123             tw.in_loop = this;
1124             push(tw);
1125             this.body.walk(tw);
1126             if (has_loop_control(this, tw.parent())) {
1127                 pop(tw);
1128                 push(tw);
1129             }
1130             this.condition.walk(tw);
1131             pop(tw);
1132             tw.in_loop = save_loop;
1133             return true;
1134         });
1135         def(AST_For, function(tw, descend, compressor) {
1136             var node = this;
1137             reset_block_variables(tw, compressor, node);
1138             if (node.init) node.init.walk(tw);
1139             var save_loop = tw.in_loop;
1140             tw.in_loop = node;
1141             push(tw);
1142             if (node.condition) node.condition.walk(tw);
1143             node.body.walk(tw);
1144             if (node.step) {
1145                 if (has_loop_control(node, tw.parent())) {
1146                     pop(tw);
1147                     push(tw);
1148                 }
1149                 node.step.walk(tw);
1150             }
1151             pop(tw);
1152             tw.in_loop = save_loop;
1153             return true;
1154         });
1155         def(AST_ForEnumeration, function(tw, descend, compressor) {
1156             var node = this;
1157             reset_block_variables(tw, compressor, node);
1158             node.object.walk(tw);
1159             var save_loop = tw.in_loop;
1160             tw.in_loop = node;
1161             push(tw);
1162             var init = node.init;
1163             if (init instanceof AST_Definitions) {
1164                 init.definitions[0].name.mark_symbol(function(node) {
1165                     if (node instanceof AST_SymbolDeclaration) {
1166                         var def = node.definition();
1167                         def.assignments++;
1168                         def.fixed = false;
1169                     }
1170                 }, tw);
1171             } else if (init instanceof AST_Destructured || init instanceof AST_SymbolRef) {
1172                 init.mark_symbol(function(node) {
1173                     if (node instanceof AST_SymbolRef) {
1174                         var def = node.definition();
1175                         push_ref(def, node);
1176                         def.assignments++;
1177                         if (!node.is_immutable()) def.fixed = false;
1178                     }
1179                 }, tw);
1180             } else {
1181                 init.walk(tw);
1182             }
1183             node.body.walk(tw);
1184             pop(tw);
1185             tw.in_loop = save_loop;
1186             return true;
1187         });
1188         def(AST_If, function(tw) {
1189             this.condition.walk(tw);
1190             push(tw);
1191             this.body.walk(tw);
1192             pop(tw);
1193             if (this.alternative) {
1194                 push(tw);
1195                 this.alternative.walk(tw);
1196                 pop(tw);
1197             }
1198             return true;
1199         });
1200         def(AST_LabeledStatement, function(tw) {
1201             push(tw);
1202             this.body.walk(tw);
1203             pop(tw);
1204             return true;
1205         });
1206         def(AST_Lambda, function(tw, descend, compressor) {
1207             var fn = this;
1208             if (HOP(fn, "safe_ids") && fn.safe_ids !== tw.safe_ids) return true;
1209             if (!push_uniq(tw.fn_visited, fn)) return true;
1210             fn.inlined = false;
1211             push(tw);
1212             reset_variables(tw, compressor, fn);
1213             descend();
1214             pop_scope(tw, fn);
1215             if (fn.name) mark_escaped(tw, fn.name.definition(), fn, fn.name, fn, 0, 1);
1216             return true;
1217         });
1218         def(AST_LambdaDefinition, function(tw, descend, compressor) {
1219             var fn = this;
1220             var def = fn.name.definition();
1221             var parent = tw.parent();
1222             if (parent instanceof AST_ExportDeclaration || parent instanceof AST_ExportDefault) def.single_use = false;
1223             if (HOP(fn, "safe_ids") && fn.safe_ids !== tw.safe_ids) return true;
1224             if (!push_uniq(tw.fn_visited, fn)) return true;
1225             fn.inlined = false;
1226             push(tw);
1227             reset_variables(tw, compressor, fn);
1228             descend();
1229             pop_scope(tw, fn);
1230             return true;
1231         });
1232         def(AST_Sub, function(tw) {
1233             if (!this.optional) return;
1234             this.expression.walk(tw);
1235             push(tw);
1236             this.property.walk(tw);
1237             pop(tw);
1238             return true;
1239         });
1240         def(AST_Switch, function(tw, descend, compressor) {
1241             var node = this;
1242             reset_block_variables(tw, compressor, node);
1243             node.expression.walk(tw);
1244             var first = true;
1245             node.body.forEach(function(branch) {
1246                 if (branch instanceof AST_Default) return;
1247                 branch.expression.walk(tw);
1248                 if (first) {
1249                     first = false;
1250                     push(tw);
1251                 }
1252             })
1253             if (!first) pop(tw);
1254             walk_body(node, tw);
1255             return true;
1256         });
1257         def(AST_SwitchBranch, function(tw) {
1258             push(tw);
1259             walk_body(this, tw);
1260             pop(tw);
1261             return true;
1262         });
1263         def(AST_SymbolCatch, function() {
1264             this.definition().fixed = false;
1265         });
1266         def(AST_SymbolImport, function() {
1267             this.definition().fixed = false;
1268         });
1269         def(AST_SymbolRef, function(tw, descend, compressor) {
1270             var d = this.definition();
1271             push_ref(d, this);
1272             if (d.references.length == 1 && !d.fixed && d.orig[0] instanceof AST_SymbolDefun) {
1273                 tw.loop_ids[d.id] = tw.in_loop;
1274             }
1275             var recursive = recursive_ref(tw, d);
1276             if (recursive) recursive.enclosed.forEach(function(def) {
1277                 if (d === def) return;
1278                 if (def.scope.resolve() === recursive) return;
1279                 var assigns = def.fixed && def.fixed.assigns;
1280                 if (!assigns) return;
1281                 if (assigns[assigns.length - 1] instanceof AST_VarDef) return;
1282                 var safe = tw.safe_ids[def.id];
1283                 if (!safe) return;
1284                 safe.assign = true;
1285             });
1286             if (d.fixed === false || d.fixed === 0) {
1287                 var redef = d.redefined();
1288                 if (redef && cross_scope(d.scope, this.scope)) redef.single_use = false;
1289             } else if (d.fixed === undefined || !safe_to_read(tw, d)) {
1290                 d.fixed = false;
1291             } else if (d.fixed) {
1292                 if (this.in_arg && d.orig[0] instanceof AST_SymbolLambda) this.fixed = d.scope;
1293                 var value = this.fixed_value();
1294                 if (recursive) {
1295                     d.recursive_refs++;
1296                 } else if (value && ref_once(compressor, d)) {
1297                     d.in_loop = tw.loop_ids[d.id] !== tw.in_loop;
1298                     d.single_use = is_lambda(value)
1299                             && !value.pinned()
1300                             && (!d.in_loop || tw.parent() instanceof AST_Call)
1301                         || !d.in_loop
1302                             && d.scope === this.scope.resolve()
1303                             && value.is_constant_expression();
1304                 } else {
1305                     d.single_use = false;
1306                 }
1307                 if (is_modified(compressor, tw, this, value, 0, is_immutable(value), recursive)) {
1308                     if (d.single_use) {
1309                         d.single_use = "m";
1310                     } else {
1311                         d.fixed = 0;
1312                     }
1313                 }
1314                 if (d.fixed && tw.loop_ids[d.id] !== tw.in_loop) d.cross_loop = true;
1315                 mark_escaped(tw, d, this.scope, this, value, 0, 1);
1316             }
1317             if (!this.fixed) this.fixed = d.fixed;
1318             var parent;
1319             if (value instanceof AST_Lambda
1320                 && !((parent = tw.parent()) instanceof AST_Call && parent.expression === this)) {
1321                 mark_fn_def(tw, d, value);
1322             }
1323         });
1324         def(AST_Template, function(tw, descend) {
1325             var node = this;
1326             var tag = node.tag;
1327             if (!tag) return;
1328             if (tag instanceof AST_LambdaExpression) {
1329                 node.expressions.forEach(function(exp) {
1330                     exp.walk(tw);
1331                 });
1332                 tag.walk(tw);
1333                 return true;
1334             }
1335             tag.walk(tw);
1336             node.expressions.forEach(function(exp) {
1337                 exp.walk(tw);
1338             });
1339             var fixed = tag instanceof AST_SymbolRef && tag.fixed_value();
1340             if (fixed instanceof AST_Lambda) {
1341                 mark_fn_def(tw, tag.definition(), fixed);
1342             } else {
1343                 tw.find_parent(AST_Scope).may_call_this();
1344             }
1345             return true;
1346         });
1347         def(AST_Toplevel, function(tw, descend, compressor) {
1348             var node = this;
1349             node.globals.each(function(def) {
1350                 reset_def(tw, compressor, def);
1351             });
1352             push(tw);
1353             reset_variables(tw, compressor, node);
1354             descend();
1355             pop_scope(tw, node);
1356             return true;
1357         });
1358         def(AST_Try, function(tw, descend, compressor) {
1359             var node = this;
1360             reset_block_variables(tw, compressor, node);
1361             push(tw);
1362             walk_body(node, tw);
1363             pop(tw);
1364             if (node.bcatch) {
1365                 push(tw);
1366                 node.bcatch.walk(tw);
1367                 pop(tw);
1368             }
1369             if (node.bfinally) node.bfinally.walk(tw);
1370             return true;
1371         });
1372         def(AST_Unary, function(tw, descend) {
1373             var node = this;
1374             if (!UNARY_POSTFIX[node.operator]) return;
1375             var exp = node.expression;
1376             if (!(exp instanceof AST_SymbolRef)) {
1377                 mark_assignment_to_arguments(exp);
1378                 return;
1379             }
1380             var d = exp.definition();
1381             d.assignments++;
1382             var fixed = d.fixed;
1383             if (safe_to_read(tw, d) && !exp.in_arg && safe_to_assign(tw, d)) {
1384                 push_ref(d, exp);
1385                 mark(tw, d);
1386                 if (d.single_use) d.single_use = false;
1387                 d.fixed = function() {
1388                     return make_node(AST_Binary, node, {
1389                         operator: node.operator.slice(0, -1),
1390                         left: make_node(AST_UnaryPrefix, node, {
1391                             operator: "+",
1392                             expression: make_ref(exp, fixed)
1393                         }),
1394                         right: make_node(AST_Number, node, {
1395                             value: 1
1396                         })
1397                     });
1398                 };
1399                 d.fixed.assigns = fixed && fixed.assigns ? fixed.assigns.slice() : [];
1400                 d.fixed.assigns.push(node);
1401                 if (node instanceof AST_UnaryPrefix) {
1402                     exp.fixed = d.fixed;
1403                 } else {
1404                     exp.fixed = function() {
1405                         return make_node(AST_UnaryPrefix, node, {
1406                             operator: "+",
1407                             expression: make_ref(exp, fixed)
1408                         });
1409                     };
1410                     exp.fixed.assigns = fixed && fixed.assigns;
1411                 }
1412             } else {
1413                 exp.walk(tw);
1414                 d.fixed = false;
1415             }
1416             return true;
1417         });
1418         def(AST_VarDef, function(tw, descend, compressor) {
1419             var node = this;
1420             var value = node.value;
1421             if (value instanceof AST_LambdaExpression && node.name instanceof AST_SymbolDeclaration) {
1422                 walk_defn();
1423                 value.parent_scope.resolve().fn_defs.push(value);
1424                 value.safe_ids = null;
1425                 var ld = node.name.definition();
1426                 if (!ld.fixed) mark_fn_def(tw, ld, value);
1427             } else if (value) {
1428                 value.walk(tw);
1429                 walk_defn();
1430             } else if (tw.parent() instanceof AST_Let) {
1431                 walk_defn();
1432             }
1433             return true;
1434
1435             function walk_defn() {
1436                 scan_declaration(tw, compressor, node.name, function() {
1437                     return node.value || make_node(AST_Undefined, node);
1438                 }, function(name, fixed) {
1439                     var d = name.definition();
1440                     if (fixed && safe_to_assign(tw, d, true)) {
1441                         mark(tw, d);
1442                         tw.loop_ids[d.id] = tw.in_loop;
1443                         d.fixed = fixed;
1444                         d.fixed.assigns = [ node ];
1445                         if (name instanceof AST_SymbolConst && d.redefined()
1446                             || !(can_drop_symbol(name) || is_safe_lexical(d))) {
1447                             d.single_use = false;
1448                         }
1449                     } else {
1450                         d.fixed = false;
1451                     }
1452                 });
1453             }
1454         });
1455         def(AST_While, function(tw, descend) {
1456             var save_loop = tw.in_loop;
1457             tw.in_loop = this;
1458             push(tw);
1459             descend();
1460             pop(tw);
1461             tw.in_loop = save_loop;
1462             return true;
1463         });
1464     })(function(node, func) {
1465         node.DEFMETHOD("reduce_vars", func);
1466     });
1467
1468     function reset_flags(node) {
1469         node._squeezed = false;
1470         node._optimized = false;
1471         delete node.fixed;
1472         if (node instanceof AST_Scope) delete node._var_names;
1473     }
1474
1475     AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) {
1476         var tw = new TreeWalker(compressor.option("reduce_vars") ? function(node, descend) {
1477             reset_flags(node);
1478             return node.reduce_vars(tw, descend, compressor);
1479         } : reset_flags);
1480         // Flow control for visiting lambda definitions
1481         tw.fn_scanning = null;
1482         tw.fn_visited = [];
1483         // Record the loop body in which `AST_SymbolDeclaration` is first encountered
1484         tw.in_loop = null;
1485         tw.loop_ids = Object.create(null);
1486         // Stack of look-up tables to keep track of whether a `SymbolDef` has been
1487         // properly assigned before use:
1488         // - `push()` & `pop()` when visiting conditional branches
1489         // - backup & restore via `save_ids` when visiting out-of-order sections
1490         tw.safe_ids = Object.create(null);
1491         this.walk(tw);
1492     });
1493
1494     AST_Symbol.DEFMETHOD("fixed_value", function() {
1495         var fixed = this.definition().fixed;
1496         if (fixed) {
1497             if (this.fixed) fixed = this.fixed;
1498             return fixed instanceof AST_Node ? fixed : fixed();
1499         }
1500         fixed = fixed === 0 && this.fixed;
1501         if (!fixed) return fixed;
1502         var value = fixed instanceof AST_Node ? fixed : fixed();
1503         return value.is_constant() && value;
1504     });
1505
1506     AST_SymbolRef.DEFMETHOD("is_immutable", function() {
1507         var def = this.redef || this.definition();
1508         return (this.in_arg || def.orig.length == 1) && def.orig[0] instanceof AST_SymbolLambda;
1509     });
1510
1511     AST_Node.DEFMETHOD("convert_symbol", noop);
1512     function convert_destructured(type, process) {
1513         return this.transform(new TreeTransformer(function(node, descend) {
1514             if (node instanceof AST_DefaultValue) {
1515                 node = node.clone();
1516                 node.name = node.name.transform(this);
1517                 return node;
1518             }
1519             if (node instanceof AST_Destructured) {
1520                 node = node.clone();
1521                 descend(node, this);
1522                 return node;
1523             }
1524             if (node instanceof AST_DestructuredKeyVal) {
1525                 node = node.clone();
1526                 node.value = node.value.transform(this);
1527                 return node;
1528             }
1529             return node.convert_symbol(type, process);
1530         }));
1531     }
1532     AST_DefaultValue.DEFMETHOD("convert_symbol", convert_destructured);
1533     AST_Destructured.DEFMETHOD("convert_symbol", convert_destructured);
1534     function convert_symbol(type, process) {
1535         var node = make_node(type, this, this);
1536         process(node, this);
1537         return node;
1538     }
1539     AST_SymbolDeclaration.DEFMETHOD("convert_symbol", convert_symbol);
1540     AST_SymbolRef.DEFMETHOD("convert_symbol", convert_symbol);
1541
1542     function mark_destructured(process, tw) {
1543         var marker = new TreeWalker(function(node) {
1544             if (node instanceof AST_DefaultValue) {
1545                 node.value.walk(tw);
1546                 node.name.walk(marker);
1547                 return true;
1548             }
1549             if (node instanceof AST_DestructuredKeyVal) {
1550                 if (node.key instanceof AST_Node) node.key.walk(tw);
1551                 node.value.walk(marker);
1552                 return true;
1553             }
1554             return process(node);
1555         });
1556         this.walk(marker);
1557     }
1558     AST_DefaultValue.DEFMETHOD("mark_symbol", mark_destructured);
1559     AST_Destructured.DEFMETHOD("mark_symbol", mark_destructured);
1560     function mark_symbol(process) {
1561         return process(this);
1562     }
1563     AST_SymbolDeclaration.DEFMETHOD("mark_symbol", mark_symbol);
1564     AST_SymbolRef.DEFMETHOD("mark_symbol", mark_symbol);
1565
1566     AST_Node.DEFMETHOD("match_symbol", function(predicate) {
1567         return predicate(this);
1568     });
1569     function match_destructured(predicate, ignore_side_effects) {
1570         var found = false;
1571         var tw = new TreeWalker(function(node) {
1572             if (found) return true;
1573             if (node instanceof AST_DefaultValue) {
1574                 if (!ignore_side_effects) return found = true;
1575                 node.name.walk(tw);
1576                 return true;
1577             }
1578             if (node instanceof AST_DestructuredKeyVal) {
1579                 if (!ignore_side_effects && node.key instanceof AST_Node) return found = true;
1580                 node.value.walk(tw);
1581                 return true;
1582             }
1583             if (predicate(node)) return found = true;
1584         });
1585         this.walk(tw);
1586         return found;
1587     }
1588     AST_DefaultValue.DEFMETHOD("match_symbol", match_destructured);
1589     AST_Destructured.DEFMETHOD("match_symbol", match_destructured);
1590
1591     function in_async_generator(scope) {
1592         return scope instanceof AST_AsyncGeneratorDefun || scope instanceof AST_AsyncGeneratorFunction;
1593     }
1594
1595     function find_scope(compressor) {
1596         var level = 0, node;
1597         while (node = compressor.parent(level++)) {
1598             if (node.variables) return node;
1599         }
1600     }
1601
1602     var identifier_atom = makePredicate("Infinity NaN undefined");
1603     function is_lhs_read_only(lhs, compressor) {
1604         if (lhs instanceof AST_ObjectIdentity) return true;
1605         if (lhs instanceof AST_PropAccess) {
1606             if (lhs.property === "__proto__") return true;
1607             lhs = lhs.expression;
1608             if (lhs instanceof AST_SymbolRef) {
1609                 if (lhs.is_immutable()) return false;
1610                 lhs = lhs.fixed_value();
1611             }
1612             if (!lhs) return true;
1613             if (lhs.tail_node().is_constant()) return true;
1614             return is_lhs_read_only(lhs, compressor);
1615         }
1616         if (lhs instanceof AST_SymbolRef) {
1617             if (lhs.is_immutable()) return true;
1618             var def = lhs.definition();
1619             return compressor.exposed(def) && identifier_atom[def.name];
1620         }
1621         return false;
1622     }
1623
1624     function make_node(ctor, orig, props) {
1625         if (!props) props = {};
1626         if (orig) {
1627             if (!props.start) props.start = orig.start;
1628             if (!props.end) props.end = orig.end;
1629         }
1630         return new ctor(props);
1631     }
1632
1633     function make_sequence(orig, expressions) {
1634         if (expressions.length == 1) return expressions[0];
1635         return make_node(AST_Sequence, orig, {
1636             expressions: expressions.reduce(merge_sequence, [])
1637         });
1638     }
1639
1640     function make_node_from_constant(val, orig) {
1641         switch (typeof val) {
1642           case "string":
1643             return make_node(AST_String, orig, {
1644                 value: val
1645             });
1646           case "number":
1647             if (isNaN(val)) return make_node(AST_NaN, orig);
1648             if (isFinite(val)) {
1649                 return 1 / val < 0 ? make_node(AST_UnaryPrefix, orig, {
1650                     operator: "-",
1651                     expression: make_node(AST_Number, orig, { value: -val })
1652                 }) : make_node(AST_Number, orig, { value: val });
1653             }
1654             return val < 0 ? make_node(AST_UnaryPrefix, orig, {
1655                 operator: "-",
1656                 expression: make_node(AST_Infinity, orig)
1657             }) : make_node(AST_Infinity, orig);
1658           case "boolean":
1659             return make_node(val ? AST_True : AST_False, orig);
1660           case "undefined":
1661             return make_node(AST_Undefined, orig);
1662           default:
1663             if (val === null) {
1664                 return make_node(AST_Null, orig, { value: null });
1665             }
1666             if (val instanceof RegExp) {
1667                 return make_node(AST_RegExp, orig, { value: val });
1668             }
1669             throw new Error(string_template("Can't handle constant of type: {type}", {
1670                 type: typeof val
1671             }));
1672         }
1673     }
1674
1675     function needs_unbinding(compressor, val) {
1676         return val instanceof AST_PropAccess
1677             || is_undeclared_ref(val) && val.name == "eval";
1678     }
1679
1680     // we shouldn't compress (1,func)(something) to
1681     // func(something) because that changes the meaning of
1682     // the func (becomes lexical instead of global).
1683     function maintain_this_binding(compressor, parent, orig, val) {
1684         var wrap = false;
1685         if (parent.TYPE == "Call") {
1686             wrap = parent.expression === orig && needs_unbinding(compressor, val);
1687         } else if (parent instanceof AST_Template) {
1688             wrap = parent.tag === orig && needs_unbinding(compressor, val);
1689         } else if (parent instanceof AST_UnaryPrefix) {
1690             wrap = parent.operator == "delete"
1691                 || parent.operator == "typeof" && is_undeclared_ref(val);
1692         }
1693         return wrap ? make_sequence(orig, [ make_node(AST_Number, orig, { value: 0 }), val ]) : val;
1694     }
1695
1696     function merge_sequence(array, node) {
1697         if (node instanceof AST_Sequence) {
1698             array.push.apply(array, node.expressions);
1699         } else {
1700             array.push(node);
1701         }
1702         return array;
1703     }
1704
1705     function is_lexical_definition(stat) {
1706         return stat instanceof AST_Const || stat instanceof AST_DefClass || stat instanceof AST_Let;
1707     }
1708
1709     function safe_to_trim(stat) {
1710         if (stat instanceof AST_LambdaDefinition) {
1711             var def = stat.name.definition();
1712             var scope = stat.name.scope;
1713             return def.scope === scope || all(def.references, function(ref) {
1714                 var s = ref.scope;
1715                 do {
1716                     if (s === scope) return true;
1717                 } while (s = s.parent_scope);
1718             });
1719         }
1720         return !is_lexical_definition(stat);
1721     }
1722
1723     function as_statement_array(thing) {
1724         if (thing === null) return [];
1725         if (thing instanceof AST_BlockStatement) return all(thing.body, safe_to_trim) ? thing.body : [ thing ];
1726         if (thing instanceof AST_EmptyStatement) return [];
1727         if (is_statement(thing)) return [ thing ];
1728         throw new Error("Can't convert thing to statement array");
1729     }
1730
1731     function is_empty(thing) {
1732         if (thing === null) return true;
1733         if (thing instanceof AST_EmptyStatement) return true;
1734         if (thing instanceof AST_BlockStatement) return thing.body.length == 0;
1735         return false;
1736     }
1737
1738     function has_declarations_only(block) {
1739         return all(block.body, function(stat) {
1740             return is_empty(stat)
1741                 || stat instanceof AST_Defun
1742                 || stat instanceof AST_Var && declarations_only(stat);
1743         });
1744     }
1745
1746     function loop_body(x) {
1747         if (x instanceof AST_IterationStatement) {
1748             return x.body instanceof AST_BlockStatement ? x.body : x;
1749         }
1750         return x;
1751     }
1752
1753     function is_iife_call(node) {
1754         if (node.TYPE != "Call") return false;
1755         do {
1756             node = node.expression;
1757         } while (node instanceof AST_PropAccess);
1758         return node instanceof AST_LambdaExpression ? !is_arrow(node) : is_iife_call(node);
1759     }
1760
1761     function is_iife_single(call) {
1762         var exp = call.expression;
1763         if (exp.name) return false;
1764         if (!(call instanceof AST_New)) return true;
1765         var found = false;
1766         exp.walk(new TreeWalker(function(node) {
1767             if (found) return true;
1768             if (node instanceof AST_NewTarget) return found = true;
1769             if (node instanceof AST_Scope && node !== exp) return true;
1770         }));
1771         return !found;
1772     }
1773
1774     function is_undeclared_ref(node) {
1775         return node instanceof AST_SymbolRef && node.definition().undeclared;
1776     }
1777
1778     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");
1779     AST_SymbolRef.DEFMETHOD("is_declared", function(compressor) {
1780         return this.defined
1781             || !this.definition().undeclared
1782             || compressor.option("unsafe") && global_names[this.name];
1783     });
1784
1785     function declarations_only(node) {
1786         return all(node.definitions, function(var_def) {
1787             return !var_def.value;
1788         });
1789     }
1790
1791     function is_declaration(stat, lexical) {
1792         if (stat instanceof AST_DefClass) return lexical && !stat.extends && all(stat.properties, function(prop) {
1793             if (prop.key instanceof AST_Node) return false;
1794             if (prop instanceof AST_ClassField && prop.static && prop.value) return false;
1795             return true;
1796         });
1797         if (stat instanceof AST_Definitions) return (lexical || stat instanceof AST_Var) && declarations_only(stat);
1798         if (stat instanceof AST_ExportDeclaration) return is_declaration(stat.body, lexical);
1799         if (stat instanceof AST_ExportDefault) return is_declaration(stat.body, lexical);
1800         return stat instanceof AST_LambdaDefinition;
1801     }
1802
1803     function is_last_statement(body, stat) {
1804         var index = body.lastIndexOf(stat);
1805         if (index < 0) return false;
1806         while (++index < body.length) {
1807             if (!is_declaration(body[index], true)) return false;
1808         }
1809         return true;
1810     }
1811
1812     function tighten_body(statements, compressor) {
1813         var in_loop, in_try, scope;
1814         find_loop_scope_try();
1815         var CHANGED, max_iter = 10;
1816         do {
1817             CHANGED = false;
1818             eliminate_spurious_blocks(statements);
1819             if (compressor.option("dead_code")) {
1820                 eliminate_dead_code(statements, compressor);
1821             }
1822             if (compressor.option("if_return")) {
1823                 handle_if_return(statements, compressor);
1824             }
1825             if (compressor.sequences_limit > 0) {
1826                 sequencesize(statements, compressor);
1827                 sequencesize_2(statements, compressor);
1828             }
1829             if (compressor.option("join_vars")) {
1830                 join_consecutive_vars(statements);
1831             }
1832             if (compressor.option("collapse_vars")) {
1833                 collapse(statements, compressor);
1834             }
1835         } while (CHANGED && max_iter-- > 0);
1836         return statements;
1837
1838         function find_loop_scope_try() {
1839             var node = compressor.self(), level = 0;
1840             do {
1841                 if (node instanceof AST_Catch) {
1842                     if (compressor.parent(level).bfinally) {
1843                         if (!in_try) in_try = {};
1844                         in_try.bfinally = true;
1845                     }
1846                     level++;
1847                 } else if (node instanceof AST_Finally) {
1848                     level++;
1849                 } else if (node instanceof AST_IterationStatement) {
1850                     in_loop = true;
1851                 } else if (node instanceof AST_Scope) {
1852                     scope = node;
1853                     break;
1854                 } else if (node instanceof AST_Try) {
1855                     if (!in_try) in_try = {};
1856                     if (node.bcatch) in_try.bcatch = true;
1857                     if (node.bfinally) in_try.bfinally = true;
1858                 }
1859             } while (node = compressor.parent(level++));
1860         }
1861
1862         // Search from right to left for assignment-like expressions:
1863         // - `var a = x;`
1864         // - `a = x;`
1865         // - `++a`
1866         // For each candidate, scan from left to right for first usage, then try
1867         // to fold assignment into the site for compression.
1868         // Will not attempt to collapse assignments into or past code blocks
1869         // which are not sequentially executed, e.g. loops and conditionals.
1870         function collapse(statements, compressor) {
1871             if (scope.pinned()) return statements;
1872             var args;
1873             var assignments = Object.create(null);
1874             var candidates = [];
1875             var declare_only = Object.create(null);
1876             var force_single;
1877             var stat_index = statements.length;
1878             var scanner = new TreeTransformer(function(node, descend) {
1879                 if (abort) return node;
1880                 // Skip nodes before `candidate` as quickly as possible
1881                 if (!hit) {
1882                     if (node !== hit_stack[hit_index]) return node;
1883                     hit_index++;
1884                     if (hit_index < hit_stack.length) return handle_custom_scan_order(node, scanner);
1885                     hit = true;
1886                     stop_after = (value_def ? find_stop_value : find_stop)(node, 0);
1887                     if (stop_after === node) abort = true;
1888                     return node;
1889                 }
1890                 // Stop immediately if these node types are encountered
1891                 var parent = scanner.parent();
1892                 if (should_stop(node, parent)) {
1893                     abort = true;
1894                     return node;
1895                 }
1896                 // Stop only if candidate is found within conditional branches
1897                 if (!stop_if_hit && in_conditional(node, parent)) {
1898                     stop_if_hit = parent;
1899                 }
1900                 // Skip transient nodes caused by single-use variable replacement
1901                 if (node.single_use && parent instanceof AST_VarDef && parent.value === node) return node;
1902                 // Replace variable with assignment when found
1903                 var hit_rhs;
1904                 if (!(node instanceof AST_SymbolDeclaration)
1905                     && (scan_lhs && lhs.equivalent_to(node)
1906                         || scan_rhs && (hit_rhs = scan_rhs(node, this)))) {
1907                     if (!can_replace || stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) {
1908                         if (!hit_rhs && !value_def) abort = true;
1909                         return node;
1910                     }
1911                     if (is_lhs(node, parent)) {
1912                         if (value_def && !hit_rhs) assign_used = true;
1913                         return node;
1914                     } else if (value_def) {
1915                         if (stop_if_hit && assign_pos == 0) assign_pos = remaining - replaced;
1916                         if (!hit_rhs) replaced++;
1917                         return node;
1918                     } else {
1919                         replaced++;
1920                     }
1921                     CHANGED = abort = true;
1922                     AST_Node.info("Collapsing {node} [{file}:{line},{col}]", {
1923                         node: node,
1924                         file: node.start.file,
1925                         line: node.start.line,
1926                         col: node.start.col,
1927                     });
1928                     if (candidate.TYPE == "Binary") return make_node(AST_Assign, candidate, {
1929                         operator: "=",
1930                         left: candidate.right.left,
1931                         right: make_node(AST_Conditional, candidate, {
1932                             condition: candidate.operator == "&&" ? candidate.left : candidate.left.negate(compressor),
1933                             consequent: candidate.right.right,
1934                             alternative: node,
1935                         }),
1936                     });
1937                     if (candidate instanceof AST_UnaryPostfix) {
1938                         if (lhs instanceof AST_SymbolRef) lhs.definition().fixed = false;
1939                         return make_node(AST_UnaryPrefix, candidate, candidate);
1940                     }
1941                     if (candidate instanceof AST_VarDef) {
1942                         var def = candidate.name.definition();
1943                         if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
1944                             def.replaced++;
1945                             return maintain_this_binding(compressor, parent, node, candidate.value);
1946                         }
1947                         return make_node(AST_Assign, candidate, {
1948                             operator: "=",
1949                             left: make_node(AST_SymbolRef, candidate.name, candidate.name),
1950                             right: candidate.value,
1951                         });
1952                     }
1953                     var assign = candidate;
1954                     while (assign.write_only) {
1955                         assign.write_only = false;
1956                         if (!(assign instanceof AST_Assign)) break;
1957                         assign = assign.right;
1958                     }
1959                     return candidate;
1960                 }
1961                 // These node types have child nodes that execute sequentially,
1962                 // but are otherwise not safe to scan into or beyond them.
1963                 if (is_last_node(node, parent) || may_throw(node)) {
1964                     stop_after = node;
1965                     if (node instanceof AST_Scope) abort = true;
1966                 }
1967                 // Scan but don't replace inside getter/setter
1968                 if (node instanceof AST_Accessor) {
1969                     var replace = can_replace;
1970                     can_replace = false;
1971                     descend(node, scanner);
1972                     can_replace = replace;
1973                     return signal_abort(node);
1974                 }
1975                 // Scan but don't replace inside destructuring expression
1976                 if (node instanceof AST_Destructured) {
1977                     var replace = can_replace;
1978                     can_replace = false;
1979                     descend(node, scanner);
1980                     can_replace = replace;
1981                     return signal_abort(node);
1982                 }
1983                 // Scan but don't replace inside default value
1984                 if (node instanceof AST_DefaultValue) {
1985                     node.name = node.name.transform(scanner);
1986                     var replace = can_replace;
1987                     can_replace = false;
1988                     node.value = node.value.transform(scanner);
1989                     can_replace = replace;
1990                     return signal_abort(node);
1991                 }
1992                 // Scan but don't replace inside block scope with colliding variable
1993                 if (node instanceof AST_BlockScope
1994                     && !(node instanceof AST_Scope)
1995                     && !(node.variables && node.variables.all(function(def) {
1996                         return !lvalues.has(def.name);
1997                     }))) {
1998                     var replace = can_replace;
1999                     can_replace = false;
2000                     if (!handle_custom_scan_order(node, scanner)) descend(node, scanner);
2001                     can_replace = replace;
2002                     return signal_abort(node);
2003                 }
2004                 return handle_custom_scan_order(node, scanner);
2005             }, signal_abort);
2006             var multi_replacer = new TreeTransformer(function(node) {
2007                 if (abort) return node;
2008                 // Skip nodes before `candidate` as quickly as possible
2009                 if (!hit) {
2010                     if (node !== hit_stack[hit_index]) return node;
2011                     hit_index++;
2012                     switch (hit_stack.length - hit_index) {
2013                       case 0:
2014                         hit = true;
2015                         if (assign_used) return node;
2016                         if (node !== candidate) return node;
2017                         if (node instanceof AST_VarDef) return node;
2018                         def.replaced++;
2019                         var parent = multi_replacer.parent();
2020                         if (parent instanceof AST_Sequence && parent.tail_node() !== node) {
2021                             value_def.replaced++;
2022                             return List.skip;
2023                         }
2024                         return rvalue;
2025                       case 1:
2026                         if (!assign_used && node.body === candidate) {
2027                             hit = true;
2028                             def.replaced++;
2029                             value_def.replaced++;
2030                             return null;
2031                         }
2032                       default:
2033                         return handle_custom_scan_order(node, multi_replacer);
2034                     }
2035                 }
2036                 // Replace variable when found
2037                 if (node instanceof AST_SymbolRef && node.definition() === def) {
2038                     if (is_lhs(node, multi_replacer.parent())) return node;
2039                     if (!--replaced) abort = true;
2040                     var ref = rvalue.clone();
2041                     ref.scope = node.scope;
2042                     ref.reference();
2043                     if (replaced == assign_pos) {
2044                         abort = true;
2045                         return make_node(AST_Assign, candidate, {
2046                             operator: "=",
2047                             left: node,
2048                             right: ref,
2049                         });
2050                     }
2051                     def.replaced++;
2052                     return ref;
2053                 }
2054                 // Skip (non-executed) functions and (leading) default case in switch statements
2055                 if (node instanceof AST_Default || node instanceof AST_Scope) return node;
2056             }, patch_sequence);
2057             while (--stat_index >= 0) {
2058                 // Treat parameters as collapsible in IIFE, i.e.
2059                 //   function(a, b){ ... }(x());
2060                 // would be translated into equivalent assignments:
2061                 //   var a = x(), b = undefined;
2062                 if (stat_index == 0 && compressor.option("unused")) extract_args();
2063                 // Find collapsible assignments
2064                 var hit_stack = [];
2065                 extract_candidates(statements[stat_index]);
2066                 while (candidates.length > 0) {
2067                     hit_stack = candidates.pop();
2068                     var hit_index = 0;
2069                     var candidate = hit_stack[hit_stack.length - 1];
2070                     var assign_pos = -1;
2071                     var assign_used = false;
2072                     var remaining;
2073                     var value_def = null;
2074                     var stop_after = null;
2075                     var stop_if_hit = null;
2076                     var lhs = get_lhs(candidate);
2077                     var side_effects = lhs && lhs.has_side_effects(compressor);
2078                     var scan_lhs = lhs && !side_effects && !is_lhs_read_only(lhs, compressor);
2079                     var scan_rhs = foldable(candidate);
2080                     if (!scan_lhs && !scan_rhs) continue;
2081                     var funarg = candidate.name instanceof AST_SymbolFunarg;
2082                     var may_throw = return_false;
2083                     if (candidate.may_throw(compressor)) {
2084                         if (funarg && is_async(scope)) continue;
2085                         may_throw = in_try ? function(node) {
2086                             return node.has_side_effects(compressor);
2087                         } : side_effects_external;
2088                     }
2089                     var read_toplevel = false;
2090                     var modify_toplevel = false;
2091                     // Locate symbols which may execute code outside of scanning range
2092                     var well_defined = true;
2093                     var lvalues = get_lvalues(candidate);
2094                     var lhs_local = is_lhs_local(lhs);
2095                     var rvalue = get_rvalue(candidate);
2096                     if (!side_effects) side_effects = value_has_side_effects();
2097                     var check_destructured = in_try || !lhs_local ? function(node) {
2098                         return node instanceof AST_Destructured;
2099                     } : return_false;
2100                     var replace_all = replace_all_symbols(candidate);
2101                     var hit = funarg;
2102                     var abort = false;
2103                     var replaced = 0;
2104                     var can_replace = !args || !hit;
2105                     if (!can_replace) {
2106                         for (var j = candidate.arg_index + 1; !abort && j < args.length; j++) {
2107                             if (args[j]) args[j].transform(scanner);
2108                         }
2109                         can_replace = true;
2110                     }
2111                     for (var i = stat_index; !abort && i < statements.length; i++) {
2112                         statements[i].transform(scanner);
2113                     }
2114                     if (value_def) {
2115                         if (!replaced || remaining > replaced + assign_used) {
2116                             candidates.push(hit_stack);
2117                             force_single = true;
2118                             continue;
2119                         }
2120                         if (replaced == assign_pos) assign_used = true;
2121                         var def = lhs.definition();
2122                         abort = false;
2123                         hit_index = 0;
2124                         hit = funarg;
2125                         for (var i = stat_index; !abort && i < statements.length; i++) {
2126                             if (!statements[i].transform(multi_replacer)) statements.splice(i--, 1);
2127                         }
2128                         replaced = candidate instanceof AST_VarDef
2129                             && candidate === hit_stack[hit_stack.length - 1]
2130                             && def.references.length == def.replaced
2131                             && !compressor.exposed(def);
2132                         value_def.last_ref = false;
2133                         value_def.single_use = false;
2134                         CHANGED = true;
2135                     }
2136                     if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
2137                 }
2138             }
2139
2140             function signal_abort(node) {
2141                 if (abort) return node;
2142                 if (stop_after === node) abort = true;
2143                 if (stop_if_hit === node) stop_if_hit = null;
2144                 return node;
2145             }
2146
2147             function handle_custom_scan_order(node, tt) {
2148                 if (!(node instanceof AST_BlockScope)) {
2149                     if (!(node instanceof AST_ClassProperty && !node.static)) return;
2150                     // Skip non-static class property values
2151                     if (node.key instanceof AST_Node) node.key = node.key.transform(tt);
2152                     return node;
2153                 }
2154                 // Skip (non-executed) functions
2155                 if (node instanceof AST_Scope) return node;
2156                 // Scan object only in a for-in/of statement
2157                 if (node instanceof AST_ForEnumeration) {
2158                     node.object = node.object.transform(tt);
2159                     abort = true;
2160                     return node;
2161                 }
2162                 // Scan first case expression only in a switch statement
2163                 if (node instanceof AST_Switch) {
2164                     node.expression = node.expression.transform(tt);
2165                     for (var i = 0; !abort && i < node.body.length; i++) {
2166                         var branch = node.body[i];
2167                         if (branch instanceof AST_Case) {
2168                             if (!hit) {
2169                                 if (branch !== hit_stack[hit_index]) continue;
2170                                 hit_index++;
2171                             }
2172                             branch.expression = branch.expression.transform(tt);
2173                             if (!replace_all) break;
2174                             scan_rhs = false;
2175                         }
2176                     }
2177                     abort = true;
2178                     return node;
2179                 }
2180             }
2181
2182             function is_direct_assignment(node, parent) {
2183                 if (parent instanceof AST_Assign) return parent.operator == "=" && parent.left === node;
2184                 if (parent instanceof AST_DefaultValue) return parent.name === node;
2185                 if (parent instanceof AST_DestructuredArray) return true;
2186                 if (parent instanceof AST_DestructuredKeyVal) return parent.value === node;
2187             }
2188
2189             function should_stop(node, parent) {
2190                 if (node === rvalue) return true;
2191                 if (parent instanceof AST_For) {
2192                     if (node !== parent.init) return true;
2193                 }
2194                 if (node instanceof AST_Assign) {
2195                     return node.operator != "=" && lhs.equivalent_to(node.left);
2196                 }
2197                 if (node instanceof AST_Call) {
2198                     if (!(lhs instanceof AST_PropAccess)) return false;
2199                     if (!lhs.equivalent_to(node.expression)) return false;
2200                     return !(rvalue instanceof AST_LambdaExpression && !rvalue.contains_this());
2201                 }
2202                 if (node instanceof AST_Class) return !compressor.has_directive("use strict");
2203                 if (node instanceof AST_Debugger) return true;
2204                 if (node instanceof AST_Defun) return funarg && lhs.name === node.name.name;
2205                 if (node instanceof AST_DestructuredKeyVal) return node.key instanceof AST_Node;
2206                 if (node instanceof AST_DWLoop) return true;
2207                 if (node instanceof AST_LoopControl) return true;
2208                 if (node instanceof AST_SymbolRef) {
2209                     if (node.is_declared(compressor)) {
2210                         if (node.fixed_value()) return false;
2211                         if (can_drop_symbol(node)) {
2212                             return !(parent instanceof AST_PropAccess && parent.expression === node)
2213                                 && is_arguments(node.definition());
2214                         }
2215                     } else if (is_direct_assignment(node, parent)) {
2216                         return false;
2217                     }
2218                     if (!replace_all) return true;
2219                     scan_rhs = false;
2220                     return false;
2221                 }
2222                 if (node instanceof AST_Try) return true;
2223                 if (node instanceof AST_With) return true;
2224                 return false;
2225             }
2226
2227             function in_conditional(node, parent) {
2228                 if (parent instanceof AST_Assign) return parent.left !== node && lazy_op[parent.operator.slice(0, -1)];
2229                 if (parent instanceof AST_Binary) return parent.left !== node && lazy_op[parent.operator];
2230                 if (parent instanceof AST_Call) return parent.optional && parent.expression !== node;
2231                 if (parent instanceof AST_Case) return parent.expression !== node;
2232                 if (parent instanceof AST_Conditional) return parent.condition !== node;
2233                 if (parent instanceof AST_If) return parent.condition !== node;
2234                 if (parent instanceof AST_Sub) return parent.optional && parent.expression !== node;
2235             }
2236
2237             function is_last_node(node, parent) {
2238                 if (node instanceof AST_Await) return true;
2239                 if (node.TYPE == "Binary") return node.operator == "in" && !is_object(node.right);
2240                 if (node instanceof AST_Call) {
2241                     var def, fn = node.expression;
2242                     if (fn instanceof AST_SymbolRef) {
2243                         def = fn.definition();
2244                         fn = fn.fixed_value();
2245                     }
2246                     if (!(fn instanceof AST_Lambda)) return !node.is_expr_pure(compressor);
2247                     if (def && recursive_ref(compressor, def, fn)) return true;
2248                     if (fn.collapse_scanning) return false;
2249                     fn.collapse_scanning = true;
2250                     var replace = can_replace;
2251                     can_replace = false;
2252                     var after = stop_after;
2253                     var if_hit = stop_if_hit;
2254                     if (!all(fn.argnames, function(argname) {
2255                         if (argname instanceof AST_DefaultValue) {
2256                             argname.value.transform(scanner);
2257                             if (abort) return false;
2258                             argname = argname.name;
2259                         }
2260                         return !(argname instanceof AST_Destructured);
2261                     })) {
2262                         abort = true;
2263                     } else if (is_arrow(fn) && fn.value) {
2264                         fn.value.transform(scanner);
2265                     } else for (var i = 0; !abort && i < fn.body.length; i++) {
2266                         var stat = fn.body[i];
2267                         if (stat instanceof AST_Return) {
2268                             if (stat.value) stat.value.transform(scanner);
2269                             break;
2270                         }
2271                         stat.transform(scanner);
2272                     }
2273                     stop_if_hit = if_hit;
2274                     stop_after = after;
2275                     can_replace = replace;
2276                     delete fn.collapse_scanning;
2277                     if (!abort) return false;
2278                     abort = false;
2279                     return true;
2280                 }
2281                 if (node instanceof AST_Exit) {
2282                     if (in_try) {
2283                         if (in_try.bfinally) return true;
2284                         if (in_try.bcatch && node instanceof AST_Throw) return true;
2285                     }
2286                     return side_effects || lhs instanceof AST_PropAccess || may_modify(lhs);
2287                 }
2288                 if (node instanceof AST_Function) {
2289                     return compressor.option("ie") && node.name && lvalues.has(node.name.name);
2290                 }
2291                 if (node instanceof AST_ObjectIdentity) return symbol_in_lvalues(node, parent);
2292                 if (node instanceof AST_PropAccess) {
2293                     if (side_effects) return true;
2294                     var exp = node.expression;
2295                     if (exp instanceof AST_SymbolRef && is_arguments(exp.definition())) return true;
2296                     if (compressor.option("unsafe")) {
2297                         if (is_undeclared_ref(exp) && global_names[exp.name]) return false;
2298                         if (is_static_fn(exp)) return false;
2299                     }
2300                     if (!well_defined) return true;
2301                     if (value_def) return false;
2302                     if (!in_try && lhs_local) return false;
2303                     if (node.optional) return false;
2304                     return exp.may_throw_on_access(compressor);
2305                 }
2306                 if (node instanceof AST_Spread) return true;
2307                 if (node instanceof AST_SymbolRef) {
2308                     if (symbol_in_lvalues(node, parent)) return !is_direct_assignment(node, parent);
2309                     if (side_effects && may_modify(node)) return true;
2310                     var def = node.definition();
2311                     return (in_try || def.scope.resolve() !== scope) && !can_drop_symbol(node);
2312                 }
2313                 if (node instanceof AST_Template) return !node.is_expr_pure(compressor);
2314                 if (node instanceof AST_VarDef) {
2315                     if (check_destructured(node.name)) return true;
2316                     return (node.value || parent instanceof AST_Let) && node.name.match_symbol(function(node) {
2317                         return node instanceof AST_SymbolDeclaration
2318                             && (lvalues.has(node.name) || side_effects && may_modify(node));
2319                     }, true);
2320                 }
2321                 if (node instanceof AST_Yield) return true;
2322                 var sym = is_lhs(node.left, node);
2323                 if (!sym) return false;
2324                 if (sym instanceof AST_PropAccess) return true;
2325                 if (check_destructured(sym)) return true;
2326                 return sym.match_symbol(function(node) {
2327                     return node instanceof AST_SymbolRef
2328                         && (lvalues.has(node.name) || read_toplevel && compressor.exposed(node.definition()));
2329                 }, true);
2330             }
2331
2332             function may_throw_destructured(node, value) {
2333                 if (!value) return !(node instanceof AST_Symbol);
2334                 if (node instanceof AST_DefaultValue) {
2335                     return value.has_side_effects(compressor)
2336                         || node.value.has_side_effects(compressor)
2337                         || may_throw_destructured(node.name, is_undefined(value) && node.value);
2338                 }
2339                 if (node instanceof AST_Destructured) {
2340                     if (node.rest && may_throw_destructured(node.rest)) return true;
2341                     if (node instanceof AST_DestructuredArray) {
2342                         if (!(value instanceof AST_Array || value.is_string(compressor))) return true;
2343                         return !all(node.elements, function(element) {
2344                             return !may_throw_destructured(element);
2345                         });
2346                     }
2347                     if (node instanceof AST_DestructuredObject) {
2348                         if (!value.is_defined(compressor)) return true;
2349                         return !all(node.properties, function(prop) {
2350                             if (prop instanceof AST_Node && prop.has_side_effects(compressor)) return false;
2351                             return !may_throw_destructured(prop.value);
2352                         });
2353                     }
2354                 }
2355             }
2356
2357             function extract_args() {
2358                 var iife, fn = compressor.self();
2359                 if (fn instanceof AST_LambdaExpression
2360                     && !is_generator(fn)
2361                     && !fn.uses_arguments
2362                     && !fn.pinned()
2363                     && (iife = compressor.parent()) instanceof AST_Call
2364                     && iife.expression === fn
2365                     && is_iife_single(iife)
2366                     && all(iife.args, function(arg) {
2367                         return !(arg instanceof AST_Spread);
2368                     })) {
2369                     var fn_strict = compressor.has_directive("use strict");
2370                     if (fn_strict && !member(fn_strict, fn.body)) fn_strict = false;
2371                     var has_await = is_async(fn) ? function(node) {
2372                         return node instanceof AST_Symbol && node.name == "await";
2373                     } : function(node) {
2374                         return node instanceof AST_Await && !tw.find_parent(AST_Scope);
2375                     };
2376                     var arg_scope = null;
2377                     var tw = new TreeWalker(function(node, descend) {
2378                         if (!arg) return true;
2379                         if (has_await(node) || node instanceof AST_Yield) {
2380                             arg = null;
2381                             return true;
2382                         }
2383                         if (node instanceof AST_ObjectIdentity && (fn_strict || !arg_scope)) {
2384                             arg = null;
2385                             return true;
2386                         }
2387                         if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
2388                             var s = node.definition().scope;
2389                             if (s !== scope) while (s = s.parent_scope) {
2390                                 if (s === scope) return true;
2391                             }
2392                             arg = null;
2393                         }
2394                         if (node instanceof AST_Scope && !is_arrow(node)) {
2395                             var save_scope = arg_scope;
2396                             arg_scope = node;
2397                             descend();
2398                             arg_scope = save_scope;
2399                             return true;
2400                         }
2401                     });
2402                     args = iife.args.slice();
2403                     var len = args.length;
2404                     var names = Object.create(null);
2405                     for (var i = fn.argnames.length; --i >= 0;) {
2406                         var sym = fn.argnames[i];
2407                         var arg = args[i];
2408                         var value;
2409                         if (sym instanceof AST_DefaultValue) {
2410                             value = sym.value;
2411                             sym = sym.name;
2412                             args[len + i] = value;
2413                         }
2414                         if (sym instanceof AST_Destructured) {
2415                             if (!may_throw_destructured(sym, arg)) continue;
2416                             candidates.length = 0;
2417                             break;
2418                         }
2419                         if (sym.name in names) continue;
2420                         names[sym.name] = true;
2421                         if (value) arg = !arg || is_undefined(arg) ? value : null;
2422                         if (!arg && !value) {
2423                             arg = make_node(AST_Undefined, sym).transform(compressor);
2424                         } else if (arg instanceof AST_Lambda && arg.pinned()) {
2425                             arg = null;
2426                         } else if (arg) {
2427                             arg.walk(tw);
2428                         }
2429                         if (!arg) continue;
2430                         var candidate = make_node(AST_VarDef, sym, {
2431                             name: sym,
2432                             value: arg,
2433                         });
2434                         candidate.name_index = i;
2435                         candidate.arg_index = value ? len + i : i;
2436                         candidates.unshift([ candidate ]);
2437                     }
2438                 }
2439             }
2440
2441             function extract_candidates(expr, unused) {
2442                 hit_stack.push(expr);
2443                 if (expr instanceof AST_Array) {
2444                     expr.elements.forEach(function(node) {
2445                         extract_candidates(node, unused);
2446                     });
2447                 } else if (expr instanceof AST_Assign) {
2448                     var lhs = expr.left;
2449                     if (!(lhs instanceof AST_Destructured)) candidates.push(hit_stack.slice());
2450                     extract_candidates(lhs);
2451                     extract_candidates(expr.right);
2452                     if (lhs instanceof AST_SymbolRef && expr.operator == "=") {
2453                         assignments[lhs.name] = (assignments[lhs.name] || 0) + 1;
2454                     }
2455                 } else if (expr instanceof AST_Await) {
2456                     extract_candidates(expr.expression, unused);
2457                 } else if (expr instanceof AST_Binary) {
2458                     var lazy = lazy_op[expr.operator];
2459                     if (unused
2460                         && lazy
2461                         && expr.operator != "??"
2462                         && expr.right instanceof AST_Assign
2463                         && expr.right.operator == "="
2464                         && !(expr.right.left instanceof AST_Destructured)) {
2465                         candidates.push(hit_stack.slice());
2466                     }
2467                     extract_candidates(expr.left, !lazy && unused);
2468                     extract_candidates(expr.right, unused);
2469                 } else if (expr instanceof AST_Call) {
2470                     extract_candidates(expr.expression);
2471                     expr.args.forEach(extract_candidates);
2472                 } else if (expr instanceof AST_Case) {
2473                     extract_candidates(expr.expression);
2474                 } else if (expr instanceof AST_Conditional) {
2475                     extract_candidates(expr.condition);
2476                     extract_candidates(expr.consequent, unused);
2477                     extract_candidates(expr.alternative, unused);
2478                 } else if (expr instanceof AST_Definitions) {
2479                     expr.definitions.forEach(extract_candidates);
2480                 } else if (expr instanceof AST_Dot) {
2481                     extract_candidates(expr.expression);
2482                 } else if (expr instanceof AST_DWLoop) {
2483                     extract_candidates(expr.condition);
2484                     if (!(expr.body instanceof AST_Block)) {
2485                         extract_candidates(expr.body);
2486                     }
2487                 } else if (expr instanceof AST_Exit) {
2488                     if (expr.value) extract_candidates(expr.value);
2489                 } else if (expr instanceof AST_For) {
2490                     if (expr.init) extract_candidates(expr.init, true);
2491                     if (expr.condition) extract_candidates(expr.condition);
2492                     if (expr.step) extract_candidates(expr.step, true);
2493                     if (!(expr.body instanceof AST_Block)) {
2494                         extract_candidates(expr.body);
2495                     }
2496                 } else if (expr instanceof AST_ForEnumeration) {
2497                     extract_candidates(expr.object);
2498                     if (!(expr.body instanceof AST_Block)) {
2499                         extract_candidates(expr.body);
2500                     }
2501                 } else if (expr instanceof AST_If) {
2502                     extract_candidates(expr.condition);
2503                     if (!(expr.body instanceof AST_Block)) {
2504                         extract_candidates(expr.body);
2505                     }
2506                     if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
2507                         extract_candidates(expr.alternative);
2508                     }
2509                 } else if (expr instanceof AST_Object) {
2510                     expr.properties.forEach(function(prop) {
2511                         hit_stack.push(prop);
2512                         if (prop.key instanceof AST_Node) extract_candidates(prop.key);
2513                         if (prop instanceof AST_ObjectKeyVal) extract_candidates(prop.value, unused);
2514                         hit_stack.pop();
2515                     });
2516                 } else if (expr instanceof AST_Sequence) {
2517                     var end = expr.expressions.length - (unused ? 0 : 1);
2518                     expr.expressions.forEach(function(node, index) {
2519                         extract_candidates(node, index < end);
2520                     });
2521                 } else if (expr instanceof AST_SimpleStatement) {
2522                     extract_candidates(expr.body, true);
2523                 } else if (expr instanceof AST_Spread) {
2524                     extract_candidates(expr.expression);
2525                 } else if (expr instanceof AST_Sub) {
2526                     extract_candidates(expr.expression);
2527                     extract_candidates(expr.property);
2528                 } else if (expr instanceof AST_Switch) {
2529                     extract_candidates(expr.expression);
2530                     expr.body.forEach(extract_candidates);
2531                 } else if (expr instanceof AST_Unary) {
2532                     if (UNARY_POSTFIX[expr.operator]) {
2533                         candidates.push(hit_stack.slice());
2534                     } else {
2535                         extract_candidates(expr.expression);
2536                     }
2537                 } else if (expr instanceof AST_VarDef) {
2538                     if (expr.name instanceof AST_SymbolVar) {
2539                         if (expr.value) {
2540                             var def = expr.name.definition();
2541                             if (def.references.length > def.replaced) {
2542                                 candidates.push(hit_stack.slice());
2543                             }
2544                         } else {
2545                             declare_only[expr.name.name] = (declare_only[expr.name.name] || 0) + 1;
2546                         }
2547                     }
2548                     if (expr.value) extract_candidates(expr.value);
2549                 } else if (expr instanceof AST_Yield) {
2550                     if (expr.expression) extract_candidates(expr.expression);
2551                 }
2552                 hit_stack.pop();
2553             }
2554
2555             function find_stop(node, level) {
2556                 var parent = scanner.parent(level);
2557                 if (parent instanceof AST_Array) return node;
2558                 if (parent instanceof AST_Assign) return node;
2559                 if (parent instanceof AST_Await) return node;
2560                 if (parent instanceof AST_Binary) return node;
2561                 if (parent instanceof AST_Call) return node;
2562                 if (parent instanceof AST_Case) return node;
2563                 if (parent instanceof AST_Conditional) return node;
2564                 if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
2565                 if (parent instanceof AST_Exit) return node;
2566                 if (parent instanceof AST_If) return node;
2567                 if (parent instanceof AST_IterationStatement) return node;
2568                 if (parent instanceof AST_ObjectProperty) return node;
2569                 if (parent instanceof AST_PropAccess) return node;
2570                 if (parent instanceof AST_Sequence) {
2571                     return (parent.tail_node() === node ? find_stop : find_stop_unused)(parent, level + 1);
2572                 }
2573                 if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
2574                 if (parent instanceof AST_Spread) return node;
2575                 if (parent instanceof AST_Switch) return node;
2576                 if (parent instanceof AST_Unary) return node;
2577                 if (parent instanceof AST_VarDef) return node;
2578                 if (parent instanceof AST_Yield) return node;
2579                 return null;
2580             }
2581
2582             function find_stop_logical(parent, op, level) {
2583                 var node;
2584                 do {
2585                     node = parent;
2586                     parent = scanner.parent(++level);
2587                 } while (parent instanceof AST_Assign && parent.operator.slice(0, -1) == op
2588                     || parent instanceof AST_Binary && parent.operator == op);
2589                 return node;
2590             }
2591
2592             function find_stop_expr(expr, cont, node, parent, level) {
2593                 var replace = can_replace;
2594                 can_replace = false;
2595                 var after = stop_after;
2596                 var if_hit = stop_if_hit;
2597                 var stack = scanner.stack;
2598                 scanner.stack = [ parent ];
2599                 expr.transform(scanner);
2600                 scanner.stack = stack;
2601                 stop_if_hit = if_hit;
2602                 stop_after = after;
2603                 can_replace = replace;
2604                 if (abort) {
2605                     abort = false;
2606                     return node;
2607                 }
2608                 return cont(parent, level + 1);
2609             }
2610
2611             function find_stop_value(node, level) {
2612                 var parent = scanner.parent(level);
2613                 if (parent instanceof AST_Array) return find_stop_value(parent, level + 1);
2614                 if (parent instanceof AST_Assign) {
2615                     if (may_throw(parent)) return node;
2616                     if (parent.left.match_symbol(function(ref) {
2617                         return ref instanceof AST_SymbolRef && (lhs.name == ref.name || value_def.name == ref.name);
2618                     })) return node;
2619                     var op;
2620                     if (parent.left === node || !lazy_op[op = parent.operator.slice(0, -1)]) {
2621                         return find_stop_value(parent, level + 1);
2622                     }
2623                     return find_stop_logical(parent, op, level);
2624                 }
2625                 if (parent instanceof AST_Binary) {
2626                     var op;
2627                     if (parent.left === node || !lazy_op[op = parent.operator]) {
2628                         return find_stop_value(parent, level + 1);
2629                     }
2630                     return find_stop_logical(parent, op, level);
2631                 }
2632                 if (parent instanceof AST_Call) return parent;
2633                 if (parent instanceof AST_Case) {
2634                     if (parent.expression !== node) return node;
2635                     return find_stop_value(parent, level + 1);
2636                 }
2637                 if (parent instanceof AST_Conditional) {
2638                     if (parent.condition !== node) return node;
2639                     return find_stop_value(parent, level + 1);
2640                 }
2641                 if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
2642                 if (parent instanceof AST_Do) return node;
2643                 if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1);
2644                 if (parent instanceof AST_For) {
2645                     if (parent.init !== node && parent.condition !== node) return node;
2646                     return find_stop_value(parent, level + 1);
2647                 }
2648                 if (parent instanceof AST_ForEnumeration) {
2649                     if (parent.init !== node) return node;
2650                     return find_stop_value(parent, level + 1);
2651                 }
2652                 if (parent instanceof AST_If) {
2653                     if (parent.condition !== node) return node;
2654                     return find_stop_value(parent, level + 1);
2655                 }
2656                 if (parent instanceof AST_ObjectProperty) {
2657                     var obj = scanner.parent(level + 1);
2658                     return all(obj.properties, function(prop) {
2659                         return prop instanceof AST_ObjectKeyVal;
2660                     }) ? find_stop_value(obj, level + 2) : obj;
2661                 }
2662                 if (parent instanceof AST_PropAccess) {
2663                     var exp = parent.expression;
2664                     return exp === node ? find_stop_value(parent, level + 1) : node;
2665                 }
2666                 if (parent instanceof AST_Sequence) {
2667                     return (parent.tail_node() === node ? find_stop_value : find_stop_unused)(parent, level + 1);
2668                 }
2669                 if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
2670                 if (parent instanceof AST_Spread) return find_stop_value(parent, level + 1);
2671                 if (parent instanceof AST_Switch) {
2672                     if (parent.expression !== node) return node;
2673                     return find_stop_value(parent, level + 1);
2674                 }
2675                 if (parent instanceof AST_Unary) {
2676                     if (parent.operator == "delete") return node;
2677                     return find_stop_value(parent, level + 1);
2678                 }
2679                 if (parent instanceof AST_VarDef) return parent.name.match_symbol(function(sym) {
2680                     return sym instanceof AST_SymbolDeclaration && (lhs.name == sym.name || value_def.name == sym.name);
2681                 }) ? node : find_stop_value(parent, level + 1);
2682                 if (parent instanceof AST_While) {
2683                     if (parent.condition !== node) return node;
2684                     return find_stop_value(parent, level + 1);
2685                 }
2686                 if (parent instanceof AST_Yield) return find_stop_value(parent, level + 1);
2687                 return null;
2688             }
2689
2690             function find_stop_unused(node, level) {
2691                 var parent = scanner.parent(level);
2692                 if (is_last_node(node, parent)) return node;
2693                 if (in_conditional(node, parent)) return node;
2694                 if (parent instanceof AST_Array) return find_stop_unused(parent, level + 1);
2695                 if (parent instanceof AST_Assign) return check_assignment(parent.left);
2696                 if (parent instanceof AST_Await) return node;
2697                 if (parent instanceof AST_Binary) return find_stop_unused(parent, level + 1);
2698                 if (parent instanceof AST_Call) return find_stop_unused(parent, level + 1);
2699                 if (parent instanceof AST_Case) return find_stop_unused(parent, level + 1);
2700                 if (parent instanceof AST_Conditional) return find_stop_unused(parent, level + 1);
2701                 if (parent instanceof AST_Definitions) return find_stop_unused(parent, level + 1);
2702                 if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1);
2703                 if (parent instanceof AST_If) return find_stop_unused(parent, level + 1);
2704                 if (parent instanceof AST_IterationStatement) return node;
2705                 if (parent instanceof AST_ObjectProperty) {
2706                     var obj = scanner.parent(level + 1);
2707                     return all(obj.properties, function(prop) {
2708                         return prop instanceof AST_ObjectKeyVal;
2709                     }) ? find_stop_unused(obj, level + 2) : obj;
2710                 }
2711                 if (parent instanceof AST_PropAccess) {
2712                     var exp = parent.expression;
2713                     if (exp === node) return find_stop_unused(parent, level + 1);
2714                     return find_stop_expr(exp, find_stop_unused, node, parent, level);
2715                 }
2716                 if (parent instanceof AST_Sequence) return find_stop_unused(parent, level + 1);
2717                 if (parent instanceof AST_SimpleStatement) return find_stop_unused(parent, level + 1);
2718                 if (parent instanceof AST_Spread) return node;
2719                 if (parent instanceof AST_Switch) return find_stop_unused(parent, level + 1);
2720                 if (parent instanceof AST_Unary) return find_stop_unused(parent, level + 1);
2721                 if (parent instanceof AST_VarDef) return check_assignment(parent.name);
2722                 if (parent instanceof AST_Yield) return node;
2723                 return null;
2724
2725                 function check_assignment(lhs) {
2726                     if (may_throw(parent)) return node;
2727                     if (lhs !== node && lhs instanceof AST_Destructured) {
2728                         return find_stop_expr(lhs, find_stop_unused, node, parent, level);
2729                     }
2730                     return find_stop_unused(parent, level + 1);
2731                 }
2732             }
2733
2734             function mangleable_var(rhs) {
2735                 if (force_single) {
2736                     force_single = false;
2737                     return;
2738                 }
2739                 if (remaining < 1) return;
2740                 var value = rhs instanceof AST_Assign && rhs.operator == "=" ? rhs.left : rhs;
2741                 if (!(value instanceof AST_SymbolRef)) return;
2742                 var def = value.definition();
2743                 if (def.undeclared) return;
2744                 if (is_arguments(def)) return;
2745                 if (value !== rhs) {
2746                     if (is_lhs_read_only(value, compressor)) return;
2747                     var referenced = def.references.length - def.replaced;
2748                     if (referenced < 2) return;
2749                     var expr = candidate.clone();
2750                     expr[expr instanceof AST_Assign ? "right" : "value"] = value;
2751                     if (candidate.name_index >= 0) {
2752                         expr.name_index = candidate.name_index;
2753                         expr.arg_index = candidate.arg_index;
2754                     }
2755                     candidate = expr;
2756                 }
2757                 return value_def = def;
2758             }
2759
2760             function remaining_refs(def) {
2761                 return def.references.length - def.replaced - (assignments[def.name] || 0);
2762             }
2763
2764             function get_lhs(expr) {
2765                 if (expr instanceof AST_Assign) {
2766                     var lhs = expr.left;
2767                     if (expr.operator != "=") return lhs;
2768                     if (!(lhs instanceof AST_SymbolRef)) return lhs;
2769                     var def = lhs.definition();
2770                     if (scope.uses_arguments && is_funarg(def)) return lhs;
2771                     if (compressor.exposed(def)) return lhs;
2772                     remaining = remaining_refs(def);
2773                     if (def.fixed && lhs.fixed) {
2774                         var matches = def.references.filter(function(ref) {
2775                             return ref.fixed === lhs.fixed;
2776                         }).length - 1;
2777                         if (matches < remaining) {
2778                             remaining = matches;
2779                             assign_pos = 0;
2780                         }
2781                     }
2782                     mangleable_var(expr.right);
2783                     return lhs;
2784                 }
2785                 if (expr instanceof AST_Binary) return expr.right.left;
2786                 if (expr instanceof AST_Unary) return expr.expression;
2787                 if (expr instanceof AST_VarDef) {
2788                     var lhs = expr.name;
2789                     var def = lhs.definition();
2790                     if (def.const_redefs) return;
2791                     if (!member(lhs, def.orig)) return;
2792                     if (scope.uses_arguments && is_funarg(def)) return;
2793                     var declared = def.orig.length - def.eliminated - (declare_only[def.name] || 0);
2794                     remaining = remaining_refs(def);
2795                     if (def.fixed) remaining = Math.min(remaining, def.references.filter(function(ref) {
2796                         if (!ref.fixed) return true;
2797                         if (!ref.fixed.assigns) return true;
2798                         var assign = ref.fixed.assigns[0];
2799                         return assign === lhs || get_rvalue(assign) === expr.value;
2800                     }).length);
2801                     if (declared > 1 && !(lhs instanceof AST_SymbolFunarg)) {
2802                         mangleable_var(expr.value);
2803                         return make_node(AST_SymbolRef, lhs, lhs);
2804                     }
2805                     if (mangleable_var(expr.value) || remaining == 1 && !compressor.exposed(def)) {
2806                         return make_node(AST_SymbolRef, lhs, lhs);
2807                     }
2808                     return;
2809                 }
2810             }
2811
2812             function get_rvalue(expr) {
2813                 if (expr instanceof AST_Assign) return expr.right;
2814                 if (expr instanceof AST_Binary) {
2815                     var node = expr.clone();
2816                     node.right = expr.right.right;
2817                     return node;
2818                 }
2819                 if (expr instanceof AST_VarDef) return expr.value;
2820             }
2821
2822             function invariant(expr) {
2823                 if (expr instanceof AST_Array) return false;
2824                 if (expr instanceof AST_Binary && lazy_op[expr.operator]) {
2825                     return invariant(expr.left) && invariant(expr.right);
2826                 }
2827                 if (expr instanceof AST_Call) return false;
2828                 if (expr instanceof AST_Conditional) {
2829                     return invariant(expr.consequent) && invariant(expr.alternative);
2830                 }
2831                 if (expr instanceof AST_Object) return false;
2832                 return !expr.has_side_effects(compressor);
2833             }
2834
2835             function foldable(expr) {
2836                 if (expr instanceof AST_Assign && expr.right.single_use) return;
2837                 var lhs_ids = Object.create(null);
2838                 var marker = new TreeWalker(function(node) {
2839                     if (node instanceof AST_SymbolRef) lhs_ids[node.definition().id] = true;
2840                 });
2841                 while (expr instanceof AST_Assign && expr.operator == "=") {
2842                     expr.left.walk(marker);
2843                     expr = expr.right;
2844                 }
2845                 if (expr instanceof AST_ObjectIdentity) return rhs_exact_match;
2846                 if (expr instanceof AST_SymbolRef) {
2847                     var value = expr.evaluate(compressor);
2848                     if (value === expr) return rhs_exact_match;
2849                     return rhs_fuzzy_match(value, rhs_exact_match);
2850                 }
2851                 if (expr.is_truthy()) return rhs_fuzzy_match(true, return_false);
2852                 if (expr.is_constant()) {
2853                     var ev = expr.evaluate(compressor);
2854                     if (!(ev instanceof AST_Node)) return rhs_fuzzy_match(ev, rhs_exact_match);
2855                 }
2856                 if (!(lhs instanceof AST_SymbolRef)) return false;
2857                 if (!invariant(expr)) return false;
2858                 var circular;
2859                 expr.walk(new TreeWalker(function(node) {
2860                     if (circular) return true;
2861                     if (node instanceof AST_SymbolRef && lhs_ids[node.definition().id]) circular = true;
2862                 }));
2863                 return !circular && rhs_exact_match;
2864
2865                 function rhs_exact_match(node) {
2866                     return expr.equivalent_to(node);
2867                 }
2868             }
2869
2870             function rhs_fuzzy_match(value, fallback) {
2871                 return function(node, tw) {
2872                     if (tw.in_boolean_context()) {
2873                         if (value && node.is_truthy() && !node.has_side_effects(compressor)) {
2874                             return true;
2875                         }
2876                         if (node.is_constant()) {
2877                             var ev = node.evaluate(compressor);
2878                             if (!(ev instanceof AST_Node)) return !ev == !value;
2879                         }
2880                     }
2881                     return fallback(node);
2882                 };
2883             }
2884
2885             function may_be_global(node) {
2886                 if (node instanceof AST_SymbolRef) {
2887                     node = node.fixed_value();
2888                     if (!node) return true;
2889                 }
2890                 if (node instanceof AST_Assign) return node.operator == "=" && may_be_global(node.right);
2891                 return node instanceof AST_PropAccess || node instanceof AST_ObjectIdentity;
2892             }
2893
2894             function get_lvalues(expr) {
2895                 var lvalues = new Dictionary();
2896                 if (expr instanceof AST_VarDef) {
2897                     if (!expr.name.definition().fixed) well_defined = false;
2898                     lvalues.add(expr.name.name, lhs);
2899                 }
2900                 var find_arguments = scope.uses_arguments && !compressor.has_directive("use strict");
2901                 var scan_toplevel = scope instanceof AST_Toplevel;
2902                 var tw = new TreeWalker(function(node) {
2903                     var value;
2904                     if (node instanceof AST_SymbolRef) {
2905                         value = node.fixed_value();
2906                         if (!value) {
2907                             value = node;
2908                             var def = node.definition();
2909                             if (!def.undeclared
2910                                 && (def.assignments || !def.escaped || def.escaped.cross_scope)
2911                                 && (has_escaped(def, node.scope, node, tw.parent()) || !same_scope(def))) {
2912                                 well_defined = false;
2913                             }
2914                         }
2915                     } else if (node instanceof AST_ObjectIdentity) {
2916                         value = node;
2917                     }
2918                     if (value) lvalues.add(node.name, is_modified(compressor, tw, node, value, 0));
2919                     if (find_arguments && node instanceof AST_Sub) {
2920                         scope.each_argname(function(argname) {
2921                             if (!compressor.option("reduce_vars") || argname.definition().assignments) {
2922                                 if (!argname.definition().fixed) well_defined = false;
2923                                 lvalues.add(argname.name, true);
2924                             }
2925                         });
2926                         find_arguments = false;
2927                     }
2928                     if (!scan_toplevel) return;
2929                     if (node.TYPE == "Call") {
2930                         if (modify_toplevel) return;
2931                         var exp = node.expression;
2932                         if (exp instanceof AST_PropAccess) return;
2933                         if (exp instanceof AST_LambdaExpression && !exp.contains_this()) return;
2934                         modify_toplevel = true;
2935                     } else if (node instanceof AST_PropAccess && may_be_global(node.expression)) {
2936                         if (node === lhs && !(expr instanceof AST_Unary)) {
2937                             modify_toplevel = true;
2938                         } else {
2939                             read_toplevel = true;
2940                         }
2941                     }
2942                 });
2943                 expr.walk(tw);
2944                 return lvalues;
2945             }
2946
2947             function remove_candidate(expr) {
2948                 var index = expr.name_index;
2949                 if (index >= 0) {
2950                     var argname = scope.argnames[index];
2951                     if (argname instanceof AST_DefaultValue) {
2952                         argname.value = make_node(AST_Number, argname, {
2953                             value: 0
2954                         });
2955                         argname.name.definition().fixed = false;
2956                     } else {
2957                         var args = compressor.parent().args;
2958                         if (args[index]) {
2959                             args[index] = make_node(AST_Number, args[index], {
2960                                 value: 0
2961                             });
2962                             argname.definition().fixed = false;
2963                         }
2964                     }
2965                     return true;
2966                 }
2967                 var end = hit_stack.length - 1;
2968                 if (hit_stack[end - 1].body === hit_stack[end]) end--;
2969                 var tt = new TreeTransformer(function(node, descend, in_list) {
2970                     if (hit) return node;
2971                     if (node !== hit_stack[hit_index]) return node;
2972                     hit_index++;
2973                     if (hit_index <= end) return handle_custom_scan_order(node, tt);
2974                     hit = true;
2975                     if (node instanceof AST_VarDef) {
2976                         declare_only[node.name.name] = (declare_only[node.name.name] || 0) + 1;
2977                         if (value_def) value_def.replaced++;
2978                         node = node.clone();
2979                         node.value = null;
2980                         return node;
2981                     }
2982                     return in_list ? List.skip : null;
2983                 }, patch_sequence);
2984                 abort = false;
2985                 hit = false;
2986                 hit_index = 0;
2987                 return statements[stat_index].transform(tt);
2988             }
2989
2990             function patch_sequence(node) {
2991                 if (node instanceof AST_Sequence) switch (node.expressions.length) {
2992                   case 0: return null;
2993                   case 1: return maintain_this_binding(compressor, this.parent(), node, node.expressions[0]);
2994                 }
2995             }
2996
2997             function is_lhs_local(lhs) {
2998                 var sym = root_expr(lhs);
2999                 return sym instanceof AST_SymbolRef
3000                     && sym.definition().scope.resolve() === scope
3001                     && !(in_loop
3002                         && (lvalues.has(sym.name) && lvalues.get(sym.name)[0] !== lhs
3003                             || candidate instanceof AST_Unary
3004                             || candidate instanceof AST_Assign && candidate.operator != "="));
3005             }
3006
3007             function value_has_side_effects() {
3008                 if (candidate instanceof AST_Unary) return false;
3009                 return rvalue.has_side_effects(compressor);
3010             }
3011
3012             function replace_all_symbols(expr) {
3013                 if (expr instanceof AST_Unary) return false;
3014                 if (side_effects) return false;
3015                 if (value_def) return true;
3016                 if (!(lhs instanceof AST_SymbolRef)) return false;
3017                 var referenced;
3018                 if (expr instanceof AST_VarDef) {
3019                     referenced = 1;
3020                 } else if (expr.operator == "=") {
3021                     referenced = 2;
3022                 } else {
3023                     return false;
3024                 }
3025                 var def = lhs.definition();
3026                 return def.references.length - def.replaced == referenced;
3027             }
3028
3029             function symbol_in_lvalues(sym, parent) {
3030                 var lvalue = lvalues.get(sym.name);
3031                 if (!lvalue || all(lvalue, function(lhs) {
3032                     return !lhs;
3033                 })) return;
3034                 if (lvalue[0] !== lhs) return true;
3035                 scan_rhs = false;
3036             }
3037
3038             function may_modify(sym) {
3039                 var def = sym.definition();
3040                 if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return false;
3041                 if (def.scope.resolve() !== scope) return true;
3042                 if (modify_toplevel && compressor.exposed(def)) return true;
3043                 return !all(def.references, function(ref) {
3044                     return ref.scope.resolve() === scope;
3045                 });
3046             }
3047
3048             function side_effects_external(node, lhs) {
3049                 if (node instanceof AST_Assign) return side_effects_external(node.left, true);
3050                 if (node instanceof AST_Unary) return side_effects_external(node.expression, true);
3051                 if (node instanceof AST_VarDef) return node.value && side_effects_external(node.value);
3052                 if (lhs) {
3053                     if (node instanceof AST_Dot) return side_effects_external(node.expression, true);
3054                     if (node instanceof AST_Sub) return side_effects_external(node.expression, true);
3055                     if (node instanceof AST_SymbolRef) return node.definition().scope.resolve() !== scope;
3056                 }
3057                 return false;
3058             }
3059         }
3060
3061         function eliminate_spurious_blocks(statements) {
3062             var seen_dirs = [];
3063             for (var i = 0; i < statements.length;) {
3064                 var stat = statements[i];
3065                 if (stat instanceof AST_BlockStatement) {
3066                     if (all(stat.body, safe_to_trim)) {
3067                         CHANGED = true;
3068                         eliminate_spurious_blocks(stat.body);
3069                         [].splice.apply(statements, [i, 1].concat(stat.body));
3070                         i += stat.body.length;
3071                         continue;
3072                     }
3073                 }
3074                 if (stat instanceof AST_Directive) {
3075                     if (member(stat.value, seen_dirs)) {
3076                         CHANGED = true;
3077                         statements.splice(i, 1);
3078                         continue;
3079                     }
3080                     seen_dirs.push(stat.value);
3081                 }
3082                 if (stat instanceof AST_EmptyStatement) {
3083                     CHANGED = true;
3084                     statements.splice(i, 1);
3085                     continue;
3086                 }
3087                 i++;
3088             }
3089         }
3090
3091         function handle_if_return(statements, compressor) {
3092             var self = compressor.self();
3093             var parent = compressor.parent();
3094             var in_lambda = last_of(function(node) {
3095                 return node instanceof AST_Lambda;
3096             });
3097             var in_iife = in_lambda && parent && parent.TYPE == "Call";
3098             var multiple_if_returns = has_multiple_if_returns(statements);
3099             for (var i = statements.length; --i >= 0;) {
3100                 var stat = statements[i];
3101                 var j = next_index(i);
3102                 var next = statements[j];
3103
3104                 if (in_lambda && !next && stat instanceof AST_Return) {
3105                     if (!stat.value) {
3106                         CHANGED = true;
3107                         statements.splice(i, 1);
3108                         continue;
3109                     }
3110                     var tail = stat.value.tail_node();
3111                     if (tail instanceof AST_UnaryPrefix && tail.operator == "void") {
3112                         CHANGED = true;
3113                         var body;
3114                         if (tail === stat.value) {
3115                             body = tail.expression;
3116                         } else {
3117                             body = stat.value.clone();
3118                             body.expressions[body.length - 1] = tail.expression;
3119                         }
3120                         statements[i] = make_node(AST_SimpleStatement, stat, {
3121                             body: body,
3122                         });
3123                         continue;
3124                     }
3125                 }
3126
3127                 if (stat instanceof AST_If) {
3128                     var ab = aborts(stat.body);
3129                     if (can_merge_flow(ab)) {
3130                         if (ab.label) remove(ab.label.thedef.references, ab);
3131                         CHANGED = true;
3132                         stat = stat.clone();
3133                         stat.condition = stat.condition.negate(compressor);
3134                         var body = as_statement_array_with_return(stat.body, ab);
3135                         stat.body = make_node(AST_BlockStatement, stat, {
3136                             body: as_statement_array(stat.alternative).concat(extract_functions())
3137                         });
3138                         stat.alternative = make_node(AST_BlockStatement, stat, {
3139                             body: body
3140                         });
3141                         statements[i] = stat;
3142                         statements[i] = stat.transform(compressor);
3143                         continue;
3144                     }
3145
3146                     if (ab && !stat.alternative && stat.body instanceof AST_BlockStatement && next instanceof AST_Jump) {
3147                         var negated = stat.condition.negate(compressor);
3148                         if (negated.print_to_string().length <= stat.condition.print_to_string().length) {
3149                             CHANGED = true;
3150                             stat = stat.clone();
3151                             stat.condition = negated;
3152                             statements[j] = stat.body;
3153                             stat.body = next;
3154                             statements[i] = stat;
3155                             statements[i] = stat.transform(compressor);
3156                             continue;
3157                         }
3158                     }
3159
3160                     var alt = aborts(stat.alternative);
3161                     if (can_merge_flow(alt)) {
3162                         if (alt.label) remove(alt.label.thedef.references, alt);
3163                         CHANGED = true;
3164                         stat = stat.clone();
3165                         stat.body = make_node(AST_BlockStatement, stat.body, {
3166                             body: as_statement_array(stat.body).concat(extract_functions())
3167                         });
3168                         var body = as_statement_array_with_return(stat.alternative, alt);
3169                         stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
3170                             body: body
3171                         });
3172                         statements[i] = stat;
3173                         statements[i] = stat.transform(compressor);
3174                         continue;
3175                     }
3176
3177                     if (compressor.option("typeofs")) {
3178                         if (ab && !alt) {
3179                             mark_locally_defined(stat.condition, null, make_node(AST_BlockStatement, self, {
3180                                 body: statements.slice(i + 1)
3181                             }));
3182                         }
3183                         if (!ab && alt) {
3184                             mark_locally_defined(stat.condition, make_node(AST_BlockStatement, self, {
3185                                 body: statements.slice(i + 1)
3186                             }));
3187                         }
3188                     }
3189                 }
3190
3191                 if (stat instanceof AST_If && stat.body instanceof AST_Return) {
3192                     var value = stat.body.value;
3193                     var in_bool = stat.body.in_bool || next instanceof AST_Return && next.in_bool;
3194                     //---
3195                     // pretty silly case, but:
3196                     // if (foo()) return; return; ---> foo(); return;
3197                     if (!value && !stat.alternative
3198                         && (in_lambda && !next || next instanceof AST_Return && !next.value)) {
3199                         CHANGED = true;
3200                         statements[i] = make_node(AST_SimpleStatement, stat.condition, {
3201                             body: stat.condition
3202                         });
3203                         continue;
3204                     }
3205                     //---
3206                     // if (foo()) return x; return y; ---> return foo() ? x : y;
3207                     if (!stat.alternative && next instanceof AST_Return) {
3208                         CHANGED = true;
3209                         stat = stat.clone();
3210                         stat.alternative = next;
3211                         statements.splice(i, 1, stat.transform(compressor));
3212                         statements.splice(j, 1);
3213                         continue;
3214                     }
3215                     //---
3216                     // if (foo()) return x; [ return ; ] ---> return foo() ? x : undefined;
3217                     if (!stat.alternative && !next && in_lambda && (in_bool || value && multiple_if_returns)) {
3218                         CHANGED = true;
3219                         stat = stat.clone();
3220                         stat.alternative = make_node(AST_Return, stat, {
3221                             value: null
3222                         });
3223                         statements.splice(i, 1, stat.transform(compressor));
3224                         continue;
3225                     }
3226                     //---
3227                     // if (a) return b; if (c) return d; e; ---> return a ? b : c ? d : void e;
3228                     //
3229                     // if sequences is not enabled, this can lead to an endless loop (issue #866).
3230                     // however, with sequences on this helps producing slightly better output for
3231                     // the example code.
3232                     var prev = statements[prev_index(i)];
3233                     if (compressor.option("sequences") && in_lambda && !stat.alternative
3234                         && (!prev && in_iife || prev instanceof AST_If && prev.body instanceof AST_Return)
3235                         && next_index(j) == statements.length && next instanceof AST_SimpleStatement) {
3236                         CHANGED = true;
3237                         stat = stat.clone();
3238                         stat.alternative = make_node(AST_BlockStatement, next, {
3239                             body: [
3240                                 next,
3241                                 make_node(AST_Return, next, {
3242                                     value: null
3243                                 })
3244                             ]
3245                         });
3246                         statements.splice(i, 1, stat.transform(compressor));
3247                         statements.splice(j, 1);
3248                         continue;
3249                     }
3250                 }
3251             }
3252
3253             function has_multiple_if_returns(statements) {
3254                 var n = 0;
3255                 for (var i = statements.length; --i >= 0;) {
3256                     var stat = statements[i];
3257                     if (stat instanceof AST_If && stat.body instanceof AST_Return) {
3258                         if (++n > 1) return true;
3259                     }
3260                 }
3261                 return false;
3262             }
3263
3264             function is_return_void(value) {
3265                 return !value || value instanceof AST_UnaryPrefix && value.operator == "void";
3266             }
3267
3268             function last_of(predicate) {
3269                 var block = self, stat, level = 0;
3270                 do {
3271                     do {
3272                         if (predicate(block)) return true;
3273                         block = compressor.parent(level++);
3274                     } while (block instanceof AST_If && (stat = block));
3275                 } while ((block instanceof AST_BlockStatement || block instanceof AST_Scope)
3276                     && is_last_statement(block.body, stat));
3277             }
3278
3279             function match_target(target) {
3280                 return last_of(function(node) {
3281                     return node === target;
3282                 });
3283             }
3284
3285             function can_drop_abort(ab) {
3286                 if (ab instanceof AST_Return) return in_lambda && is_return_void(ab.value);
3287                 if (!(ab instanceof AST_LoopControl)) return false;
3288                 var lct = compressor.loopcontrol_target(ab);
3289                 if (ab instanceof AST_Continue) return match_target(loop_body(lct));
3290                 if (lct instanceof AST_IterationStatement) return false;
3291                 return match_target(lct);
3292             }
3293
3294             function can_merge_flow(ab) {
3295                 if (!can_drop_abort(ab)) return false;
3296                 for (var j = statements.length; --j > i;) {
3297                     var stat = statements[j];
3298                     if (stat instanceof AST_DefClass) {
3299                         if (stat.name.definition().preinit) return false;
3300                     } else if (stat instanceof AST_Const || stat instanceof AST_Let) {
3301                         if (!all(stat.definitions, function(defn) {
3302                             return !defn.name.match_symbol(function(node) {
3303                                 return node instanceof AST_SymbolDeclaration && node.definition().preinit;
3304                             });
3305                         })) return false;
3306                     }
3307                 }
3308                 return true;
3309             }
3310
3311             function extract_functions() {
3312                 var defuns = [];
3313                 var lexical = false;
3314                 var tail = statements.splice(i + 1).filter(function(stat) {
3315                     if (stat instanceof AST_LambdaDefinition) {
3316                         defuns.push(stat);
3317                         return false;
3318                     }
3319                     if (is_lexical_definition(stat)) lexical = true;
3320                     return true;
3321                 });
3322                 [].push.apply(lexical ? tail : statements, defuns);
3323                 return tail;
3324             }
3325
3326             function as_statement_array_with_return(node, ab) {
3327                 var body = as_statement_array(node);
3328                 var block = body, last;
3329                 while ((last = block[block.length - 1]) !== ab) {
3330                     block = last.body;
3331                 }
3332                 block.pop();
3333                 if (ab.value) block.push(make_node(AST_SimpleStatement, ab.value, {
3334                     body: ab.value.expression
3335                 }));
3336                 return body;
3337             }
3338
3339             function next_index(i) {
3340                 for (var j = i + 1; j < statements.length; j++) {
3341                     if (!is_declaration(statements[j])) break;
3342                 }
3343                 return j;
3344             }
3345
3346             function prev_index(i) {
3347                 for (var j = i; --j >= 0;) {
3348                     if (!is_declaration(statements[j])) break;
3349                 }
3350                 return j;
3351             }
3352         }
3353
3354         function eliminate_dead_code(statements, compressor) {
3355             var has_quit;
3356             var self = compressor.self();
3357             for (var i = 0, n = 0, len = statements.length; i < len; i++) {
3358                 var stat = statements[i];
3359                 if (stat instanceof AST_LoopControl) {
3360                     var lct = compressor.loopcontrol_target(stat);
3361                     if (loop_body(lct) !== self
3362                         || stat instanceof AST_Break && lct instanceof AST_IterationStatement) {
3363                         statements[n++] = stat;
3364                     } else if (stat.label) {
3365                         remove(stat.label.thedef.references, stat);
3366                     }
3367                 } else {
3368                     statements[n++] = stat;
3369                 }
3370                 if (aborts(stat)) {
3371                     has_quit = statements.slice(i + 1);
3372                     break;
3373                 }
3374             }
3375             statements.length = n;
3376             if (has_quit) has_quit.forEach(function(stat) {
3377                 extract_declarations_from_unreachable_code(compressor, stat, statements);
3378             });
3379             CHANGED = statements.length != len;
3380         }
3381
3382         function sequencesize(statements, compressor) {
3383             if (statements.length < 2) return;
3384             var seq = [], n = 0;
3385             function push_seq() {
3386                 if (!seq.length) return;
3387                 var body = make_sequence(seq[0], seq);
3388                 statements[n++] = make_node(AST_SimpleStatement, body, { body: body });
3389                 seq = [];
3390             }
3391             for (var i = 0, len = statements.length; i < len; i++) {
3392                 var stat = statements[i];
3393                 if (stat instanceof AST_SimpleStatement) {
3394                     if (seq.length >= compressor.sequences_limit) push_seq();
3395                     var body = stat.body;
3396                     if (seq.length > 0) body = body.drop_side_effect_free(compressor);
3397                     if (body) merge_sequence(seq, body);
3398                 } else if (is_declaration(stat)) {
3399                     statements[n++] = stat;
3400                 } else {
3401                     push_seq();
3402                     statements[n++] = stat;
3403                 }
3404             }
3405             push_seq();
3406             statements.length = n;
3407             if (n != len) CHANGED = true;
3408         }
3409
3410         function to_simple_statement(block, decls) {
3411             if (!(block instanceof AST_BlockStatement)) return block;
3412             var stat = null;
3413             for (var i = 0; i < block.body.length; i++) {
3414                 var line = block.body[i];
3415                 if (line instanceof AST_Var && declarations_only(line)) {
3416                     decls.push(line);
3417                 } else if (stat || is_lexical_definition(line)) {
3418                     return false;
3419                 } else {
3420                     stat = line;
3421                 }
3422             }
3423             return stat;
3424         }
3425
3426         function sequencesize_2(statements, compressor) {
3427             function cons_seq(right) {
3428                 n--;
3429                 CHANGED = true;
3430                 var left = prev.body;
3431                 return make_sequence(left, [ left, right ]);
3432             }
3433             var n = 0, prev;
3434             for (var i = 0; i < statements.length; i++) {
3435                 var stat = statements[i];
3436                 if (prev) {
3437                     if (stat instanceof AST_Exit) {
3438                         if (stat.value || !in_async_generator(scope)) {
3439                             stat.value = cons_seq(stat.value || make_node(AST_Undefined, stat)).optimize(compressor);
3440                         }
3441                     } else if (stat instanceof AST_For) {
3442                         if (!(stat.init instanceof AST_Definitions)) {
3443                             var abort = false;
3444                             prev.body.walk(new TreeWalker(function(node) {
3445                                 if (abort || node instanceof AST_Scope) return true;
3446                                 if (node instanceof AST_Binary && node.operator == "in") {
3447                                     abort = true;
3448                                     return true;
3449                                 }
3450                             }));
3451                             if (!abort) {
3452                                 if (stat.init) stat.init = cons_seq(stat.init);
3453                                 else {
3454                                     stat.init = prev.body;
3455                                     n--;
3456                                     CHANGED = true;
3457                                 }
3458                             }
3459                         }
3460                     } else if (stat instanceof AST_ForIn) {
3461                         if (!is_lexical_definition(stat.init)) stat.object = cons_seq(stat.object);
3462                     } else if (stat instanceof AST_If) {
3463                         stat.condition = cons_seq(stat.condition);
3464                     } else if (stat instanceof AST_Switch) {
3465                         stat.expression = cons_seq(stat.expression);
3466                     } else if (stat instanceof AST_With) {
3467                         stat.expression = cons_seq(stat.expression);
3468                     }
3469                 }
3470                 if (compressor.option("conditionals") && stat instanceof AST_If) {
3471                     var decls = [];
3472                     var body = to_simple_statement(stat.body, decls);
3473                     var alt = to_simple_statement(stat.alternative, decls);
3474                     if (body !== false && alt !== false && decls.length > 0) {
3475                         var len = decls.length;
3476                         decls.push(make_node(AST_If, stat, {
3477                             condition: stat.condition,
3478                             body: body || make_node(AST_EmptyStatement, stat.body),
3479                             alternative: alt
3480                         }));
3481                         decls.unshift(n, 1);
3482                         [].splice.apply(statements, decls);
3483                         i += len;
3484                         n += len + 1;
3485                         prev = null;
3486                         CHANGED = true;
3487                         continue;
3488                     }
3489                 }
3490                 statements[n++] = stat;
3491                 prev = stat instanceof AST_SimpleStatement ? stat : null;
3492             }
3493             statements.length = n;
3494         }
3495
3496         function extract_exprs(body) {
3497             if (body instanceof AST_Assign) return [ body ];
3498             if (body instanceof AST_Sequence) return body.expressions.slice();
3499         }
3500
3501         function join_assigns(defn, body, keep) {
3502             var exprs = extract_exprs(body);
3503             if (!exprs) return;
3504             var trimmed = false;
3505             for (var i = exprs.length - (keep || 0); --i >= 0;) {
3506                 var expr = exprs[i];
3507                 if (!can_trim(expr)) continue;
3508                 var tail;
3509                 if (expr.left instanceof AST_SymbolRef) {
3510                     tail = exprs.slice(i + 1);
3511                 } else if (expr.left instanceof AST_PropAccess && can_trim(expr.left.expression)) {
3512                     tail = exprs.slice(i + 1);
3513                     var flattened = expr.clone();
3514                     expr = expr.left.expression;
3515                     flattened.left = flattened.left.clone();
3516                     flattened.left.expression = expr.left.clone();
3517                     tail.unshift(flattened);
3518                 } else {
3519                     continue;
3520                 }
3521                 if (tail.length == 0) continue;
3522                 if (!trim_assigns(expr.left, expr.right, tail)) continue;
3523                 trimmed = true;
3524                 exprs = exprs.slice(0, i).concat(expr, tail);
3525             }
3526             if (defn instanceof AST_Definitions) {
3527                 keep = keep || 0;
3528                 for (var i = defn.definitions.length; --i >= 0;) {
3529                     var def = defn.definitions[i];
3530                     if (!def.value) continue;
3531                     if (trim_assigns(def.name, def.value, exprs)) trimmed = true;
3532                     if (merge_conditional_assignments(def, exprs, keep)) trimmed = true;
3533                     break;
3534                 }
3535                 if (defn instanceof AST_Var && join_var_assign(defn.definitions, exprs, keep)) trimmed = true;
3536             }
3537             return trimmed && exprs;
3538
3539             function can_trim(node) {
3540                 return node instanceof AST_Assign && node.operator == "=";
3541             }
3542         }
3543
3544         function merge_assigns(prev, defn) {
3545             if (!(prev instanceof AST_SimpleStatement)) return;
3546             if (declarations_only(defn)) return;
3547             var exprs = extract_exprs(prev.body);
3548             if (!exprs) return;
3549             var definitions = [];
3550             if (!join_var_assign(definitions, exprs.reverse(), 0)) return;
3551             defn.definitions = definitions.reverse().concat(defn.definitions);
3552             return exprs.reverse();
3553         }
3554
3555         function merge_conditional_assignments(var_def, exprs, keep) {
3556             if (!compressor.option("conditionals")) return;
3557             if (var_def.name instanceof AST_Destructured) return;
3558             var trimmed = false;
3559             var def = var_def.name.definition();
3560             while (exprs.length > keep) {
3561                 var cond = to_conditional_assignment(compressor, def, var_def.value, exprs[0]);
3562                 if (!cond) break;
3563                 var_def.value = cond;
3564                 exprs.shift();
3565                 trimmed = true;
3566             }
3567             return trimmed;
3568         }
3569
3570         function join_var_assign(definitions, exprs, keep) {
3571             var trimmed = false;
3572             while (exprs.length > keep) {
3573                 var expr = exprs[0];
3574                 if (!(expr instanceof AST_Assign)) break;
3575                 if (expr.operator != "=") break;
3576                 var lhs = expr.left;
3577                 if (!(lhs instanceof AST_SymbolRef)) break;
3578                 if (is_undeclared_ref(lhs)) break;
3579                 if (lhs.scope.resolve() !== scope) break;
3580                 var def = lhs.definition();
3581                 if (def.scope !== scope) break;
3582                 if (def.orig.length > def.eliminated + 1) break;
3583                 if (def.orig[0].TYPE != "SymbolVar") break;
3584                 var name = make_node(AST_SymbolVar, lhs, lhs);
3585                 definitions.push(make_node(AST_VarDef, expr, {
3586                     name: name,
3587                     value: expr.right
3588                 }));
3589                 def.orig.push(name);
3590                 def.replaced++;
3591                 exprs.shift();
3592                 trimmed = true;
3593             }
3594             return trimmed;
3595         }
3596
3597         function trim_assigns(name, value, exprs) {
3598             var names = Object.create(null);
3599             names[name.name] = true;
3600             while (value instanceof AST_Assign && value.operator == "=") {
3601                 if (value.left instanceof AST_SymbolRef) names[value.left.name] = true;
3602                 value = value.right;
3603             }
3604             if (!(value instanceof AST_Object)) return;
3605             var trimmed = false;
3606             do {
3607                 if (!try_join(exprs[0])) break;
3608                 exprs.shift();
3609                 trimmed = true;
3610             } while (exprs.length);
3611             return trimmed;
3612
3613             function try_join(node) {
3614                 if (!(node instanceof AST_Assign)) return;
3615                 if (node.operator != "=") return;
3616                 if (!(node.left instanceof AST_PropAccess)) return;
3617                 var sym = node.left.expression;
3618                 if (!(sym instanceof AST_SymbolRef)) return;
3619                 if (!(sym.name in names)) return;
3620                 if (!node.right.is_constant_expression(scope)) return;
3621                 var prop = node.left.property;
3622                 if (prop instanceof AST_Node) {
3623                     if (try_join(prop)) prop = node.left.property = prop.right.clone();
3624                     prop = prop.evaluate(compressor);
3625                 }
3626                 if (prop instanceof AST_Node) return;
3627                 prop = "" + prop;
3628                 var diff = prop == "__proto__" || compressor.has_directive("use strict") ? function(node) {
3629                     var key = node.key;
3630                     return typeof key == "string" && key != prop && key != "__proto__";
3631                 } : function(node) {
3632                     var key = node.key;
3633                     if (node instanceof AST_ObjectGetter || node instanceof AST_ObjectSetter) {
3634                         return typeof key == "string" && key != prop;
3635                     }
3636                     return key !== "__proto__";
3637                 };
3638                 if (!all(value.properties, diff)) return;
3639                 value.properties.push(make_node(AST_ObjectKeyVal, node, {
3640                     key: prop,
3641                     value: node.right,
3642                 }));
3643                 return true;
3644             }
3645         }
3646
3647         function join_consecutive_vars(statements) {
3648             var defs;
3649             for (var i = 0, j = -1; i < statements.length; i++) {
3650                 var stat = statements[i];
3651                 var prev = statements[j];
3652                 if (stat instanceof AST_Definitions) {
3653                     if (prev && prev.TYPE == stat.TYPE) {
3654                         prev.definitions = prev.definitions.concat(stat.definitions);
3655                         CHANGED = true;
3656                     } else if (defs && defs.TYPE == stat.TYPE && declarations_only(stat)) {
3657                         defs.definitions = defs.definitions.concat(stat.definitions);
3658                         CHANGED = true;
3659                     } else if (stat instanceof AST_Var) {
3660                         var exprs = merge_assigns(prev, stat);
3661                         if (exprs) {
3662                             if (exprs.length) {
3663                                 prev.body = make_sequence(prev, exprs);
3664                                 j++;
3665                             }
3666                             CHANGED = true;
3667                         } else {
3668                             j++;
3669                         }
3670                         statements[j] = defs = stat;
3671                     } else {
3672                         statements[++j] = stat;
3673                     }
3674                     continue;
3675                 } else if (stat instanceof AST_Exit) {
3676                     stat.value = join_assigns_expr(stat.value);
3677                 } else if (stat instanceof AST_For) {
3678                     var exprs = join_assigns(prev, stat.init);
3679                     if (exprs) {
3680                         CHANGED = true;
3681                         stat.init = exprs.length ? make_sequence(stat.init, exprs) : null;
3682                     } else if (prev instanceof AST_Var && (!stat.init || stat.init.TYPE == prev.TYPE)) {
3683                         if (stat.init) {
3684                             prev.definitions = prev.definitions.concat(stat.init.definitions);
3685                         }
3686                         defs = stat.init = prev;
3687                         statements[j] = merge_defns(stat);
3688                         CHANGED = true;
3689                         continue;
3690                     } else if (defs && stat.init && defs.TYPE == stat.init.TYPE && declarations_only(stat.init)) {
3691                         defs.definitions = defs.definitions.concat(stat.init.definitions);
3692                         stat.init = null;
3693                         CHANGED = true;
3694                     } else if (stat.init instanceof AST_Var) {
3695                         defs = stat.init;
3696                         exprs = merge_assigns(prev, stat.init);
3697                         if (exprs) {
3698                             CHANGED = true;
3699                             if (exprs.length == 0) {
3700                                 statements[j] = merge_defns(stat);
3701                                 continue;
3702                             }
3703                             prev.body = make_sequence(prev, exprs);
3704                         }
3705                     }
3706                 } else if (stat instanceof AST_ForEnumeration) {
3707                     if (defs && defs.TYPE == stat.init.TYPE) {
3708                         var defns = defs.definitions.slice();
3709                         stat.init = stat.init.definitions[0].name.convert_symbol(AST_SymbolRef, function(ref, name) {
3710                             defns.push(make_node(AST_VarDef, name, {
3711                                 name: name,
3712                                 value: null,
3713                             }));
3714                             name.definition().references.push(ref);
3715                         });
3716                         defs.definitions = defns;
3717                         CHANGED = true;
3718                     }
3719                     stat.object = join_assigns_expr(stat.object);
3720                 } else if (stat instanceof AST_If) {
3721                     stat.condition = join_assigns_expr(stat.condition);
3722                 } else if (stat instanceof AST_SimpleStatement) {
3723                     var exprs = join_assigns(prev, stat.body);
3724                     if (exprs) {
3725                         CHANGED = true;
3726                         if (!exprs.length) continue;
3727                         stat.body = make_sequence(stat.body, exprs);
3728                     }
3729                 } else if (stat instanceof AST_Switch) {
3730                     stat.expression = join_assigns_expr(stat.expression);
3731                 } else if (stat instanceof AST_With) {
3732                     stat.expression = join_assigns_expr(stat.expression);
3733                 }
3734                 statements[++j] = defs ? merge_defns(stat) : stat;
3735             }
3736             statements.length = j + 1;
3737
3738             function join_assigns_expr(value) {
3739                 var exprs = join_assigns(prev, value, 1);
3740                 if (!exprs) return value;
3741                 CHANGED = true;
3742                 var tail = value.tail_node();
3743                 if (exprs[exprs.length - 1] !== tail) exprs.push(tail.left);
3744                 return make_sequence(value, exprs);
3745             }
3746
3747             function merge_defns(stat) {
3748                 return stat.transform(new TreeTransformer(function(node, descend, in_list) {
3749                     if (node instanceof AST_Definitions) {
3750                         if (defs === node) return node;
3751                         if (defs.TYPE != node.TYPE) return node;
3752                         var parent = this.parent();
3753                         if (parent instanceof AST_ForEnumeration && parent.init === node) return node;
3754                         if (!declarations_only(node)) return node;
3755                         defs.definitions = defs.definitions.concat(node.definitions);
3756                         CHANGED = true;
3757                         if (parent instanceof AST_For && parent.init === node) return null;
3758                         return in_list ? List.skip : make_node(AST_EmptyStatement, node);
3759                     }
3760                     if (node instanceof AST_ExportDeclaration) return node;
3761                     if (node instanceof AST_Scope) return node;
3762                     if (!is_statement(node)) return node;
3763                 }));
3764             }
3765         }
3766     }
3767
3768     function extract_declarations_from_unreachable_code(compressor, stat, target) {
3769         var block;
3770         var dropped = false;
3771         stat.walk(new TreeWalker(function(node, descend) {
3772             if (node instanceof AST_DefClass) {
3773                 node.extends = null;
3774                 node.properties = [];
3775                 push(node);
3776                 return true;
3777             }
3778             if (node instanceof AST_Definitions) {
3779                 var defns = [];
3780                 if (node.remove_initializers(compressor, defns)) {
3781                     AST_Node.warn("Dropping initialization in unreachable code [{file}:{line},{col}]", node.start);
3782                 }
3783                 if (defns.length > 0) {
3784                     node.definitions = defns;
3785                     push(node);
3786                 }
3787                 return true;
3788             }
3789             if (node instanceof AST_LambdaDefinition) {
3790                 push(node);
3791                 return true;
3792             }
3793             if (node instanceof AST_Scope) return true;
3794             if (node instanceof AST_BlockScope) {
3795                 var save = block;
3796                 block = [];
3797                 descend();
3798                 if (block.required) {
3799                     target.push(make_node(AST_BlockStatement, stat, { body: block }));
3800                 } else if (block.length) {
3801                     [].push.apply(target, block);
3802                 }
3803                 block = save;
3804                 return true;
3805             }
3806             if (!(node instanceof AST_LoopControl)) dropped = true;
3807         }));
3808         if (dropped) AST_Node.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
3809
3810         function push(node) {
3811             if (block) {
3812                 block.push(node);
3813                 if (!safe_to_trim(node)) block.required = true;
3814             } else {
3815                 target.push(node);
3816             }
3817         }
3818     }
3819
3820     function is_undefined(node, compressor) {
3821         return node.is_undefined
3822             || node instanceof AST_Undefined
3823             || node instanceof AST_UnaryPrefix
3824                 && node.operator == "void"
3825                 && !(compressor && node.expression.has_side_effects(compressor));
3826     }
3827
3828     // is_truthy()
3829     // return true if `!!node === true`
3830     (function(def) {
3831         def(AST_Node, return_false);
3832         def(AST_Array, return_true);
3833         def(AST_Assign, function() {
3834             return this.operator == "=" && this.right.is_truthy();
3835         });
3836         def(AST_Lambda, return_true);
3837         def(AST_Object, return_true);
3838         def(AST_RegExp, return_true);
3839         def(AST_Sequence, function() {
3840             return this.tail_node().is_truthy();
3841         });
3842         def(AST_SymbolRef, function() {
3843             var fixed = this.fixed_value();
3844             if (!fixed) return false;
3845             this.is_truthy = return_false;
3846             var result = fixed.is_truthy();
3847             delete this.is_truthy;
3848             return result;
3849         });
3850     })(function(node, func) {
3851         node.DEFMETHOD("is_truthy", func);
3852     });
3853
3854     // is_negative_zero()
3855     // return true if the node may represent -0
3856     (function(def) {
3857         def(AST_Node, return_true);
3858         def(AST_Array, return_false);
3859         function binary(op, left, right) {
3860             switch (op) {
3861               case "-":
3862                 return left.is_negative_zero()
3863                     && (!(right instanceof AST_Constant) || right.value == 0);
3864               case "&&":
3865               case "||":
3866                 return left.is_negative_zero() || right.is_negative_zero();
3867               case "*":
3868               case "/":
3869               case "%":
3870               case "**":
3871                 return true;
3872               default:
3873                 return false;
3874             }
3875         }
3876         def(AST_Assign, function() {
3877             var op = this.operator;
3878             if (op == "=") return this.right.is_negative_zero();
3879             return binary(op.slice(0, -1), this.left, this.right);
3880         });
3881         def(AST_Binary, function() {
3882             return binary(this.operator, this.left, this.right);
3883         });
3884         def(AST_Constant, function() {
3885             return this.value == 0 && 1 / this.value < 0;
3886         });
3887         def(AST_Lambda, return_false);
3888         def(AST_Object, return_false);
3889         def(AST_RegExp, return_false);
3890         def(AST_Sequence, function() {
3891             return this.tail_node().is_negative_zero();
3892         });
3893         def(AST_SymbolRef, function() {
3894             var fixed = this.fixed_value();
3895             if (!fixed) return true;
3896             this.is_negative_zero = return_true;
3897             var result = fixed.is_negative_zero();
3898             delete this.is_negative_zero;
3899             return result;
3900         });
3901         def(AST_UnaryPrefix, function() {
3902             return this.operator == "+" && this.expression.is_negative_zero()
3903                 || this.operator == "-";
3904         });
3905     })(function(node, func) {
3906         node.DEFMETHOD("is_negative_zero", func);
3907     });
3908
3909     // may_throw_on_access()
3910     // returns true if this node may be null, undefined or contain `AST_Accessor`
3911     (function(def) {
3912         AST_Node.DEFMETHOD("may_throw_on_access", function(compressor, force) {
3913             return !compressor.option("pure_getters") || this._dot_throw(compressor, force);
3914         });
3915         function is_strict(compressor, force) {
3916             return force || /strict/.test(compressor.option("pure_getters"));
3917         }
3918         def(AST_Node, is_strict);
3919         def(AST_Array, return_false);
3920         def(AST_Assign, function(compressor) {
3921             var op = this.operator;
3922             var sym = this.left;
3923             var rhs = this.right;
3924             if (op != "=") {
3925                 return lazy_op[op.slice(0, -1)] && (sym._dot_throw(compressor) || rhs._dot_throw(compressor));
3926             }
3927             if (!rhs._dot_throw(compressor)) return false;
3928             if (!(sym instanceof AST_SymbolRef)) return true;
3929             if (rhs instanceof AST_Binary && rhs.operator == "||" && sym.name == rhs.left.name) {
3930                 return rhs.right._dot_throw(compressor);
3931             }
3932             return true;
3933         });
3934         def(AST_Binary, function(compressor) {
3935             return lazy_op[this.operator] && (this.left._dot_throw(compressor) || this.right._dot_throw(compressor));
3936         });
3937         def(AST_Class, return_false);
3938         def(AST_Conditional, function(compressor) {
3939             return this.consequent._dot_throw(compressor) || this.alternative._dot_throw(compressor);
3940         });
3941         def(AST_Constant, return_false);
3942         def(AST_Dot, function(compressor, force) {
3943             if (!is_strict(compressor, force)) return false;
3944             var exp = this.expression;
3945             if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
3946             return !(this.property == "prototype" && is_lambda(exp));
3947         });
3948         def(AST_Lambda, return_false);
3949         def(AST_Null, return_true);
3950         def(AST_Object, function(compressor, force) {
3951             return is_strict(compressor, force) && !all(this.properties, function(prop) {
3952                 if (!(prop instanceof AST_ObjectKeyVal)) return false;
3953                 return !(prop.key === "__proto__" && prop.value._dot_throw(compressor, force));
3954             });
3955         });
3956         def(AST_ObjectIdentity, function(compressor, force) {
3957             return is_strict(compressor, force) && !this.scope.resolve().new;
3958         });
3959         def(AST_Sequence, function(compressor) {
3960             return this.tail_node()._dot_throw(compressor);
3961         });
3962         def(AST_SymbolRef, function(compressor, force) {
3963             if (this.is_undefined) return true;
3964             if (!is_strict(compressor, force)) return false;
3965             if (is_undeclared_ref(this) && this.is_declared(compressor)) return false;
3966             if (this.is_immutable()) return false;
3967             var def = this.definition();
3968             if (is_arguments(def) && !def.scope.rest && all(def.scope.argnames, function(argname) {
3969                 return argname instanceof AST_SymbolFunarg;
3970             })) return def.scope.uses_arguments > 2;
3971             var fixed = this.fixed_value();
3972             if (!fixed) return true;
3973             this._dot_throw = return_true;
3974             if (fixed._dot_throw(compressor)) {
3975                 delete this._dot_throw;
3976                 return true;
3977             }
3978             this._dot_throw = return_false;
3979             return false;
3980         });
3981         def(AST_UnaryPrefix, function() {
3982             return this.operator == "void";
3983         });
3984         def(AST_UnaryPostfix, return_false);
3985         def(AST_Undefined, return_true);
3986     })(function(node, func) {
3987         node.DEFMETHOD("_dot_throw", func);
3988     });
3989
3990     (function(def) {
3991         def(AST_Node, return_false);
3992         def(AST_Array, return_true);
3993         function is_binary_defined(compressor, op, node) {
3994             switch (op) {
3995               case "&&":
3996                 return node.left.is_defined(compressor) && node.right.is_defined(compressor);
3997               case "||":
3998                 return node.left.is_truthy() || node.right.is_defined(compressor);
3999               case "??":
4000                 return node.left.is_defined(compressor) || node.right.is_defined(compressor);
4001               default:
4002                 return true;
4003             }
4004         }
4005         def(AST_Assign, function(compressor) {
4006             var op = this.operator;
4007             if (op == "=") return this.right.is_defined(compressor);
4008             return is_binary_defined(compressor, op.slice(0, -1), this);
4009         });
4010         def(AST_Binary, function(compressor) {
4011             return is_binary_defined(compressor, this.operator, this);
4012         });
4013         def(AST_Conditional, function(compressor) {
4014             return this.consequent.is_defined(compressor) && this.alternative.is_defined(compressor);
4015         });
4016         def(AST_Constant, return_true);
4017         def(AST_Hole, return_false);
4018         def(AST_Lambda, return_true);
4019         def(AST_Object, return_true);
4020         def(AST_Sequence, function(compressor) {
4021             return this.tail_node().is_defined(compressor);
4022         });
4023         def(AST_SymbolRef, function(compressor) {
4024             if (this.is_undefined) return false;
4025             if (is_undeclared_ref(this) && this.is_declared(compressor)) return true;
4026             if (this.is_immutable()) return true;
4027             var fixed = this.fixed_value();
4028             if (!fixed) return false;
4029             this.is_defined = return_false;
4030             var result = fixed.is_defined(compressor);
4031             delete this.is_defined;
4032             return result;
4033         });
4034         def(AST_UnaryPrefix, function() {
4035             return this.operator != "void";
4036         });
4037         def(AST_UnaryPostfix, return_true);
4038         def(AST_Undefined, return_false);
4039     })(function(node, func) {
4040         node.DEFMETHOD("is_defined", func);
4041     });
4042
4043     /* -----[ boolean/negation helpers ]----- */
4044
4045     // methods to determine whether an expression has a boolean result type
4046     (function(def) {
4047         def(AST_Node, return_false);
4048         def(AST_Assign, function(compressor) {
4049             return this.operator == "=" && this.right.is_boolean(compressor);
4050         });
4051         var binary = makePredicate("in instanceof == != === !== < <= >= >");
4052         def(AST_Binary, function(compressor) {
4053             return binary[this.operator] || lazy_op[this.operator]
4054                 && this.left.is_boolean(compressor)
4055                 && this.right.is_boolean(compressor);
4056         });
4057         def(AST_Boolean, return_true);
4058         var fn = makePredicate("every hasOwnProperty isPrototypeOf propertyIsEnumerable some");
4059         def(AST_Call, function(compressor) {
4060             if (!compressor.option("unsafe")) return false;
4061             var exp = this.expression;
4062             return exp instanceof AST_Dot && (fn[exp.property]
4063                 || exp.property == "test" && exp.expression instanceof AST_RegExp);
4064         });
4065         def(AST_Conditional, function(compressor) {
4066             return this.consequent.is_boolean(compressor) && this.alternative.is_boolean(compressor);
4067         });
4068         def(AST_New, return_false);
4069         def(AST_Sequence, function(compressor) {
4070             return this.tail_node().is_boolean(compressor);
4071         });
4072         def(AST_SymbolRef, function(compressor) {
4073             var fixed = this.fixed_value();
4074             if (!fixed) return false;
4075             this.is_boolean = return_false;
4076             var result = fixed.is_boolean(compressor);
4077             delete this.is_boolean;
4078             return result;
4079         });
4080         var unary = makePredicate("! delete");
4081         def(AST_UnaryPrefix, function() {
4082             return unary[this.operator];
4083         });
4084     })(function(node, func) {
4085         node.DEFMETHOD("is_boolean", func);
4086     });
4087
4088     // methods to determine if an expression has a numeric result type
4089     (function(def) {
4090         def(AST_Node, return_false);
4091         var binary = makePredicate("- * / % ** & | ^ << >> >>>");
4092         def(AST_Assign, function(compressor) {
4093             return binary[this.operator.slice(0, -1)]
4094                 || this.operator == "=" && this.right.is_number(compressor);
4095         });
4096         def(AST_Binary, function(compressor) {
4097             if (binary[this.operator]) return true;
4098             if (this.operator != "+") return false;
4099             return (this.left.is_boolean(compressor) || this.left.is_number(compressor))
4100                 && (this.right.is_boolean(compressor) || this.right.is_number(compressor));
4101         });
4102         var fn = makePredicate([
4103             "charCodeAt",
4104             "getDate",
4105             "getDay",
4106             "getFullYear",
4107             "getHours",
4108             "getMilliseconds",
4109             "getMinutes",
4110             "getMonth",
4111             "getSeconds",
4112             "getTime",
4113             "getTimezoneOffset",
4114             "getUTCDate",
4115             "getUTCDay",
4116             "getUTCFullYear",
4117             "getUTCHours",
4118             "getUTCMilliseconds",
4119             "getUTCMinutes",
4120             "getUTCMonth",
4121             "getUTCSeconds",
4122             "getYear",
4123             "indexOf",
4124             "lastIndexOf",
4125             "localeCompare",
4126             "push",
4127             "search",
4128             "setDate",
4129             "setFullYear",
4130             "setHours",
4131             "setMilliseconds",
4132             "setMinutes",
4133             "setMonth",
4134             "setSeconds",
4135             "setTime",
4136             "setUTCDate",
4137             "setUTCFullYear",
4138             "setUTCHours",
4139             "setUTCMilliseconds",
4140             "setUTCMinutes",
4141             "setUTCMonth",
4142             "setUTCSeconds",
4143             "setYear",
4144             "toExponential",
4145             "toFixed",
4146             "toPrecision",
4147         ]);
4148         def(AST_Call, function(compressor) {
4149             if (!compressor.option("unsafe")) return false;
4150             var exp = this.expression;
4151             return exp instanceof AST_Dot && (fn[exp.property]
4152                 || is_undeclared_ref(exp.expression) && exp.expression.name == "Math");
4153         });
4154         def(AST_Conditional, function(compressor) {
4155             return this.consequent.is_number(compressor) && this.alternative.is_number(compressor);
4156         });
4157         def(AST_New, return_false);
4158         def(AST_Number, return_true);
4159         def(AST_Sequence, function(compressor) {
4160             return this.tail_node().is_number(compressor);
4161         });
4162         def(AST_SymbolRef, function(compressor) {
4163             var fixed = this.fixed_value();
4164             if (!fixed) return false;
4165             this.is_number = return_false;
4166             var result = fixed.is_number(compressor);
4167             delete this.is_number;
4168             return result;
4169         });
4170         var unary = makePredicate("+ - ~ ++ --");
4171         def(AST_Unary, function() {
4172             return unary[this.operator];
4173         });
4174     })(function(node, func) {
4175         node.DEFMETHOD("is_number", func);
4176     });
4177
4178     // methods to determine if an expression has a string result type
4179     (function(def) {
4180         def(AST_Node, return_false);
4181         def(AST_Assign, function(compressor) {
4182             switch (this.operator) {
4183               case "+=":
4184                 if (this.left.is_string(compressor)) return true;
4185               case "=":
4186                 return this.right.is_string(compressor);
4187             }
4188         });
4189         def(AST_Binary, function(compressor) {
4190             return this.operator == "+" &&
4191                 (this.left.is_string(compressor) || this.right.is_string(compressor));
4192         });
4193         var fn = makePredicate([
4194             "charAt",
4195             "substr",
4196             "substring",
4197             "toLowerCase",
4198             "toString",
4199             "toUpperCase",
4200             "trim",
4201         ]);
4202         def(AST_Call, function(compressor) {
4203             if (!compressor.option("unsafe")) return false;
4204             var exp = this.expression;
4205             return exp instanceof AST_Dot && fn[exp.property];
4206         });
4207         def(AST_Conditional, function(compressor) {
4208             return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
4209         });
4210         def(AST_Sequence, function(compressor) {
4211             return this.tail_node().is_string(compressor);
4212         });
4213         def(AST_String, return_true);
4214         def(AST_SymbolRef, function(compressor) {
4215             var fixed = this.fixed_value();
4216             if (!fixed) return false;
4217             this.is_string = return_false;
4218             var result = fixed.is_string(compressor);
4219             delete this.is_string;
4220             return result;
4221         });
4222         def(AST_Template, function(compressor) {
4223             return !this.tag || is_raw_tag(compressor, this.tag);
4224         });
4225         def(AST_UnaryPrefix, function() {
4226             return this.operator == "typeof";
4227         });
4228     })(function(node, func) {
4229         node.DEFMETHOD("is_string", func);
4230     });
4231
4232     var lazy_op = makePredicate("&& || ??");
4233
4234     (function(def) {
4235         function to_node(value, orig) {
4236             if (value instanceof AST_Node) return value.clone(true);
4237             if (Array.isArray(value)) return make_node(AST_Array, orig, {
4238                 elements: value.map(function(value) {
4239                     return to_node(value, orig);
4240                 })
4241             });
4242             if (value && typeof value == "object") {
4243                 var props = [];
4244                 for (var key in value) if (HOP(value, key)) {
4245                     props.push(make_node(AST_ObjectKeyVal, orig, {
4246                         key: key,
4247                         value: to_node(value[key], orig)
4248                     }));
4249                 }
4250                 return make_node(AST_Object, orig, {
4251                     properties: props
4252                 });
4253             }
4254             return make_node_from_constant(value, orig);
4255         }
4256
4257         function warn(node) {
4258             AST_Node.warn("global_defs {node} redefined [{file}:{line},{col}]", {
4259                 node: node,
4260                 file: node.start.file,
4261                 line: node.start.line,
4262                 col: node.start.col,
4263             });
4264         }
4265
4266         AST_Toplevel.DEFMETHOD("resolve_defines", function(compressor) {
4267             if (!compressor.option("global_defs")) return this;
4268             this.figure_out_scope({ ie: compressor.option("ie") });
4269             return this.transform(new TreeTransformer(function(node) {
4270                 var def = node._find_defs(compressor, "");
4271                 if (!def) return;
4272                 var level = 0, child = node, parent;
4273                 while (parent = this.parent(level++)) {
4274                     if (!(parent instanceof AST_PropAccess)) break;
4275                     if (parent.expression !== child) break;
4276                     child = parent;
4277                 }
4278                 if (is_lhs(child, parent)) {
4279                     warn(node);
4280                     return;
4281                 }
4282                 return def;
4283             }));
4284         });
4285         def(AST_Node, noop);
4286         def(AST_Dot, function(compressor, suffix) {
4287             return this.expression._find_defs(compressor, "." + this.property + suffix);
4288         });
4289         def(AST_SymbolDeclaration, function(compressor) {
4290             if (!this.definition().global) return;
4291             if (HOP(compressor.option("global_defs"), this.name)) warn(this);
4292         });
4293         def(AST_SymbolRef, function(compressor, suffix) {
4294             if (!this.definition().global) return;
4295             var defines = compressor.option("global_defs");
4296             var name = this.name + suffix;
4297             if (HOP(defines, name)) return to_node(defines[name], this);
4298         });
4299     })(function(node, func) {
4300         node.DEFMETHOD("_find_defs", func);
4301     });
4302
4303     function best_of_expression(ast1, ast2, threshold) {
4304         var delta = ast2.print_to_string().length - ast1.print_to_string().length;
4305         return delta < (threshold || 0) ? ast2 : ast1;
4306     }
4307
4308     function best_of_statement(ast1, ast2, threshold) {
4309         return best_of_expression(make_node(AST_SimpleStatement, ast1, {
4310             body: ast1
4311         }), make_node(AST_SimpleStatement, ast2, {
4312             body: ast2
4313         }), threshold).body;
4314     }
4315
4316     function best_of(compressor, ast1, ast2, threshold) {
4317         return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2, threshold);
4318     }
4319
4320     function convert_to_predicate(obj) {
4321         var map = Object.create(null);
4322         Object.keys(obj).forEach(function(key) {
4323             map[key] = makePredicate(obj[key]);
4324         });
4325         return map;
4326     }
4327
4328     function skip_directives(body) {
4329         for (var i = 0; i < body.length; i++) {
4330             var stat = body[i];
4331             if (!(stat instanceof AST_Directive)) return stat;
4332         }
4333     }
4334
4335     function arrow_first_statement() {
4336         if (this.value) return make_node(AST_Return, this.value, {
4337             value: this.value
4338         });
4339         return skip_directives(this.body);
4340     }
4341     AST_Arrow.DEFMETHOD("first_statement", arrow_first_statement);
4342     AST_AsyncArrow.DEFMETHOD("first_statement", arrow_first_statement);
4343     AST_Lambda.DEFMETHOD("first_statement", function() {
4344         return skip_directives(this.body);
4345     });
4346
4347     AST_Lambda.DEFMETHOD("length", function() {
4348         var argnames = this.argnames;
4349         for (var i = 0; i < argnames.length; i++) {
4350             if (argnames[i] instanceof AST_DefaultValue) break;
4351         }
4352         return i;
4353     });
4354
4355     function try_evaluate(compressor, node) {
4356         var ev = node.evaluate(compressor);
4357         if (ev === node) return node;
4358         ev = make_node_from_constant(ev, node).optimize(compressor);
4359         return best_of(compressor, node, ev, compressor.eval_threshold);
4360     }
4361
4362     var object_fns = [
4363         "constructor",
4364         "toString",
4365         "valueOf",
4366     ];
4367     var native_fns = convert_to_predicate({
4368         Array: [
4369             "indexOf",
4370             "join",
4371             "lastIndexOf",
4372             "slice",
4373         ].concat(object_fns),
4374         Boolean: object_fns,
4375         Function: object_fns,
4376         Number: [
4377             "toExponential",
4378             "toFixed",
4379             "toPrecision",
4380         ].concat(object_fns),
4381         Object: object_fns,
4382         RegExp: [
4383             "exec",
4384             "test",
4385         ].concat(object_fns),
4386         String: [
4387             "charAt",
4388             "charCodeAt",
4389             "concat",
4390             "indexOf",
4391             "italics",
4392             "lastIndexOf",
4393             "match",
4394             "replace",
4395             "search",
4396             "slice",
4397             "split",
4398             "substr",
4399             "substring",
4400             "toLowerCase",
4401             "toUpperCase",
4402             "trim",
4403         ].concat(object_fns),
4404     });
4405     var static_fns = convert_to_predicate({
4406         Array: [
4407             "isArray",
4408         ],
4409         Math: [
4410             "abs",
4411             "acos",
4412             "asin",
4413             "atan",
4414             "ceil",
4415             "cos",
4416             "exp",
4417             "floor",
4418             "log",
4419             "round",
4420             "sin",
4421             "sqrt",
4422             "tan",
4423             "atan2",
4424             "pow",
4425             "max",
4426             "min",
4427         ],
4428         Number: [
4429             "isFinite",
4430             "isNaN",
4431         ],
4432         Object: [
4433             "create",
4434             "getOwnPropertyDescriptor",
4435             "getOwnPropertyNames",
4436             "getPrototypeOf",
4437             "isExtensible",
4438             "isFrozen",
4439             "isSealed",
4440             "keys",
4441         ],
4442         String: [
4443             "fromCharCode",
4444             "raw",
4445         ],
4446     });
4447
4448     function is_static_fn(node) {
4449         if (!(node instanceof AST_Dot)) return false;
4450         var expr = node.expression;
4451         if (!is_undeclared_ref(expr)) return false;
4452         var static_fn = static_fns[expr.name];
4453         return static_fn && (static_fn[node.property] || expr.name == "Math" && node.property == "random");
4454     }
4455
4456     // Accomodate when compress option evaluate=false
4457     // as well as the common constant expressions !0 and -1
4458     (function(def) {
4459         def(AST_Node, return_false);
4460         def(AST_Constant, return_true);
4461         def(AST_RegExp, return_false);
4462         var unaryPrefix = makePredicate("! ~ - + void");
4463         def(AST_UnaryPrefix, function() {
4464             return unaryPrefix[this.operator] && this.expression instanceof AST_Constant;
4465         });
4466     })(function(node, func) {
4467         node.DEFMETHOD("is_constant", func);
4468     });
4469
4470     // methods to evaluate a constant expression
4471     (function(def) {
4472         // If the node has been successfully reduced to a constant,
4473         // then its value is returned; otherwise the element itself
4474         // is returned.
4475         //
4476         // They can be distinguished as constant value is never a
4477         // descendant of AST_Node.
4478         //
4479         // When `ignore_side_effects` is `true`, inspect the constant value
4480         // produced without worrying about any side effects caused by said
4481         // expression.
4482         AST_Node.DEFMETHOD("evaluate", function(compressor, ignore_side_effects) {
4483             if (!compressor.option("evaluate")) return this;
4484             var cached = [];
4485             var val = this._eval(compressor, ignore_side_effects, cached, 1);
4486             cached.forEach(function(node) {
4487                 delete node._eval;
4488             });
4489             if (ignore_side_effects) return val;
4490             if (!val || val instanceof RegExp) return val;
4491             if (typeof val == "function" || typeof val == "object") return this;
4492             return val;
4493         });
4494         var scan_modified = new TreeWalker(function(node) {
4495             if (node instanceof AST_Assign) modified(node.left);
4496             if (node instanceof AST_Unary && UNARY_POSTFIX[node.operator]) modified(node.expression);
4497         });
4498         function modified(node) {
4499             if (node instanceof AST_DestructuredArray) {
4500                 node.elements.forEach(modified);
4501             } else if (node instanceof AST_DestructuredObject) {
4502                 node.properties.forEach(function(prop) {
4503                     modified(prop.value);
4504                 });
4505             } else if (node instanceof AST_PropAccess) {
4506                 modified(node.expression);
4507             } else if (node instanceof AST_SymbolRef) {
4508                 node.definition().references.forEach(function(ref) {
4509                     delete ref._eval;
4510                 });
4511             }
4512         }
4513         def(AST_Statement, function() {
4514             throw new Error(string_template("Cannot evaluate a statement [{file}:{line},{col}]", this.start));
4515         });
4516         def(AST_Accessor, return_this);
4517         def(AST_BigInt, return_this);
4518         def(AST_Class, return_this);
4519         def(AST_Node, return_this);
4520         def(AST_Constant, function() {
4521             return this.value;
4522         });
4523         def(AST_Assign, function(compressor, ignore_side_effects, cached, depth) {
4524             var lhs = this.left;
4525             if (!ignore_side_effects) {
4526                 if (!(lhs instanceof AST_SymbolRef)) return this;
4527                 if (!HOP(lhs, "_eval")) {
4528                     if (!lhs.fixed) return this;
4529                     var def = lhs.definition();
4530                     if (!def.fixed) return this;
4531                     if (def.undeclared) return this;
4532                     if (def.last_ref !== lhs) return this;
4533                     if (def.single_use == "m") return this;
4534                 }
4535             }
4536             var op = this.operator;
4537             var node;
4538             if (!HOP(lhs, "_eval") && lhs instanceof AST_SymbolRef && lhs.fixed && lhs.definition().fixed) {
4539                 node = lhs;
4540             } else if (op == "=") {
4541                 node = this.right;
4542             } else {
4543                 node = make_node(AST_Binary, this, {
4544                     operator: op.slice(0, -1),
4545                     left: lhs,
4546                     right: this.right,
4547                 });
4548             }
4549             lhs.walk(scan_modified);
4550             var value = node._eval(compressor, ignore_side_effects, cached, depth);
4551             if (typeof value == "object") return this;
4552             modified(lhs);
4553             return value;
4554         });
4555         def(AST_Sequence, function(compressor, ignore_side_effects, cached, depth) {
4556             if (!ignore_side_effects) return this;
4557             var exprs = this.expressions;
4558             for (var i = 0, last = exprs.length - 1; i < last; i++) {
4559                 exprs[i].walk(scan_modified);
4560             }
4561             var tail = exprs[last];
4562             var value = tail._eval(compressor, ignore_side_effects, cached, depth);
4563             return value === tail ? this : value;
4564         });
4565         def(AST_Lambda, function(compressor) {
4566             if (compressor.option("unsafe")) {
4567                 var fn = function() {};
4568                 fn.node = this;
4569                 fn.toString = function() {
4570                     return "function(){}";
4571                 };
4572                 return fn;
4573             }
4574             return this;
4575         });
4576         def(AST_Array, function(compressor, ignore_side_effects, cached, depth) {
4577             if (compressor.option("unsafe")) {
4578                 var elements = [];
4579                 for (var i = 0; i < this.elements.length; i++) {
4580                     var element = this.elements[i];
4581                     if (element instanceof AST_Hole) return this;
4582                     var value = element._eval(compressor, ignore_side_effects, cached, depth);
4583                     if (element === value) return this;
4584                     elements.push(value);
4585                 }
4586                 return elements;
4587             }
4588             return this;
4589         });
4590         var nonsafe_props = makePredicate("__proto__ toString valueOf");
4591         def(AST_Object, function(compressor, ignore_side_effects, cached, depth) {
4592             if (compressor.option("unsafe")) {
4593                 var val = {};
4594                 for (var i = 0; i < this.properties.length; i++) {
4595                     var prop = this.properties[i];
4596                     if (!(prop instanceof AST_ObjectKeyVal)) return this;
4597                     var key = prop.key;
4598                     if (key instanceof AST_Node) {
4599                         key = key._eval(compressor, ignore_side_effects, cached, depth);
4600                         if (key === prop.key) return this;
4601                     }
4602                     if (nonsafe_props[key]) return this;
4603                     val[key] = prop.value._eval(compressor, ignore_side_effects, cached, depth);
4604                     if (val[key] === prop.value) return this;
4605                 }
4606                 return val;
4607             }
4608             return this;
4609         });
4610         var non_converting_unary = makePredicate("! typeof void");
4611         def(AST_UnaryPrefix, function(compressor, ignore_side_effects, cached, depth) {
4612             var e = this.expression;
4613             var op = this.operator;
4614             // Function would be evaluated to an array and so typeof would
4615             // incorrectly return "object". Hence making is a special case.
4616             if (compressor.option("typeofs")
4617                 && op == "typeof"
4618                 && (e instanceof AST_Lambda
4619                     || e instanceof AST_SymbolRef
4620                         && e.fixed_value() instanceof AST_Lambda)) {
4621                 return typeof function(){};
4622             }
4623             var def = e instanceof AST_SymbolRef && e.definition();
4624             if (!non_converting_unary[op] && !(def && def.fixed)) depth++;
4625             e.walk(scan_modified);
4626             var v = e._eval(compressor, ignore_side_effects, cached, depth);
4627             if (v === e) {
4628                 if (ignore_side_effects && op == "void") return;
4629                 return this;
4630             }
4631             switch (op) {
4632               case "!": return !v;
4633               case "typeof":
4634                 // typeof <RegExp> returns "object" or "function" on different platforms
4635                 // so cannot evaluate reliably
4636                 if (v instanceof RegExp) return this;
4637                 return typeof v;
4638               case "void": return;
4639               case "~": return ~v;
4640               case "-": return -v;
4641               case "+": return +v;
4642               case "++":
4643               case "--":
4644                 if (!def) return this;
4645                 if (!ignore_side_effects) {
4646                     if (def.undeclared) return this;
4647                     if (def.last_ref !== e) return this;
4648                 }
4649                 if (HOP(e, "_eval")) v = +(op[0] + 1) + +v;
4650                 modified(e);
4651                 return v;
4652             }
4653             return this;
4654         });
4655         def(AST_UnaryPostfix, function(compressor, ignore_side_effects, cached, depth) {
4656             var e = this.expression;
4657             if (!(e instanceof AST_SymbolRef)) {
4658                 if (!ignore_side_effects) return this;
4659             } else if (!HOP(e, "_eval")) {
4660                 if (!e.fixed) return this;
4661                 if (!ignore_side_effects) {
4662                     var def = e.definition();
4663                     if (!def.fixed) return this;
4664                     if (def.undeclared) return this;
4665                     if (def.last_ref !== e) return this;
4666                 }
4667             }
4668             if (!(e instanceof AST_SymbolRef && e.definition().fixed)) depth++;
4669             e.walk(scan_modified);
4670             var v = e._eval(compressor, ignore_side_effects, cached, depth);
4671             if (v === e) return this;
4672             modified(e);
4673             return +v;
4674         });
4675         var non_converting_binary = makePredicate("&& || === !==");
4676         def(AST_Binary, function(compressor, ignore_side_effects, cached, depth) {
4677             if (!non_converting_binary[this.operator]) depth++;
4678             var left = this.left._eval(compressor, ignore_side_effects, cached, depth);
4679             if (left === this.left) return this;
4680             if (this.operator == (left ? "||" : "&&")) return left;
4681             var rhs_ignore_side_effects = ignore_side_effects && !(left && typeof left == "object");
4682             var right = this.right._eval(compressor, rhs_ignore_side_effects, cached, depth);
4683             if (right === this.right) return this;
4684             var result;
4685             switch (this.operator) {
4686               case "&&" : result = left &&  right; break;
4687               case "||" : result = left ||  right; break;
4688               case "??" :
4689                 result = left == null ? right : left;
4690                 break;
4691               case "|"  : result = left |   right; break;
4692               case "&"  : result = left &   right; break;
4693               case "^"  : result = left ^   right; break;
4694               case "+"  : result = left +   right; break;
4695               case "-"  : result = left -   right; break;
4696               case "*"  : result = left *   right; break;
4697               case "/"  : result = left /   right; break;
4698               case "%"  : result = left %   right; break;
4699               case "<<" : result = left <<  right; break;
4700               case ">>" : result = left >>  right; break;
4701               case ">>>": result = left >>> right; break;
4702               case "==" : result = left ==  right; break;
4703               case "===": result = left === right; break;
4704               case "!=" : result = left !=  right; break;
4705               case "!==": result = left !== right; break;
4706               case "<"  : result = left <   right; break;
4707               case "<=" : result = left <=  right; break;
4708               case ">"  : result = left >   right; break;
4709               case ">=" : result = left >=  right; break;
4710               case "**":
4711                 result = Math.pow(left, right);
4712                 break;
4713               case "in":
4714                 if (right && typeof right == "object" && HOP(right, left)) {
4715                     result = true;
4716                     break;
4717                 }
4718               default:
4719                 return this;
4720             }
4721             if (isNaN(result)) return compressor.find_parent(AST_With) ? this : result;
4722             if (compressor.option("unsafe_math")
4723                 && !ignore_side_effects
4724                 && result
4725                 && typeof result == "number"
4726                 && (this.operator == "+" || this.operator == "-")) {
4727                 var digits = Math.max(0, decimals(left), decimals(right));
4728                 // 53-bit significand ---> 15.95 decimal places
4729                 if (digits < 16) return +result.toFixed(digits);
4730             }
4731             return result;
4732
4733             function decimals(operand) {
4734                 var match = /(\.[0-9]*)?(e.+)?$/.exec(+operand);
4735                 return (match[1] || ".").length - 1 - (match[2] || "").slice(1);
4736             }
4737         });
4738         def(AST_Conditional, function(compressor, ignore_side_effects, cached, depth) {
4739             var condition = this.condition._eval(compressor, ignore_side_effects, cached, depth);
4740             if (condition === this.condition) return this;
4741             var node = condition ? this.consequent : this.alternative;
4742             var value = node._eval(compressor, ignore_side_effects, cached, depth);
4743             return value === node ? this : value;
4744         });
4745         function verify_escaped(ref, depth) {
4746             var escaped = ref.definition().escaped;
4747             switch (escaped.length) {
4748               case 0:
4749                 return true;
4750               case 1:
4751                 var found = false;
4752                 escaped[0].walk(new TreeWalker(function(node) {
4753                     if (found) return true;
4754                     if (node === ref) return found = true;
4755                     if (node instanceof AST_Scope) return true;
4756                 }));
4757                 return found;
4758               default:
4759                 return depth <= escaped.depth;
4760             }
4761         }
4762         def(AST_SymbolRef, function(compressor, ignore_side_effects, cached, depth) {
4763             var fixed = this.fixed_value();
4764             if (!fixed) return this;
4765             var value;
4766             if (HOP(fixed, "_eval")) {
4767                 value = fixed._eval();
4768             } else {
4769                 this._eval = return_this;
4770                 value = fixed._eval(compressor, ignore_side_effects, cached, depth);
4771                 delete this._eval;
4772                 if (value === fixed) return this;
4773                 fixed._eval = function() {
4774                     return value;
4775                 };
4776                 cached.push(fixed);
4777             }
4778             return value && typeof value == "object" && !verify_escaped(this, depth) ? this : value;
4779         });
4780         var global_objs = {
4781             Array: Array,
4782             Math: Math,
4783             Number: Number,
4784             Object: Object,
4785             String: String,
4786         };
4787         var static_values = convert_to_predicate({
4788             Math: [
4789                 "E",
4790                 "LN10",
4791                 "LN2",
4792                 "LOG2E",
4793                 "LOG10E",
4794                 "PI",
4795                 "SQRT1_2",
4796                 "SQRT2",
4797             ],
4798             Number: [
4799                 "MAX_VALUE",
4800                 "MIN_VALUE",
4801                 "NaN",
4802                 "NEGATIVE_INFINITY",
4803                 "POSITIVE_INFINITY",
4804             ],
4805         });
4806         var regexp_props = makePredicate("global ignoreCase multiline source");
4807         def(AST_PropAccess, function(compressor, ignore_side_effects, cached, depth) {
4808             if (compressor.option("unsafe")) {
4809                 var val;
4810                 var exp = this.expression;
4811                 if (!is_undeclared_ref(exp)) {
4812                     val = exp._eval(compressor, ignore_side_effects, cached, depth + 1);
4813                     if (val == null || val === exp) return this;
4814                 }
4815                 var key = this.property;
4816                 if (key instanceof AST_Node) {
4817                     key = key._eval(compressor, ignore_side_effects, cached, depth);
4818                     if (key === this.property) return this;
4819                 }
4820                 if (val === undefined) {
4821                     var static_value = static_values[exp.name];
4822                     if (!static_value || !static_value[key]) return this;
4823                     val = global_objs[exp.name];
4824                 } else if (val instanceof RegExp) {
4825                     if (!regexp_props[key]) return this;
4826                 } else if (typeof val == "object") {
4827                     if (!HOP(val, key)) return this;
4828                 } else if (typeof val == "function") switch (key) {
4829                   case "name":
4830                     return val.node.name ? val.node.name.name : "";
4831                   case "length":
4832                     return val.node.length();
4833                   default:
4834                     return this;
4835                 }
4836                 return val[key];
4837             }
4838             return this;
4839         });
4840         function eval_all(nodes, compressor, ignore_side_effects, cached, depth) {
4841             var values = [];
4842             for (var i = 0; i < nodes.length; i++) {
4843                 var node = nodes[i];
4844                 var value = node._eval(compressor, ignore_side_effects, cached, depth);
4845                 if (node === value) return;
4846                 values.push(value);
4847             }
4848             return values;
4849         }
4850         def(AST_Call, function(compressor, ignore_side_effects, cached, depth) {
4851             var exp = this.expression;
4852             var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
4853             if (fn instanceof AST_Arrow || fn instanceof AST_Defun || fn instanceof AST_Function) {
4854                 if (fn.evaluating) return this;
4855                 if (fn.name && fn.name.definition().recursive_refs > 0) return this;
4856                 if (this.is_expr_pure(compressor)) return this;
4857                 var args = eval_all(this.args, compressor, ignore_side_effects, cached, depth);
4858                 if (!all(fn.argnames, function(sym, index) {
4859                     if (sym instanceof AST_DefaultValue) {
4860                         if (!args) return false;
4861                         if (args[index] === undefined) {
4862                             var value = sym.value._eval(compressor, ignore_side_effects, cached, depth);
4863                             if (value === sym.value) return false;
4864                             args[index] = value;
4865                         }
4866                         sym = sym.name;
4867                     }
4868                     return !(sym instanceof AST_Destructured);
4869                 })) return this;
4870                 if (fn.rest instanceof AST_Destructured) return this;
4871                 if (!args && !ignore_side_effects) return this;
4872                 var stat = fn.first_statement();
4873                 if (!(stat instanceof AST_Return)) {
4874                     if (ignore_side_effects) {
4875                         fn.walk(scan_modified);
4876                         var found = false;
4877                         fn.evaluating = true;
4878                         walk_body(fn, new TreeWalker(function(node) {
4879                             if (found) return true;
4880                             if (node instanceof AST_Return) {
4881                                 if (node.value && node.value._eval(compressor, true, cached, depth) !== undefined) {
4882                                     found = true;
4883                                 }
4884                                 return true;
4885                             }
4886                             if (node instanceof AST_Scope && node !== fn) return true;
4887                         }));
4888                         delete fn.evaluating;
4889                         if (!found) return;
4890                     }
4891                     return this;
4892                 }
4893                 var val = stat.value;
4894                 if (!val) return;
4895                 var cached_args = [];
4896                 if (!args || all(fn.argnames, function(sym, i) {
4897                     return assign(sym, args[i]);
4898                 }) && !(fn.rest && !assign(fn.rest, args.slice(fn.argnames.length))) || ignore_side_effects) {
4899                     if (ignore_side_effects) fn.argnames.forEach(function(sym) {
4900                         if (sym instanceof AST_DefaultValue) sym.value.walk(scan_modified);
4901                     });
4902                     fn.evaluating = true;
4903                     val = val._eval(compressor, ignore_side_effects, cached, depth);
4904                     delete fn.evaluating;
4905                 }
4906                 cached_args.forEach(function(node) {
4907                     delete node._eval;
4908                 });
4909                 return val === stat.value ? this : val;
4910             } else if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
4911                 var key = exp.property;
4912                 if (key instanceof AST_Node) {
4913                     key = key._eval(compressor, ignore_side_effects, cached, depth);
4914                     if (key === exp.property) return this;
4915                 }
4916                 var val;
4917                 var e = exp.expression;
4918                 if (is_undeclared_ref(e)) {
4919                     var static_fn = static_fns[e.name];
4920                     if (!static_fn || !static_fn[key]) return this;
4921                     val = global_objs[e.name];
4922                 } else {
4923                     val = e._eval(compressor, ignore_side_effects, cached, depth + 1);
4924                     if (val == null || val === e) return this;
4925                     var native_fn = native_fns[val.constructor.name];
4926                     if (!native_fn || !native_fn[key]) return this;
4927                     if (val instanceof RegExp && val.global && !(e instanceof AST_RegExp)) return this;
4928                 }
4929                 var args = eval_all(this.args, compressor, ignore_side_effects, cached, depth);
4930                 if (!args) return this;
4931                 if (key == "replace" && typeof args[1] == "function") return this;
4932                 try {
4933                     return val[key].apply(val, args);
4934                 } catch (ex) {
4935                     AST_Node.warn("Error evaluating {code} [{file}:{line},{col}]", {
4936                         code: this,
4937                         file: this.start.file,
4938                         line: this.start.line,
4939                         col: this.start.col,
4940                     });
4941                 } finally {
4942                     if (val instanceof RegExp) val.lastIndex = 0;
4943                 }
4944             }
4945             return this;
4946
4947             function assign(sym, arg) {
4948                 if (sym instanceof AST_DefaultValue) sym = sym.name;
4949                 var def = sym.definition();
4950                 if (def.orig[def.orig.length - 1] !== sym) return false;
4951                 var value = arg;
4952                 def.references.forEach(function(node) {
4953                     node._eval = function() {
4954                         return value;
4955                     };
4956                     cached_args.push(node);
4957                 });
4958                 return true;
4959             }
4960         });
4961         def(AST_New, return_this);
4962         def(AST_Template, function(compressor, ignore_side_effects, cached, depth) {
4963             if (!compressor.option("templates")) return this;
4964             if (this.tag) {
4965                 if (!is_raw_tag(compressor, this.tag)) return this;
4966                 decode = function(str) {
4967                     return str;
4968                 };
4969             }
4970             var exprs = eval_all(this.expressions, compressor, ignore_side_effects, cached, depth);
4971             if (!exprs) return this;
4972             var malformed = false;
4973             var ret = decode(this.strings[0]);
4974             for (var i = 0; i < exprs.length; i++) {
4975                 ret += exprs[i] + decode(this.strings[i + 1]);
4976             }
4977             if (!malformed) return ret;
4978             this._eval = return_this;
4979             return this;
4980
4981             function decode(str) {
4982                 str = decode_template(str);
4983                 if (typeof str != "string") malformed = true;
4984                 return str;
4985             }
4986         });
4987     })(function(node, func) {
4988         node.DEFMETHOD("_eval", func);
4989     });
4990
4991     // method to negate an expression
4992     (function(def) {
4993         function basic_negation(exp) {
4994             return make_node(AST_UnaryPrefix, exp, {
4995                 operator: "!",
4996                 expression: exp
4997             });
4998         }
4999         function best(orig, alt, first_in_statement) {
5000             var negated = basic_negation(orig);
5001             if (first_in_statement) {
5002                 var stat = make_node(AST_SimpleStatement, alt, {
5003                     body: alt
5004                 });
5005                 return best_of_expression(negated, stat) === stat ? alt : negated;
5006             }
5007             return best_of_expression(negated, alt);
5008         }
5009         def(AST_Node, function() {
5010             return basic_negation(this);
5011         });
5012         def(AST_Statement, function() {
5013             throw new Error("Cannot negate a statement");
5014         });
5015         def(AST_Binary, function(compressor, first_in_statement) {
5016             var self = this.clone(), op = this.operator;
5017             if (compressor.option("unsafe_comps")) {
5018                 switch (op) {
5019                   case "<=" : self.operator = ">"  ; return self;
5020                   case "<"  : self.operator = ">=" ; return self;
5021                   case ">=" : self.operator = "<"  ; return self;
5022                   case ">"  : self.operator = "<=" ; return self;
5023                 }
5024             }
5025             switch (op) {
5026               case "==" : self.operator = "!="; return self;
5027               case "!=" : self.operator = "=="; return self;
5028               case "===": self.operator = "!=="; return self;
5029               case "!==": self.operator = "==="; return self;
5030               case "&&":
5031                 self.operator = "||";
5032                 self.left = self.left.negate(compressor, first_in_statement);
5033                 self.right = self.right.negate(compressor);
5034                 return best(this, self, first_in_statement);
5035               case "||":
5036                 self.operator = "&&";
5037                 self.left = self.left.negate(compressor, first_in_statement);
5038                 self.right = self.right.negate(compressor);
5039                 return best(this, self, first_in_statement);
5040             }
5041             return basic_negation(this);
5042         });
5043         def(AST_ClassExpression, function() {
5044             return basic_negation(this);
5045         });
5046         def(AST_Conditional, function(compressor, first_in_statement) {
5047             var self = this.clone();
5048             self.consequent = self.consequent.negate(compressor);
5049             self.alternative = self.alternative.negate(compressor);
5050             return best(this, self, first_in_statement);
5051         });
5052         def(AST_LambdaExpression, function() {
5053             return basic_negation(this);
5054         });
5055         def(AST_Sequence, function(compressor) {
5056             var expressions = this.expressions.slice();
5057             expressions.push(expressions.pop().negate(compressor));
5058             return make_sequence(this, expressions);
5059         });
5060         def(AST_UnaryPrefix, function() {
5061             if (this.operator == "!")
5062                 return this.expression;
5063             return basic_negation(this);
5064         });
5065     })(function(node, func) {
5066         node.DEFMETHOD("negate", function(compressor, first_in_statement) {
5067             return func.call(this, compressor, first_in_statement);
5068         });
5069     });
5070
5071     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");
5072     var global_pure_constructors = makePredicate("Map Set WeakMap WeakSet");
5073     AST_Call.DEFMETHOD("is_expr_pure", function(compressor) {
5074         if (compressor.option("unsafe")) {
5075             var expr = this.expression;
5076             if (is_undeclared_ref(expr)) {
5077                 if (global_pure_fns[expr.name]) return true;
5078                 if (this instanceof AST_New && global_pure_constructors[expr.name]) return true;
5079             }
5080             if (is_static_fn(expr)) return true;
5081         }
5082         return compressor.option("annotations") && this.pure || !compressor.pure_funcs(this);
5083     });
5084     AST_Template.DEFMETHOD("is_expr_pure", function(compressor) {
5085         var tag = this.tag;
5086         if (!tag) return true;
5087         if (compressor.option("unsafe")) {
5088             if (is_undeclared_ref(tag) && global_pure_fns[tag.name]) return true;
5089             if (tag instanceof AST_Dot && is_undeclared_ref(tag.expression)) {
5090                 var static_fn = static_fns[tag.expression.name];
5091                 return static_fn && (static_fn[tag.property]
5092                     || tag.expression.name == "Math" && tag.property == "random");
5093             }
5094         }
5095         return !compressor.pure_funcs(this);
5096     });
5097     AST_Node.DEFMETHOD("is_call_pure", return_false);
5098     AST_Call.DEFMETHOD("is_call_pure", function(compressor) {
5099         if (!compressor.option("unsafe")) return false;
5100         var dot = this.expression;
5101         if (!(dot instanceof AST_Dot)) return false;
5102         var exp = dot.expression;
5103         var map;
5104         var prop = dot.property;
5105         if (exp instanceof AST_Array) {
5106             map = native_fns.Array;
5107         } else if (exp.is_boolean(compressor)) {
5108             map = native_fns.Boolean;
5109         } else if (exp.is_number(compressor)) {
5110             map = native_fns.Number;
5111         } else if (exp instanceof AST_RegExp) {
5112             map = native_fns.RegExp;
5113         } else if (exp.is_string(compressor)) {
5114             map = native_fns.String;
5115             if (prop == "replace") {
5116                 var arg = this.args[1];
5117                 if (arg && !arg.is_string(compressor)) return false;
5118             }
5119         } else if (!dot.may_throw_on_access(compressor)) {
5120             map = native_fns.Object;
5121         }
5122         return map && map[prop];
5123     });
5124
5125     function spread_side_effects(exp) {
5126         while ((exp = exp.tail_node()) instanceof AST_SymbolRef) {
5127             exp = exp.fixed_value();
5128             if (!exp) return true;
5129         }
5130         return !(exp instanceof AST_Array
5131             || exp.TYPE == "Binary" && !lazy_op[exp.operator]
5132             || exp instanceof AST_Constant
5133             || exp instanceof AST_Lambda
5134             || exp instanceof AST_Object && all(exp.properties, function(prop) {
5135                 return !(prop instanceof AST_ObjectGetter || prop instanceof AST_Spread);
5136             })
5137             || exp instanceof AST_ObjectIdentity
5138             || exp instanceof AST_Unary);
5139     }
5140
5141     // determine if expression has side effects
5142     (function(def) {
5143         function any(list, compressor, spread) {
5144             return !all(list, spread ? function(node) {
5145                 return node instanceof AST_Spread ? !spread(node, compressor) : !node.has_side_effects(compressor);
5146             } : function(node) {
5147                 return !node.has_side_effects(compressor);
5148             });
5149         }
5150         function array_spread(node, compressor) {
5151             return !node.expression.is_string(compressor) || node.expression.has_side_effects(compressor);
5152         }
5153         def(AST_Node, return_true);
5154         def(AST_Array, function(compressor) {
5155             return any(this.elements, compressor, array_spread);
5156         });
5157         def(AST_Assign, function(compressor) {
5158             var lhs = this.left;
5159             if (!(lhs instanceof AST_PropAccess)) return true;
5160             var node = lhs.expression;
5161             return !(node instanceof AST_ObjectIdentity)
5162                 || !node.scope.resolve().new
5163                 || lhs instanceof AST_Sub && lhs.property.has_side_effects(compressor)
5164                 || this.right.has_side_effects(compressor);
5165         });
5166         def(AST_Binary, function(compressor) {
5167             return this.left.has_side_effects(compressor)
5168                 || this.right.has_side_effects(compressor)
5169                 || this.operator == "in" && !is_object(this.right);
5170         });
5171         def(AST_Block, function(compressor) {
5172             return any(this.body, compressor);
5173         });
5174         def(AST_Call, function(compressor) {
5175             if (!this.is_expr_pure(compressor)
5176                 && (!this.is_call_pure(compressor) || this.expression.has_side_effects(compressor))) {
5177                 return true;
5178             }
5179             return any(this.args, compressor, array_spread);
5180         });
5181         def(AST_Case, function(compressor) {
5182             return this.expression.has_side_effects(compressor)
5183                 || any(this.body, compressor);
5184         });
5185         def(AST_Class, function(compressor) {
5186             var base = this.extends;
5187             if (base) {
5188                 if (base instanceof AST_SymbolRef) base = base.fixed_value();
5189                 if (!safe_for_extends(base)) return true;
5190             }
5191             return any(this.properties, compressor);
5192         });
5193         def(AST_ClassProperty, function(compressor) {
5194             return this.key instanceof AST_Node && this.key.has_side_effects(compressor)
5195                 || this.static && this.value && this.value.has_side_effects(compressor);
5196         });
5197         def(AST_Conditional, function(compressor) {
5198             return this.condition.has_side_effects(compressor)
5199                 || this.consequent.has_side_effects(compressor)
5200                 || this.alternative.has_side_effects(compressor);
5201         });
5202         def(AST_Constant, return_false);
5203         def(AST_Definitions, function(compressor) {
5204             return any(this.definitions, compressor);
5205         });
5206         def(AST_DestructuredArray, function(compressor) {
5207             return any(this.elements, compressor);
5208         });
5209         def(AST_DestructuredKeyVal, function(compressor) {
5210             return this.key instanceof AST_Node && this.key.has_side_effects(compressor)
5211                 || this.value.has_side_effects(compressor);
5212         });
5213         def(AST_DestructuredObject, function(compressor) {
5214             return any(this.properties, compressor);
5215         });
5216         def(AST_Dot, function(compressor) {
5217             return !this.optional && this.expression.may_throw_on_access(compressor)
5218                 || this.expression.has_side_effects(compressor);
5219         });
5220         def(AST_EmptyStatement, return_false);
5221         def(AST_If, function(compressor) {
5222             return this.condition.has_side_effects(compressor)
5223                 || this.body && this.body.has_side_effects(compressor)
5224                 || this.alternative && this.alternative.has_side_effects(compressor);
5225         });
5226         def(AST_LabeledStatement, function(compressor) {
5227             return this.body.has_side_effects(compressor);
5228         });
5229         def(AST_Lambda, return_false);
5230         def(AST_Object, function(compressor) {
5231             return any(this.properties, compressor, function(node, compressor) {
5232                 var exp = node.expression;
5233                 return spread_side_effects(exp) || exp.has_side_effects(compressor);
5234             });
5235         });
5236         def(AST_ObjectIdentity, return_false);
5237         def(AST_ObjectProperty, function(compressor) {
5238             return this.key instanceof AST_Node && this.key.has_side_effects(compressor)
5239                 || this.value.has_side_effects(compressor);
5240         });
5241         def(AST_Sequence, function(compressor) {
5242             return any(this.expressions, compressor);
5243         });
5244         def(AST_SimpleStatement, function(compressor) {
5245             return this.body.has_side_effects(compressor);
5246         });
5247         def(AST_Sub, function(compressor) {
5248             return !this.optional && this.expression.may_throw_on_access(compressor)
5249                 || this.expression.has_side_effects(compressor)
5250                 || this.property.has_side_effects(compressor);
5251         });
5252         def(AST_Switch, function(compressor) {
5253             return this.expression.has_side_effects(compressor)
5254                 || any(this.body, compressor);
5255         });
5256         def(AST_SymbolDeclaration, return_false);
5257         def(AST_SymbolRef, function(compressor) {
5258             return !this.is_declared(compressor) || !can_drop_symbol(this, compressor);
5259         });
5260         def(AST_Template, function(compressor) {
5261             return !this.is_expr_pure(compressor) || any(this.expressions, compressor);
5262         });
5263         def(AST_Try, function(compressor) {
5264             return any(this.body, compressor)
5265                 || this.bcatch && this.bcatch.has_side_effects(compressor)
5266                 || this.bfinally && this.bfinally.has_side_effects(compressor);
5267         });
5268         def(AST_Unary, function(compressor) {
5269             return unary_side_effects[this.operator]
5270                 || this.expression.has_side_effects(compressor);
5271         });
5272         def(AST_VarDef, function() {
5273             return this.value;
5274         });
5275     })(function(node, func) {
5276         node.DEFMETHOD("has_side_effects", func);
5277     });
5278
5279     // determine if expression may throw
5280     (function(def) {
5281         def(AST_Node, return_true);
5282
5283         def(AST_Constant, return_false);
5284         def(AST_Destructured, return_true);
5285         def(AST_EmptyStatement, return_false);
5286         def(AST_Lambda, return_false);
5287         def(AST_ObjectIdentity, return_false);
5288         def(AST_SymbolDeclaration, return_false);
5289
5290         function any(list, compressor) {
5291             for (var i = list.length; --i >= 0;)
5292                 if (list[i].may_throw(compressor))
5293                     return true;
5294             return false;
5295         }
5296
5297         function call_may_throw(exp, compressor) {
5298             if (exp.may_throw(compressor)) return true;
5299             if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
5300             if (!(exp instanceof AST_Lambda)) return true;
5301             if (any(exp.argnames, compressor)) return true;
5302             if (any(exp.body, compressor)) return true;
5303             return is_arrow(exp) && exp.value && exp.value.may_throw(compressor);
5304         }
5305
5306         def(AST_Array, function(compressor) {
5307             return any(this.elements, compressor);
5308         });
5309         def(AST_Assign, function(compressor) {
5310             if (this.right.may_throw(compressor)) return true;
5311             if (!compressor.has_directive("use strict")
5312                 && this.operator == "="
5313                 && this.left instanceof AST_SymbolRef) {
5314                 return false;
5315             }
5316             return this.left.may_throw(compressor);
5317         });
5318         def(AST_Binary, function(compressor) {
5319             return this.left.may_throw(compressor)
5320                 || this.right.may_throw(compressor)
5321                 || this.operator == "in" && !is_object(this.right);
5322         });
5323         def(AST_Block, function(compressor) {
5324             return any(this.body, compressor);
5325         });
5326         def(AST_Call, function(compressor) {
5327             if (any(this.args, compressor)) return true;
5328             if (this.is_expr_pure(compressor)) return false;
5329             this.may_throw = return_true;
5330             var ret = call_may_throw(this.expression, compressor);
5331             delete this.may_throw;
5332             return ret;
5333         });
5334         def(AST_Case, function(compressor) {
5335             return this.expression.may_throw(compressor)
5336                 || any(this.body, compressor);
5337         });
5338         def(AST_Conditional, function(compressor) {
5339             return this.condition.may_throw(compressor)
5340                 || this.consequent.may_throw(compressor)
5341                 || this.alternative.may_throw(compressor);
5342         });
5343         def(AST_DefaultValue, function(compressor) {
5344             return this.name.may_throw(compressor)
5345                 || this.value && this.value.may_throw(compressor);
5346         });
5347         def(AST_Definitions, function(compressor) {
5348             return any(this.definitions, compressor);
5349         });
5350         def(AST_Dot, function(compressor) {
5351             return !this.optional && this.expression.may_throw_on_access(compressor)
5352                 || this.expression.may_throw(compressor);
5353         });
5354         def(AST_If, function(compressor) {
5355             return this.condition.may_throw(compressor)
5356                 || this.body && this.body.may_throw(compressor)
5357                 || this.alternative && this.alternative.may_throw(compressor);
5358         });
5359         def(AST_LabeledStatement, function(compressor) {
5360             return this.body.may_throw(compressor);
5361         });
5362         def(AST_Object, function(compressor) {
5363             return any(this.properties, compressor);
5364         });
5365         def(AST_ObjectProperty, function(compressor) {
5366             return this.value.may_throw(compressor)
5367                 || this.key instanceof AST_Node && this.key.may_throw(compressor);
5368         });
5369         def(AST_Return, function(compressor) {
5370             return this.value && this.value.may_throw(compressor);
5371         });
5372         def(AST_Sequence, function(compressor) {
5373             return any(this.expressions, compressor);
5374         });
5375         def(AST_SimpleStatement, function(compressor) {
5376             return this.body.may_throw(compressor);
5377         });
5378         def(AST_Sub, function(compressor) {
5379             return !this.optional && this.expression.may_throw_on_access(compressor)
5380                 || this.expression.may_throw(compressor)
5381                 || this.property.may_throw(compressor);
5382         });
5383         def(AST_Switch, function(compressor) {
5384             return this.expression.may_throw(compressor)
5385                 || any(this.body, compressor);
5386         });
5387         def(AST_SymbolRef, function(compressor) {
5388             return !this.is_declared(compressor) || !can_drop_symbol(this, compressor);
5389         });
5390         def(AST_Template, function(compressor) {
5391             if (any(this.expressions, compressor)) return true;
5392             if (this.is_expr_pure(compressor)) return false;
5393             if (!this.tag) return false;
5394             this.may_throw = return_true;
5395             var ret = call_may_throw(this.tag, compressor);
5396             delete this.may_throw;
5397             return ret;
5398         });
5399         def(AST_Try, function(compressor) {
5400             return (this.bcatch ? this.bcatch.may_throw(compressor) : any(this.body, compressor))
5401                 || this.bfinally && this.bfinally.may_throw(compressor);
5402         });
5403         def(AST_Unary, function(compressor) {
5404             return this.expression.may_throw(compressor)
5405                 && !(this.operator == "typeof" && this.expression instanceof AST_SymbolRef);
5406         });
5407         def(AST_VarDef, function(compressor) {
5408             return this.name.may_throw(compressor)
5409                 || this.value && this.value.may_throw(compressor);
5410         });
5411     })(function(node, func) {
5412         node.DEFMETHOD("may_throw", func);
5413     });
5414
5415     // determine if expression is constant
5416     (function(def) {
5417         function all_constant(list, scope) {
5418             for (var i = list.length; --i >= 0;)
5419                 if (!list[i].is_constant_expression(scope))
5420                     return false;
5421             return true;
5422         }
5423         def(AST_Node, return_false);
5424         def(AST_Array, function(scope) {
5425             return all_constant(this.elements, scope);
5426         });
5427         def(AST_Binary, function(scope) {
5428             return this.left.is_constant_expression(scope)
5429                 && this.right.is_constant_expression(scope)
5430                 && (this.operator != "in" || is_object(this.right));
5431         });
5432         def(AST_Class, function(scope) {
5433             var base = this.extends;
5434             if (base && !safe_for_extends(base)) return false;
5435             return all_constant(this.properties, scope);
5436         });
5437         def(AST_ClassProperty, function(scope) {
5438             return typeof this.key == "string" && (!this.value || this.value.is_constant_expression(scope));
5439         });
5440         def(AST_Constant, return_true);
5441         def(AST_Lambda, function(scope) {
5442             var self = this;
5443             var result = true;
5444             var scopes = [];
5445             self.walk(new TreeWalker(function(node, descend) {
5446                 if (!result) return true;
5447                 if (node instanceof AST_BlockScope) {
5448                     if (node === self) return;
5449                     scopes.push(node);
5450                     descend();
5451                     scopes.pop();
5452                     return true;
5453                 }
5454                 if (node instanceof AST_SymbolRef) {
5455                     if (self.inlined || node.redef) {
5456                         result = false;
5457                         return true;
5458                     }
5459                     if (self.variables.has(node.name)) return true;
5460                     var def = node.definition();
5461                     if (member(def.scope, scopes)) return true;
5462                     if (scope && !def.redefined()) {
5463                         var scope_def = scope.find_variable(node.name);
5464                         if (scope_def ? scope_def === def : def.undeclared) {
5465                             result = "f";
5466                             return true;
5467                         }
5468                     }
5469                     result = false;
5470                     return true;
5471                 }
5472                 if (node instanceof AST_ObjectIdentity) {
5473                     if (is_arrow(self) && all(scopes, function(s) {
5474                         return !(s instanceof AST_Scope) || is_arrow(s);
5475                     })) result = false;
5476                     return true;
5477                 }
5478             }));
5479             return result;
5480         });
5481         def(AST_Object, function(scope) {
5482             return all_constant(this.properties, scope);
5483         });
5484         def(AST_ObjectProperty, function(scope) {
5485             return typeof this.key == "string" && this.value.is_constant_expression(scope);
5486         });
5487         def(AST_Unary, function(scope) {
5488             return this.expression.is_constant_expression(scope);
5489         });
5490     })(function(node, func) {
5491         node.DEFMETHOD("is_constant_expression", func);
5492     });
5493
5494     // tell me if a statement aborts
5495     function aborts(thing) {
5496         return thing && thing.aborts();
5497     }
5498     (function(def) {
5499         def(AST_Statement, return_null);
5500         def(AST_Jump, return_this);
5501         function block_aborts() {
5502             var n = this.body.length;
5503             return n > 0 && aborts(this.body[n - 1]);
5504         }
5505         def(AST_BlockStatement, block_aborts);
5506         def(AST_SwitchBranch, block_aborts);
5507         def(AST_If, function() {
5508             return this.alternative && aborts(this.body) && aborts(this.alternative) && this;
5509         });
5510     })(function(node, func) {
5511         node.DEFMETHOD("aborts", func);
5512     });
5513
5514     /* -----[ optimizers ]----- */
5515
5516     var directives = makePredicate(["use asm", "use strict"]);
5517     OPT(AST_Directive, function(self, compressor) {
5518         if (compressor.option("directives")
5519             && (!directives[self.value] || compressor.has_directive(self.value) !== self)) {
5520             return make_node(AST_EmptyStatement, self);
5521         }
5522         return self;
5523     });
5524
5525     OPT(AST_Debugger, function(self, compressor) {
5526         if (compressor.option("drop_debugger"))
5527             return make_node(AST_EmptyStatement, self);
5528         return self;
5529     });
5530
5531     OPT(AST_LabeledStatement, function(self, compressor) {
5532         if (compressor.option("dead_code")
5533             && self.body instanceof AST_Break
5534             && compressor.loopcontrol_target(self.body) === self.body) {
5535             return make_node(AST_EmptyStatement, self);
5536         }
5537         return compressor.option("unused") && self.label.references.length == 0 ? self.body : self;
5538     });
5539
5540     OPT(AST_LoopControl, function(self, compressor) {
5541         if (!compressor.option("dead_code")) return self;
5542         var label = self.label;
5543         if (label) {
5544             var lct = compressor.loopcontrol_target(self);
5545             self.label = null;
5546             if (compressor.loopcontrol_target(self) === lct) {
5547                 remove(label.thedef.references, self);
5548             } else {
5549                 self.label = label;
5550             }
5551         }
5552         return self;
5553     });
5554
5555     OPT(AST_Block, function(self, compressor) {
5556         self.body = tighten_body(self.body, compressor);
5557         return self;
5558     });
5559
5560     function trim_block(node, parent, in_list) {
5561         switch (node.body.length) {
5562           case 0:
5563             return in_list ? List.skip : make_node(AST_EmptyStatement, node);
5564           case 1:
5565             var stat = node.body[0];
5566             if (!safe_to_trim(stat)) return node;
5567             if (parent instanceof AST_IterationStatement && stat instanceof AST_LambdaDefinition) return node;
5568             return stat;
5569         }
5570         return node;
5571     }
5572
5573     OPT(AST_BlockStatement, function(self, compressor) {
5574         self.body = tighten_body(self.body, compressor);
5575         return trim_block(self, compressor.parent());
5576     });
5577
5578     function drop_rest_farg(fn, compressor) {
5579         if (!compressor.option("rests")) return;
5580         if (fn.uses_arguments) return;
5581         if (!(fn.rest instanceof AST_DestructuredArray)) return;
5582         if (!compressor.drop_fargs(fn, compressor.parent())) return;
5583         fn.argnames = fn.argnames.concat(fn.rest.elements);
5584         fn.rest = fn.rest.rest;
5585     }
5586
5587     OPT(AST_Lambda, function(self, compressor) {
5588         drop_rest_farg(self, compressor);
5589         self.body = tighten_body(self.body, compressor);
5590         return self;
5591     });
5592
5593     function opt_arrow(self, compressor) {
5594         if (!compressor.option("arrows")) return self;
5595         drop_rest_farg(self, compressor);
5596         var body = tighten_body(self.value ? [ self.first_statement() ] : self.body, compressor);
5597         switch (body.length) {
5598           case 1:
5599             var stat = body[0];
5600             if (stat instanceof AST_Return) {
5601                 self.body.length = 0;
5602                 self.value = stat.value;
5603                 break;
5604             }
5605           default:
5606             self.body = body;
5607             self.value = null;
5608             break;
5609         }
5610         return self;
5611     }
5612     OPT(AST_Arrow, opt_arrow);
5613     OPT(AST_AsyncArrow, opt_arrow);
5614
5615     OPT(AST_Function, function(self, compressor) {
5616         drop_rest_farg(self, compressor);
5617         self.body = tighten_body(self.body, compressor);
5618         var parent = compressor.parent();
5619         if (compressor.option("inline")) for (var i = 0; i < self.body.length; i++) {
5620             var stat = self.body[i];
5621             if (stat instanceof AST_Directive) continue;
5622             if (stat instanceof AST_Return) {
5623                 if (i != self.body.length - 1) break;
5624                 var call = stat.value;
5625                 if (!call || call.TYPE != "Call") break;
5626                 if (call.is_expr_pure(compressor)) break;
5627                 var fn = call.expression;
5628                 if (fn instanceof AST_SymbolRef) {
5629                     if (self.name && self.name.definition() === fn.definition()) break;
5630                     fn = fn.fixed_value();
5631                 }
5632                 if (!(fn instanceof AST_Defun || fn instanceof AST_Function)) break;
5633                 if (fn.rest) break;
5634                 if (fn.uses_arguments) break;
5635                 if (fn === call.expression) {
5636                     if (fn.parent_scope !== self) break;
5637                     if (!all(fn.enclosed, function(def) {
5638                         return def.scope !== self;
5639                     })) break;
5640                 }
5641                 if (fn.name
5642                     && (parent instanceof AST_ClassMethod || parent instanceof AST_ObjectMethod)
5643                     && parent.value === compressor.self()) break;
5644                 if (fn.contains_this()) break;
5645                 var len = fn.argnames.length;
5646                 if (len > 0 && compressor.option("inline") < 2) break;
5647                 if (len > self.argnames.length) break;
5648                 if (!all(self.argnames, function(argname) {
5649                     return argname instanceof AST_SymbolFunarg;
5650                 })) break;
5651                 if (!all(call.args, function(arg) {
5652                     return !(arg instanceof AST_Spread);
5653                 })) break;
5654                 for (var j = 0; j < len; j++) {
5655                     var arg = call.args[j];
5656                     if (!(arg instanceof AST_SymbolRef)) break;
5657                     if (arg.definition() !== self.argnames[j].definition()) break;
5658                 }
5659                 if (j < len) break;
5660                 for (; j < call.args.length; j++) {
5661                     if (call.args[j].has_side_effects(compressor)) break;
5662                 }
5663                 if (j < call.args.length) break;
5664                 if (len < self.argnames.length && !compressor.drop_fargs(self, parent)) {
5665                     if (!compressor.drop_fargs(fn, call)) break;
5666                     do {
5667                         fn.argnames.push(fn.make_var(AST_SymbolFunarg, fn, "argument_" + len));
5668                     } while (++len < self.argnames.length);
5669                 }
5670                 return call.expression;
5671             }
5672             break;
5673         }
5674         return self;
5675     });
5676
5677     var NO_MERGE = makePredicate("arguments await yield");
5678     AST_Scope.DEFMETHOD("merge_variables", function(compressor) {
5679         if (!compressor.option("merge_vars")) return;
5680         var in_try, root, segment = {}, self = this;
5681         var first = [], last = [], index = 0;
5682         var declarations = new Dictionary();
5683         var references = Object.create(null);
5684         var prev = Object.create(null);
5685         var tw = new TreeWalker(function(node, descend) {
5686             if (node instanceof AST_Assign) {
5687                 var lhs = node.left;
5688                 var rhs = node.right;
5689                 if (lhs instanceof AST_Destructured) {
5690                     rhs.walk(tw);
5691                     var marker = new TreeWalker(function(node) {
5692                         if (node instanceof AST_Destructured) return;
5693                         if (node instanceof AST_DefaultValue) {
5694                             push();
5695                             node.value.walk(tw);
5696                             pop();
5697                             node.name.walk(marker);
5698                         } else if (node instanceof AST_DestructuredKeyVal) {
5699                             if (node.key instanceof AST_Node) {
5700                                 push();
5701                                 segment.block = node;
5702                                 node.key.walk(tw);
5703                                 node.value.walk(marker);
5704                                 pop();
5705                             } else {
5706                                 node.value.walk(marker);
5707                             }
5708                         } else if (node instanceof AST_SymbolRef) {
5709                             mark(node);
5710                         } else {
5711                             node.walk(tw);
5712                         }
5713                         return true;
5714                     });
5715                     lhs.walk(marker);
5716                     return true;
5717                 }
5718                 if (lazy_op[node.operator.slice(0, -1)]) {
5719                     lhs.walk(tw);
5720                     push();
5721                     rhs.walk(tw);
5722                     if (lhs instanceof AST_SymbolRef) mark(lhs);
5723                     pop();
5724                     return true;
5725                 }
5726                 if (lhs instanceof AST_SymbolRef) {
5727                     if (node.operator != "=") mark(lhs, true);
5728                     rhs.walk(tw);
5729                     mark(lhs);
5730                     return true;
5731                 }
5732                 return;
5733             }
5734             if (node instanceof AST_Binary) {
5735                 if (!lazy_op[node.operator]) return;
5736                 node.left.walk(tw);
5737                 push();
5738                 node.right.walk(tw);
5739                 pop();
5740                 return true;
5741             }
5742             if (node instanceof AST_Break) {
5743                 var target = tw.loopcontrol_target(node);
5744                 if (!(target instanceof AST_IterationStatement)) insert(target);
5745                 return true;
5746             }
5747             if (node instanceof AST_Call) {
5748                 var exp = node.expression;
5749                 var tail = exp.tail_node();
5750                 if (!is_lambda(tail)) {
5751                     descend();
5752                     return mark_expression(exp);
5753                 }
5754                 if (exp !== tail) exp.expressions.slice(0, -1).forEach(function(node) {
5755                     node.walk(tw);
5756                 });
5757                 node.args.forEach(function(arg) {
5758                     arg.walk(tw);
5759                 });
5760                 tail.walk(tw);
5761                 return true;
5762             }
5763             if (node instanceof AST_Conditional) {
5764                 node.condition.walk(tw);
5765                 push();
5766                 node.consequent.walk(tw);
5767                 pop();
5768                 push();
5769                 node.alternative.walk(tw);
5770                 pop();
5771                 return true;
5772             }
5773             if (node instanceof AST_Continue) {
5774                 var target = tw.loopcontrol_target(node);
5775                 if (target instanceof AST_Do) insert(target);
5776                 return true;
5777             }
5778             if (node instanceof AST_Do) {
5779                 push();
5780                 segment.block = node;
5781                 segment.loop = true;
5782                 var save = segment;
5783                 node.body.walk(tw);
5784                 if (segment.inserted === node) segment = save;
5785                 node.condition.walk(tw);
5786                 pop();
5787                 return true;
5788             }
5789             if (node instanceof AST_For) {
5790                 if (node.init) node.init.walk(tw);
5791                 push();
5792                 segment.block = node;
5793                 segment.loop = true;
5794                 if (node.condition) node.condition.walk(tw);
5795                 node.body.walk(tw);
5796                 if (node.step) node.step.walk(tw);
5797                 pop();
5798                 return true;
5799             }
5800             if (node instanceof AST_ForEnumeration) {
5801                 node.object.walk(tw);
5802                 push();
5803                 segment.block = node;
5804                 segment.loop = true;
5805                 node.init.walk(tw);
5806                 node.body.walk(tw);
5807                 pop();
5808                 return true;
5809             }
5810             if (node instanceof AST_If) {
5811                 node.condition.walk(tw);
5812                 push();
5813                 node.body.walk(tw);
5814                 pop();
5815                 if (node.alternative) {
5816                     push();
5817                     node.alternative.walk(tw);
5818                     pop();
5819                 }
5820                 return true;
5821             }
5822             if (node instanceof AST_LabeledStatement) {
5823                 push();
5824                 segment.block = node;
5825                 var save = segment;
5826                 node.body.walk(tw);
5827                 if (segment.inserted === node) segment = save;
5828                 pop();
5829                 return true;
5830             }
5831             if (node instanceof AST_Scope) {
5832                 push();
5833                 segment.block = node;
5834                 if (node === self) root = segment;
5835                 if (node instanceof AST_Lambda) {
5836                     if (node.name) references[node.name.definition().id] = false;
5837                     var marker = node.uses_arguments && !tw.has_directive("use strict") ? function(node) {
5838                         if (node instanceof AST_SymbolFunarg) references[node.definition().id] = false;
5839                     } : function(node) {
5840                         if (node instanceof AST_SymbolFunarg) mark(node);
5841                     };
5842                     var scanner = new TreeWalker(function(ref) {
5843                         if (ref instanceof AST_SymbolDeclaration) references[ref.definition().id] = false;
5844                         if (!(ref instanceof AST_SymbolRef)) return;
5845                         var def = ref.definition();
5846                         var ldef = node.variables.get(ref.name);
5847                         if (ldef && (ldef === def
5848                             || def.undeclared
5849                             || node.parent_scope.find_variable(ref.name) === def)) {
5850                             references[def.id] = false;
5851                             references[ldef.id] = false;
5852                         } else {
5853                             var save = segment;
5854                             pop();
5855                             mark(ref, true);
5856                             segment = save;
5857                         }
5858                         return true;
5859                     });
5860                     node.argnames.forEach(function(argname) {
5861                         argname.mark_symbol(marker, scanner);
5862                     });
5863                     if (node.rest) node.rest.mark_symbol(marker, scanner);
5864                 }
5865                 walk_lambda(node, tw);
5866                 pop();
5867                 return true;
5868             }
5869             if (node instanceof AST_Sub) {
5870                 var exp = node.expression;
5871                 if (node.optional) {
5872                     exp.walk(tw);
5873                     push();
5874                     node.property.walk(tw);
5875                     pop();
5876                 } else {
5877                     descend();
5878                 }
5879                 return mark_expression(exp);
5880             }
5881             if (node instanceof AST_Switch) {
5882                 node.expression.walk(tw);
5883                 var save = segment;
5884                 node.body.forEach(function(branch) {
5885                     if (branch instanceof AST_Default) return;
5886                     branch.expression.walk(tw);
5887                     if (save === segment) push();
5888                 });
5889                 segment = save;
5890                 node.body.forEach(function(branch) {
5891                     push();
5892                     segment.block = node;
5893                     var save = segment;
5894                     walk_body(branch, tw);
5895                     if (segment.inserted === node) segment = save;
5896                     pop();
5897                 });
5898                 return true;
5899             }
5900             if (node instanceof AST_SymbolConst || node instanceof AST_SymbolLet) {
5901                 references[node.definition().id] = false;
5902                 return true;
5903             }
5904             if (node instanceof AST_SymbolRef) {
5905                 mark(node, true);
5906                 return true;
5907             }
5908             if (node instanceof AST_Try) {
5909                 var save_try = in_try;
5910                 in_try = node;
5911                 var save = segment;
5912                 walk_body(node, tw);
5913                 segment = save;
5914                 if (node.bcatch) {
5915                     if (node.bcatch.argname) node.bcatch.argname.mark_symbol(function(node) {
5916                         if (node instanceof AST_SymbolCatch) {
5917                             var def = node.definition();
5918                             references[def.id] = false;
5919                             if (def = def.redefined()) references[def.id] = false;
5920                         }
5921                     }, tw);
5922                     if (node.bfinally || (in_try = save_try)) {
5923                         walk_body(node.bcatch, tw);
5924                     } else {
5925                         push();
5926                         walk_body(node.bcatch, tw);
5927                         pop();
5928                     }
5929                 }
5930                 in_try = save_try;
5931                 segment = save;
5932                 if (node.bfinally) node.bfinally.walk(tw);
5933                 return true;
5934             }
5935             if (node instanceof AST_Unary) {
5936                 if (!UNARY_POSTFIX[node.operator]) return;
5937                 var sym = node.expression;
5938                 if (!(sym instanceof AST_SymbolRef)) return;
5939                 mark(sym, true);
5940                 return true;
5941             }
5942             if (node instanceof AST_VarDef) {
5943                 var assigned = node.value;
5944                 if (assigned) {
5945                     assigned.walk(tw);
5946                 } else {
5947                     assigned = segment.block instanceof AST_ForEnumeration && segment.block.init === tw.parent();
5948                 }
5949                 node.name.mark_symbol(assigned ? function(node) {
5950                     if (!(node instanceof AST_SymbolDeclaration)) return;
5951                     if (node instanceof AST_SymbolVar) {
5952                         mark(node);
5953                     } else {
5954                         references[node.definition().id] = false;
5955                     }
5956                     return true;
5957                 } : function(node) {
5958                     if (!(node instanceof AST_SymbolDeclaration)) return;
5959                     var id = node.definition().id;
5960                     if (!(node instanceof AST_SymbolVar)) {
5961                         references[id] = false;
5962                     } else if (!(id in references)) {
5963                         declarations.add(id, node);
5964                     } else if (references[id]) {
5965                         references[id].push(node);
5966                     }
5967                     return true;
5968                 }, tw);
5969                 return true;
5970             }
5971             if (node instanceof AST_While) {
5972                 push();
5973                 segment.block = node;
5974                 segment.loop = true;
5975                 descend();
5976                 pop();
5977                 return true;
5978             }
5979
5980             function mark_expression(exp) {
5981                 if (compressor.option("ie")) {
5982                     var sym = root_expr(exp);
5983                     if (sym instanceof AST_SymbolRef) sym.walk(tw);
5984                 }
5985                 return true;
5986             }
5987         });
5988         tw.directives = Object.create(compressor.directives);
5989         self.walk(tw);
5990         var merged = Object.create(null);
5991         while (first.length && last.length) {
5992             var head = first.pop();
5993             var def = head.definition;
5994             if (!(def.id in prev)) continue;
5995             if (!references[def.id]) continue;
5996             var head_refs = {
5997                 start: references[def.id].start,
5998             };
5999             while (def.id in merged) def = merged[def.id];
6000             head_refs.end = references[def.id].end;
6001             var skipped = [];
6002             do {
6003                 var tail = last.pop();
6004                 if (!tail) continue;
6005                 if (tail.index > head.index) continue;
6006                 var id = tail.definition.id;
6007                 var tail_refs = references[id];
6008                 if (!tail_refs) continue;
6009                 if (head_refs.start.block !== tail_refs.start.block
6010                     || !mergeable(head_refs, tail_refs)
6011                     || (head_refs.start.loop || !same_scope(def)) && !mergeable(tail_refs, head_refs)
6012                     || compressor.option("webkit") && is_funarg(def) !== is_funarg(tail.definition)
6013                     || !all(tail_refs, function(sym) {
6014                         return sym.scope.find_variable(def.name) === def;
6015                     })) {
6016                     skipped.unshift(tail);
6017                     continue;
6018                 }
6019                 var orig = [], refs = [];
6020                 tail_refs.forEach(function(sym) {
6021                     sym.thedef = def;
6022                     sym.name = def.name;
6023                     if (sym instanceof AST_SymbolRef) {
6024                         refs.push(sym);
6025                     } else {
6026                         orig.push(sym);
6027                     }
6028                 });
6029                 def.orig = orig.concat(def.orig);
6030                 def.references = refs.concat(def.references);
6031                 def.fixed = tail.definition.fixed && def.fixed;
6032                 merged[id] = def;
6033                 break;
6034             } while (last.length);
6035             if (skipped.length) last = last.concat(skipped);
6036         }
6037
6038         function push() {
6039             segment = Object.create(segment);
6040         }
6041
6042         function pop() {
6043             segment = Object.getPrototypeOf(segment);
6044         }
6045
6046         function mark(sym, read) {
6047             var def = sym.definition(), ldef, seg = segment;
6048             if (in_try) {
6049                 push();
6050                 seg = segment;
6051                 pop();
6052             }
6053             if (def.id in references) {
6054                 var refs = references[def.id];
6055                 if (!refs) return;
6056                 if (refs.start.block !== seg.block) return references[def.id] = false;
6057                 refs.push(sym);
6058                 refs.end = seg;
6059                 if (def.id in prev) {
6060                     last[prev[def.id]] = null;
6061                 } else if (!read) {
6062                     return;
6063                 }
6064             } else if ((ldef = self.variables.get(def.name)) !== def) {
6065                 if (ldef && root === seg) references[ldef.id] = false;
6066                 return references[def.id] = false;
6067             } else if (compressor.exposed(def) || NO_MERGE[sym.name]) {
6068                 return references[def.id] = false;
6069             } else {
6070                 var refs = declarations.get(def.id) || [];
6071                 refs.push(sym);
6072                 references[def.id] = refs;
6073                 if (!read) {
6074                     refs.start = seg;
6075                     return first.push({
6076                         index: index++,
6077                         definition: def,
6078                     });
6079                 }
6080                 if (seg.block !== self) return references[def.id] = false;
6081                 refs.start = root;
6082             }
6083             prev[def.id] = last.length;
6084             last.push({
6085                 index: index++,
6086                 definition: def,
6087             });
6088         }
6089
6090         function insert(target) {
6091             var stack = [];
6092             while (true) {
6093                 if (HOP(segment, "block")) {
6094                     var block = segment.block;
6095                     if (block instanceof AST_LabeledStatement) block = block.body;
6096                     if (block === target) break;
6097                 }
6098                 stack.push(segment);
6099                 pop();
6100             }
6101             segment.inserted = segment.block;
6102             push();
6103             while (stack.length) {
6104                 var seg = stack.pop();
6105                 push();
6106                 if (HOP(seg, "block")) segment.block = seg.block;
6107                 if (HOP(seg, "loop")) segment.loop = seg.loop;
6108             }
6109         }
6110
6111         function must_visit(base, segment) {
6112             return base === segment || base.isPrototypeOf(segment);
6113         }
6114
6115         function mergeable(head, tail) {
6116             return must_visit(head.start, head.end) || must_visit(head.start, tail.start);
6117         }
6118     });
6119
6120     function fill_holes(orig, elements) {
6121         for (var i = elements.length; --i >= 0;) {
6122             if (!elements[i]) elements[i] = make_node(AST_Hole, orig);
6123         }
6124     }
6125
6126     function to_class_expr(defcl, drop_name) {
6127         var cl = make_node(AST_ClassExpression, defcl, defcl);
6128         cl.name = drop_name ? null : make_node(AST_SymbolClass, defcl.name, defcl.name);
6129         return cl;
6130     }
6131
6132     function to_func_expr(defun, drop_name) {
6133         var ctor;
6134         switch (defun.CTOR) {
6135           case AST_AsyncDefun:
6136             ctor = AST_AsyncFunction;
6137             break;
6138           case AST_AsyncGeneratorDefun:
6139             ctor = AST_AsyncGeneratorFunction;
6140             break;
6141           case AST_Defun:
6142             ctor = AST_Function;
6143             break;
6144           case AST_GeneratorDefun:
6145             ctor = AST_GeneratorFunction;
6146             break;
6147         }
6148         var fn = make_node(ctor, defun, defun);
6149         fn.name = drop_name ? null : make_node(AST_SymbolLambda, defun.name, defun.name);
6150         return fn;
6151     }
6152
6153     AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
6154         if (!compressor.option("unused")) return;
6155         var self = this;
6156         var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs;
6157         var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars;
6158         var assign_as_unused = /keep_assign/.test(compressor.option("unused")) ? return_false : function(node, props) {
6159             var sym, nested = false;
6160             if (node instanceof AST_Assign) {
6161                 if (node.write_only || node.operator == "=") sym = extract_reference(node.left, props);
6162             } else if (node instanceof AST_Unary) {
6163                 if (node.write_only) sym = extract_reference(node.expression, props);
6164             }
6165             if (!(sym instanceof AST_SymbolRef)) return;
6166             var def = sym.definition();
6167             if (export_defaults[def.id]) return;
6168             if (compressor.exposed(def)) return;
6169             if (!can_drop_symbol(sym, compressor, nested)) return;
6170             return sym;
6171
6172             function extract_reference(node, props) {
6173                 if (node instanceof AST_PropAccess) {
6174                     var expr = node.expression;
6175                     if (!expr.may_throw_on_access(compressor, true)) {
6176                         nested = true;
6177                         if (props && node instanceof AST_Sub) props.unshift(node.property);
6178                         return extract_reference(expr, props);
6179                     }
6180                 } else if (node instanceof AST_Assign && node.operator == "=") {
6181                     node.write_only = "p";
6182                     var ref = extract_reference(node.right);
6183                     if (!props) return ref;
6184                     props.assign = node;
6185                     return ref instanceof AST_SymbolRef ? ref : node.left;
6186                 }
6187                 return node;
6188             }
6189         };
6190         var assign_in_use = Object.create(null);
6191         var export_defaults = Object.create(null);
6192         var find_variable = function(name) {
6193             find_variable = compose(self, 0, noop);
6194             return find_variable(name);
6195
6196             function compose(child, level, find) {
6197                 var parent = compressor.parent(level);
6198                 if (!parent) return find;
6199                 var in_arg = parent instanceof AST_Lambda && member(child, parent.argnames);
6200                 return compose(parent, level + 1, in_arg ? function(name) {
6201                     var def = find(name);
6202                     if (def) return def;
6203                     def = parent.variables.get(name);
6204                     if (def) {
6205                         var sym = def.orig[0];
6206                         if (sym instanceof AST_SymbolFunarg || sym instanceof AST_SymbolLambda) return def;
6207                     }
6208                 } : parent.variables ? function(name) {
6209                     return find(name) || parent.variables.get(name);
6210                 } : find);
6211             }
6212         };
6213         var for_ins = Object.create(null);
6214         var in_use = [];
6215         var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use
6216         var value_read = Object.create(null);
6217         var value_modified = Object.create(null);
6218         var var_defs = Object.create(null);
6219         if (self instanceof AST_Toplevel && compressor.top_retain) {
6220             self.variables.each(function(def) {
6221                 if (compressor.top_retain(def) && !(def.id in in_use_ids)) {
6222                     in_use_ids[def.id] = true;
6223                     in_use.push(def);
6224                 }
6225             });
6226         }
6227         var assignments = new Dictionary();
6228         var initializations = new Dictionary();
6229         // pass 1: find out which symbols are directly used in
6230         // this scope (not in nested scopes).
6231         var scope = this;
6232         var tw = new TreeWalker(function(node, descend) {
6233             if (node instanceof AST_Lambda && node.uses_arguments && !tw.has_directive("use strict")) {
6234                 node.each_argname(function(argname) {
6235                     var def = argname.definition();
6236                     if (!(def.id in in_use_ids)) {
6237                         in_use_ids[def.id] = true;
6238                         in_use.push(def);
6239                     }
6240                 });
6241             }
6242             if (node === self) return;
6243             if (scope === self) {
6244                 if (node instanceof AST_DefClass) {
6245                     var def = node.name.definition();
6246                     if ((!drop_funcs || def.exported) && !(def.id in in_use_ids)) {
6247                         in_use_ids[def.id] = true;
6248                         in_use.push(def);
6249                     }
6250                     if (node.extends) node.extends.walk(tw);
6251                     var is_export = false;
6252                     if (tw.parent() instanceof AST_ExportDefault) {
6253                         is_export = true;
6254                         export_defaults[def.id] = true;
6255                     }
6256                     node.properties.forEach(function(prop) {
6257                         if (prop.key instanceof AST_Node) prop.key.walk(tw);
6258                         if (!prop.value) return;
6259                         if (is_export || prop instanceof AST_ClassField && prop.static) {
6260                             var save_scope = scope;
6261                             scope = node;
6262                             prop.value.walk(tw);
6263                             scope = save_scope;
6264                         } else {
6265                             initializations.add(def.id, prop.value);
6266                         }
6267                     });
6268                     return true;
6269                 }
6270                 if (node instanceof AST_LambdaDefinition) {
6271                     var def = node.name.definition();
6272                     if ((!drop_funcs || def.exported) && !(def.id in in_use_ids)) {
6273                         in_use_ids[def.id] = true;
6274                         in_use.push(def);
6275                     }
6276                     initializations.add(def.id, node);
6277                     if (tw.parent() instanceof AST_ExportDefault) {
6278                         export_defaults[def.id] = true;
6279                     } else {
6280                         return true;
6281                     }
6282                 }
6283                 if (node instanceof AST_Definitions) {
6284                     node.definitions.forEach(function(defn) {
6285                         var value = defn.value;
6286                         var side_effects = value
6287                             && (defn.name instanceof AST_Destructured || value.has_side_effects(compressor));
6288                         var shared = side_effects && value.tail_node().operator == "=";
6289                         defn.name.mark_symbol(function(name) {
6290                             if (!(name instanceof AST_SymbolDeclaration)) return;
6291                             var def = name.definition();
6292                             var_defs[def.id] = (var_defs[def.id] || 0) + 1;
6293                             if (node instanceof AST_Var && def.orig[0] instanceof AST_SymbolCatch) {
6294                                 var redef = def.redefined();
6295                                 if (redef) var_defs[redef.id] = (var_defs[redef.id] || 0) + 1;
6296                             }
6297                             if (!(def.id in in_use_ids) && (!drop_vars || def.exported
6298                                 || (node instanceof AST_Const ? def.redefined() : def.const_redefs)
6299                                 || !(node instanceof AST_Var || is_safe_lexical(def)))) {
6300                                 in_use_ids[def.id] = true;
6301                                 in_use.push(def);
6302                             }
6303                             if (value) {
6304                                 if (!side_effects) {
6305                                     initializations.add(def.id, value);
6306                                 } else if (shared) {
6307                                     verify_safe_usage(def, name, value_modified[def.id]);
6308                                 }
6309                                 assignments.add(def.id, defn);
6310                             }
6311                             return true;
6312                         }, tw);
6313                         if (side_effects) value.walk(tw);
6314                     });
6315                     return true;
6316                 }
6317                 if (node instanceof AST_SymbolFunarg) {
6318                     var def = node.definition();
6319                     var_defs[def.id] = (var_defs[def.id] || 0) + 1;
6320                     assignments.add(def.id, node);
6321                     return true;
6322                 }
6323                 if (node instanceof AST_SymbolImport) {
6324                     var def = node.definition();
6325                     if (!(def.id in in_use_ids) && (!drop_vars || !is_safe_lexical(def))) {
6326                         in_use_ids[def.id] = true;
6327                         in_use.push(def);
6328                     }
6329                     return true;
6330                 }
6331             }
6332             return scan_ref_scoped(node, descend, true);
6333         });
6334         tw.directives = Object.create(compressor.directives);
6335         self.walk(tw);
6336         var drop_fn_name = compressor.option("keep_fnames") ? return_false : compressor.option("ie") ? function(def) {
6337             return !compressor.exposed(def) && def.references.length == def.replaced;
6338         } : function(def) {
6339             if (!(def.id in in_use_ids)) return true;
6340             if (def.orig.length - def.eliminated < 2) return false;
6341             // function argument will always overshadow its name
6342             if (def.orig[1] instanceof AST_SymbolFunarg) return true;
6343             // retain if referenced within destructured object of argument
6344             return all(def.references, function(ref) {
6345                 return !ref.in_arg;
6346             });
6347         };
6348         if (compressor.option("ie")) initializations.each(function(init, id) {
6349             if (id in in_use_ids) return;
6350             init.forEach(function(init) {
6351                 init.walk(new TreeWalker(function(node) {
6352                     if (node instanceof AST_Function && node.name && !drop_fn_name(node.name.definition())) {
6353                         node.walk(tw);
6354                         return true;
6355                     }
6356                     if (node instanceof AST_Scope) return true;
6357                 }));
6358             });
6359         });
6360         // pass 2: for every used symbol we need to walk its
6361         // initialization code to figure out if it uses other
6362         // symbols (that may not be in_use).
6363         tw = new TreeWalker(scan_ref_scoped);
6364         for (var i = 0; i < in_use.length; i++) {
6365             var init = initializations.get(in_use[i].id);
6366             if (init) init.forEach(function(init) {
6367                 init.walk(tw);
6368             });
6369         }
6370         Object.keys(assign_in_use).forEach(function(id) {
6371             var assigns = assign_in_use[id];
6372             if (!assigns) {
6373                 delete assign_in_use[id];
6374                 return;
6375             }
6376             assigns = assigns.reduce(function(in_use, assigns) {
6377                 assigns.forEach(function(assign) {
6378                     push_uniq(in_use, assign);
6379                 });
6380                 return in_use;
6381             }, []);
6382             var in_use = (assignments.get(id) || []).filter(function(node) {
6383                 return find_if(node instanceof AST_Unary ? function(assign) {
6384                     return assign === node;
6385                 } : function(assign) {
6386                     if (assign === node) return true;
6387                     if (assign instanceof AST_Unary) return false;
6388                     return get_rvalue(assign) === get_rvalue(node);
6389                 }, assigns);
6390             });
6391             if (assigns.length == in_use.length) {
6392                 assign_in_use[id] = in_use;
6393             } else {
6394                 delete assign_in_use[id];
6395             }
6396         });
6397         // pass 3: we should drop declarations not in_use
6398         var trim_defns = [];
6399         var unused_fn_names = [];
6400         var calls_to_drop_args = [];
6401         var fns_with_marked_args = [];
6402         var trimmer = new TreeTransformer(function(node) {
6403             if (node instanceof AST_DefaultValue) return trim_default(trimmer, node);
6404             if (node instanceof AST_Destructured && node.rest) node.rest = node.rest.transform(trimmer);
6405             if (node instanceof AST_DestructuredArray) {
6406                 var trim = !node.rest;
6407                 for (var i = node.elements.length; --i >= 0;) {
6408                     var element = node.elements[i].transform(trimmer);
6409                     if (element) {
6410                         node.elements[i] = element;
6411                         trim = false;
6412                     } else if (trim) {
6413                         node.elements.pop();
6414                     } else {
6415                         node.elements[i] = make_node(AST_Hole, node.elements[i]);
6416                     }
6417                 }
6418                 return node;
6419             }
6420             if (node instanceof AST_DestructuredObject) {
6421                 var properties = [];
6422                 node.properties.forEach(function(prop) {
6423                     var retain = false;
6424                     if (prop.key instanceof AST_Node) {
6425                         prop.key = prop.key.transform(tt);
6426                         retain = prop.key.has_side_effects(compressor);
6427                     }
6428                     if ((retain || node.rest) && is_decl(prop.value)) {
6429                         prop.value = prop.value.transform(tt);
6430                         properties.push(prop);
6431                     } else {
6432                         var value = prop.value.transform(trimmer);
6433                         if (!value && node.rest) {
6434                             if (prop.value instanceof AST_DestructuredArray) {
6435                                 value = make_node(AST_DestructuredArray, prop.value, { elements: [] });
6436                             } else {
6437                                 value = make_node(AST_DestructuredObject, prop.value, { properties: [] });
6438                             }
6439                         }
6440                         if (value) {
6441                             prop.value = value;
6442                             properties.push(prop);
6443                         }
6444                     }
6445                 });
6446                 node.properties = properties;
6447                 return node;
6448             }
6449             if (node instanceof AST_SymbolDeclaration) return node.definition().id in in_use_ids ? node : null;
6450         });
6451         var tt = new TreeTransformer(function(node, descend, in_list) {
6452             var parent = tt.parent();
6453             if (drop_vars) {
6454                 var props = [], sym = assign_as_unused(node, props);
6455                 if (sym) {
6456                     var value;
6457                     if (can_drop_lhs(sym, node)) {
6458                         if (node instanceof AST_Assign) {
6459                             value = get_rhs(node);
6460                             if (node.write_only === true) value = value.drop_side_effect_free(compressor);
6461                         }
6462                         if (!value) value = make_node(AST_Number, node, { value: 0 });
6463                     }
6464                     if (value) {
6465                         if (props.assign) {
6466                             var assign = props.assign.drop_side_effect_free(compressor);
6467                             if (assign) {
6468                                 assign.write_only = true;
6469                                 props.unshift(assign);
6470                             }
6471                         }
6472                         if (!(parent instanceof AST_Sequence)
6473                             || parent.tail_node() === node
6474                             || value.has_side_effects(compressor)) {
6475                             props.push(value);
6476                         }
6477                         switch (props.length) {
6478                           case 0:
6479                             return List.skip;
6480                           case 1:
6481                             return maintain_this_binding(compressor, parent, node, props[0].transform(tt));
6482                           default:
6483                             return make_sequence(node, props.map(function(prop) {
6484                                 return prop.transform(tt);
6485                             }));
6486                         }
6487                     }
6488                 } else if (node instanceof AST_UnaryPostfix
6489                     && node.expression instanceof AST_SymbolRef
6490                     && indexOf_assign(node.expression.definition(), node) < 0) {
6491                     return make_node(AST_UnaryPrefix, node, {
6492                         operator: "+",
6493                         expression: node.expression
6494                     });
6495                 }
6496             }
6497             if (node instanceof AST_Call) calls_to_drop_args.push(node);
6498             if (scope !== self) return;
6499             if (drop_funcs && node !== self && node instanceof AST_DefClass) {
6500                 var def = node.name.definition();
6501                 if (!(def.id in in_use_ids)) {
6502                     log(node.name, "Dropping unused class {name}");
6503                     def.eliminated++;
6504                     descend(node, tt);
6505                     if (parent instanceof AST_ExportDefault) return to_class_expr(node, true);
6506                     var trimmed = node.drop_side_effect_free(compressor, true);
6507                     if (trimmed === node) trimmed = to_class_expr(node, true);
6508                     if (trimmed) return make_node(AST_SimpleStatement, node, { body: trimmed });
6509                     return in_list ? List.skip : make_node(AST_EmptyStatement, node);
6510                 }
6511             }
6512             if (node instanceof AST_ClassExpression && node.name && drop_fn_name(node.name.definition())) {
6513                 unused_fn_names.push(node);
6514             }
6515             if (node instanceof AST_Lambda) {
6516                 if (drop_funcs && node !== self && node instanceof AST_LambdaDefinition) {
6517                     var def = node.name.definition();
6518                     if (!(def.id in in_use_ids)) {
6519                         log(node.name, "Dropping unused function {name}");
6520                         def.eliminated++;
6521                         if (parent instanceof AST_ExportDefault) {
6522                             descend_scope();
6523                             return to_func_expr(node, true);
6524                         }
6525                         return in_list ? List.skip : make_node(AST_EmptyStatement, node);
6526                     }
6527                 }
6528                 if (node instanceof AST_LambdaExpression && node.name && drop_fn_name(node.name.definition())) {
6529                     unused_fn_names.push(node);
6530                 }
6531                 if (!(node instanceof AST_Accessor)) {
6532                     if (node.rest) {
6533                         var rest = node.rest.transform(trimmer);
6534                         if (rest instanceof AST_Destructured && !rest.rest
6535                             && (!node.uses_arguments || tt.has_directive("use strict"))) {
6536                             if (rest instanceof AST_DestructuredArray) {
6537                                 if (rest.elements.length == 0) rest = null;
6538                             } else if (rest.properties.length == 0) {
6539                                 rest = null;
6540                             }
6541                         }
6542                         node.rest = rest;
6543                     }
6544                     var argnames = node.argnames;
6545                     var trim = compressor.drop_fargs(node, parent) && !node.rest;
6546                     var default_length = trim ? -1 : node.length();
6547                     for (var i = argnames.length; --i >= 0;) {
6548                         var sym = argnames[i];
6549                         if (!(sym instanceof AST_SymbolFunarg)) {
6550                             var arg = sym.transform(trimmer);
6551                             if (arg) {
6552                                 trim = false;
6553                             } else if (trim) {
6554                                 log(sym.name, "Dropping unused default argument {name}");
6555                                 argnames.pop();
6556                             } else if (i > default_length) {
6557                                 log(sym.name, "Dropping unused default argument assignment {name}");
6558                                 sym.name.__unused = true;
6559                                 argnames[i] = sym.name;
6560                             } else {
6561                                 log(sym.name, "Dropping unused default argument value {name}");
6562                                 sym.value = make_node(AST_Number, sym, { value: 0 });
6563                             }
6564                             continue;
6565                         }
6566                         var def = sym.definition();
6567                         if (def.id in in_use_ids) {
6568                             trim = false;
6569                             if (indexOf_assign(def, sym) < 0) sym.__unused = null;
6570                         } else if (trim) {
6571                             log(sym, "Dropping unused function argument {name}");
6572                             argnames.pop();
6573                         } else {
6574                             sym.__unused = true;
6575                         }
6576                     }
6577                     fns_with_marked_args.push(node);
6578                 }
6579             }
6580             if (node instanceof AST_Catch && node.argname instanceof AST_Destructured) {
6581                 node.argname.transform(trimmer);
6582             }
6583             if (node instanceof AST_Definitions && !(parent instanceof AST_ForEnumeration && parent.init === node)) {
6584                 // place uninitialized names at the start
6585                 var body = [], head = [], tail = [];
6586                 // for unused names whose initialization has
6587                 // side effects, we can cascade the init. code
6588                 // into the next one, or next statement.
6589                 var side_effects = [];
6590                 var duplicated = 0;
6591                 var is_var = node instanceof AST_Var;
6592                 node.definitions.forEach(function(def) {
6593                     if (def.value) def.value = def.value.transform(tt);
6594                     var value = def.value;
6595                     if (def.name instanceof AST_Destructured) {
6596                         var trimmed = trim_destructured(def.name, value, function(node) {
6597                             if (!drop_vars) return node;
6598                             if (node.definition().id in in_use_ids) return node;
6599                             if (is_catch(node)) return node;
6600                             if (is_var && !can_drop_symbol(node)) return node;
6601                             return null;
6602                         }, true);
6603                         if (trimmed.name) {
6604                             def = make_node(AST_VarDef, def, {
6605                                 name: trimmed.name,
6606                                 value: value = trimmed.value,
6607                             });
6608                             flush();
6609                         } else if (trimmed.value) {
6610                             side_effects.push(trimmed.value);
6611                         }
6612                         return;
6613                     }
6614                     var sym = def.name.definition();
6615                     var drop_sym = is_var ? can_drop_symbol(def.name) : is_safe_lexical(sym);
6616                     if (!drop_sym || !drop_vars || sym.id in in_use_ids) {
6617                         if (value && indexOf_assign(sym, def) < 0) {
6618                             value = value.drop_side_effect_free(compressor);
6619                             if (value) {
6620                                 AST_Node.warn("Side effects in last use of variable {name} [{file}:{line},{col}]", template(def.name));
6621                                 side_effects.push(value);
6622                             }
6623                             value = null;
6624                             trim_defns.push(def);
6625                         }
6626                         var old_def;
6627                         if (!value && !(node instanceof AST_Let)) {
6628                             if (parent instanceof AST_ExportDeclaration) {
6629                                 flush();
6630                             } else if (drop_sym && var_defs[sym.id] > 1) {
6631                                 AST_Node.info("Dropping declaration of variable {name} [{file}:{line},{col}]", template(def.name));
6632                                 var_defs[sym.id]--;
6633                                 sym.eliminated++;
6634                             } else {
6635                                 head.push(def);
6636                             }
6637                         } else if (compressor.option("functions")
6638                             && !compressor.option("ie")
6639                             && drop_sym
6640                             && var_defs[sym.id] == 1
6641                             && sym.assignments == 0
6642                             && value instanceof AST_LambdaExpression
6643                             && !is_arguments(sym)
6644                             && !is_arrow(value)
6645                             && assigned_once(value, sym.references)
6646                             && can_declare_defun(value)
6647                             && (old_def = rename_def(value, def.name.name)) !== false) {
6648                             AST_Node.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name));
6649                             var ctor;
6650                             switch (value.CTOR) {
6651                               case AST_AsyncFunction:
6652                                 ctor = AST_AsyncDefun;
6653                                 break;
6654                               case AST_AsyncGeneratorFunction:
6655                                 ctor = AST_AsyncGeneratorDefun;
6656                                 break;
6657                               case AST_Function:
6658                                 ctor = AST_Defun;
6659                                 break;
6660                               case AST_GeneratorFunction:
6661                                 ctor = AST_GeneratorDefun;
6662                                 break;
6663                             }
6664                             var defun = make_node(ctor, def, value);
6665                             defun.name = make_node(AST_SymbolDefun, def.name, def.name);
6666                             var name_def = def.name.scope.resolve().def_function(defun.name);
6667                             if (old_def) old_def.forEach(function(node) {
6668                                 node.name = name_def.name;
6669                                 node.thedef = name_def;
6670                                 node.reference();
6671                             });
6672                             body.push(defun);
6673                         } else {
6674                             if (drop_sym
6675                                 && var_defs[sym.id] > 1
6676                                 && !(parent instanceof AST_ExportDeclaration)
6677                                 && sym.orig.indexOf(def.name) > sym.eliminated) {
6678                                 var_defs[sym.id]--;
6679                                 duplicated++;
6680                             }
6681                             flush();
6682                         }
6683                     } else if (is_catch(def.name)) {
6684                         value = value && value.drop_side_effect_free(compressor);
6685                         if (value) side_effects.push(value);
6686                         if (var_defs[sym.id] > 1) {
6687                             AST_Node.warn("Dropping duplicated declaration of variable {name} [{file}:{line},{col}]", template(def.name));
6688                             var_defs[sym.id]--;
6689                             sym.eliminated++;
6690                         } else {
6691                             def.value = null;
6692                             head.push(def);
6693                         }
6694                     } else {
6695                         value = value && !value.single_use && value.drop_side_effect_free(compressor);
6696                         if (value) {
6697                             AST_Node.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
6698                             side_effects.push(value);
6699                         } else {
6700                             log(def.name, "Dropping unused variable {name}");
6701                         }
6702                         sym.eliminated++;
6703                     }
6704
6705                     function assigned_once(fn, refs) {
6706                         if (refs.length == 0) return fn === def.name.fixed_value();
6707                         return all(refs, function(ref) {
6708                             return fn === ref.fixed_value();
6709                         });
6710                     }
6711
6712                     function can_declare_defun(fn) {
6713                         if (!is_var || compressor.has_directive("use strict") || !(fn instanceof AST_Function)) {
6714                             return parent instanceof AST_Scope;
6715                         }
6716                         return parent instanceof AST_Block
6717                             || parent instanceof AST_For && parent.init === node
6718                             || parent instanceof AST_If;
6719                     }
6720
6721                     function rename_def(fn, name) {
6722                         if (!fn.name) return null;
6723                         var def = fn.name.definition();
6724                         if (def.orig.length > 1) return null;
6725                         if (def.assignments > 0) return false;
6726                         if (def.name == name) return def;
6727                         var forbidden;
6728                         switch (name) {
6729                           case "await":
6730                             forbidden = is_async;
6731                             break;
6732                           case "yield":
6733                             forbidden = is_generator;
6734                             break;
6735                         }
6736                         return all(def.references, function(ref) {
6737                             var scope = ref.scope;
6738                             if (scope.find_variable(name) !== sym) return false;
6739                             if (forbidden) do {
6740                                 scope = scope.resolve();
6741                                 if (forbidden(scope)) return false;
6742                             } while (scope !== fn && (scope = scope.parent_scope));
6743                             return true;
6744                         }) && def;
6745                     }
6746
6747                     function is_catch(node) {
6748                         var sym = node.definition();
6749                         return sym.orig[0] instanceof AST_SymbolCatch && sym.scope.resolve() === node.scope.resolve();
6750                     }
6751
6752                     function flush() {
6753                         if (side_effects.length > 0) {
6754                             if (tail.length == 0) {
6755                                 body.push(make_node(AST_SimpleStatement, node, {
6756                                     body: make_sequence(node, side_effects),
6757                                 }));
6758                             } else if (value) {
6759                                 side_effects.push(value);
6760                                 def.value = make_sequence(value, side_effects);
6761                             } else {
6762                                 def.value = make_node(AST_UnaryPrefix, def, {
6763                                     operator: "void",
6764                                     expression: make_sequence(def, side_effects),
6765                                 });
6766                             }
6767                             side_effects = [];
6768                         }
6769                         tail.push(def);
6770                     }
6771                 });
6772                 switch (head.length) {
6773                   case 0:
6774                     if (tail.length == 0) break;
6775                     if (tail.length == duplicated) {
6776                         [].unshift.apply(side_effects, tail.map(function(def) {
6777                             AST_Node.info("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
6778                             var sym = def.name.definition();
6779                             var ref = make_node(AST_SymbolRef, def.name, def.name);
6780                             sym.references.push(ref);
6781                             var assign = make_node(AST_Assign, def, {
6782                                 operator: "=",
6783                                 left: ref,
6784                                 right: def.value
6785                             });
6786                             var index = indexOf_assign(sym, def);
6787                             if (index >= 0) assign_in_use[sym.id][index] = assign;
6788                             sym.eliminated++;
6789                             return assign;
6790                         }));
6791                         break;
6792                     }
6793                   case 1:
6794                     if (tail.length == 0) {
6795                         var id = head[0].name.definition().id;
6796                         if (id in for_ins) {
6797                             node.definitions = head;
6798                             for_ins[id].init = node;
6799                             break;
6800                         }
6801                     }
6802                   default:
6803                     node.definitions = head.concat(tail);
6804                     body.push(node);
6805                 }
6806                 if (side_effects.length > 0) {
6807                     body.push(make_node(AST_SimpleStatement, node, {
6808                         body: make_sequence(node, side_effects)
6809                     }));
6810                 }
6811                 return insert_statements(body, node, in_list);
6812             }
6813             if (node instanceof AST_Assign) {
6814                 descend(node, tt);
6815                 if (!(node.left instanceof AST_Destructured)) return node;
6816                 var trimmed = trim_destructured(node.left, node.right, function(node) {
6817                     return node;
6818                 }, node.write_only === true);
6819                 if (trimmed.name) return make_node(AST_Assign, node, {
6820                     operator: node.operator,
6821                     left: trimmed.name,
6822                     right: trimmed.value,
6823                 });
6824                 if (trimmed.value) return trimmed.value;
6825                 if (parent instanceof AST_Sequence && parent.tail_node() !== node) return List.skip;
6826                 return make_node(AST_Number, node, { value: 0 });
6827             }
6828             if (node instanceof AST_LabeledStatement && node.body instanceof AST_For) {
6829                 // Certain combination of unused name + side effect leads to invalid AST:
6830                 //    https://github.com/mishoo/UglifyJS/issues/1830
6831                 // We fix it at this stage by moving the label inwards, back to the `for`.
6832                 descend(node, tt);
6833                 if (node.body instanceof AST_BlockStatement) {
6834                     var block = node.body;
6835                     node.body = block.body.pop();
6836                     block.body.push(node);
6837                     return in_list ? List.splice(block.body) : block;
6838                 }
6839                 return node;
6840             }
6841             if (node instanceof AST_Scope) {
6842                 descend_scope();
6843                 return node;
6844             }
6845             if (node instanceof AST_SymbolImport) {
6846                 if (!compressor.option("imports") || node.definition().id in in_use_ids) return node;
6847                 return in_list ? List.skip : null;
6848             }
6849
6850             function descend_scope() {
6851                 var save_scope = scope;
6852                 scope = node;
6853                 descend(node, tt);
6854                 scope = save_scope;
6855             }
6856         }, function(node, in_list) {
6857             if (node instanceof AST_BlockStatement) return trim_block(node, tt.parent(), in_list);
6858             // Certain combination of unused name + side effect leads to invalid AST:
6859             //    https://github.com/mishoo/UglifyJS/issues/44
6860             //    https://github.com/mishoo/UglifyJS/issues/1838
6861             //    https://github.com/mishoo/UglifyJS/issues/3371
6862             // We fix it at this stage by moving the `var` outside the `for`.
6863             if (node instanceof AST_For) {
6864                 var block;
6865                 if (node.init instanceof AST_BlockStatement) {
6866                     block = node.init;
6867                     node.init = block.body.pop();
6868                     block.body.push(node);
6869                 }
6870                 if (node.init instanceof AST_Defun) {
6871                     if (!block) {
6872                         block = make_node(AST_BlockStatement, node, {
6873                             body: [ node ]
6874                         });
6875                     }
6876                     block.body.splice(-1, 0, node.init);
6877                     node.init = null;
6878                 } else if (node.init instanceof AST_SimpleStatement) {
6879                     node.init = node.init.body;
6880                 } else if (is_empty(node.init)) {
6881                     node.init = null;
6882                 }
6883                 return !block ? node : in_list ? List.splice(block.body) : block;
6884             }
6885             if (node instanceof AST_ForIn) {
6886                 if (!drop_vars || !compressor.option("loops")) return;
6887                 if (!is_empty(node.body)) return;
6888                 var sym = get_init_symbol(node);
6889                 if (!sym) return;
6890                 var def = sym.definition();
6891                 if (def.id in in_use_ids) return;
6892                 log(sym, "Dropping unused loop variable {name}");
6893                 if (for_ins[def.id] === node) delete for_ins[def.id];
6894                 var body = [];
6895                 var value = node.object.drop_side_effect_free(compressor);
6896                 if (value) {
6897                     AST_Node.warn("Side effects in object of for-in loop [{file}:{line},{col}]", value.start);
6898                     body.push(make_node(AST_SimpleStatement, node, {
6899                         body: value
6900                     }));
6901                 }
6902                 if (node.init instanceof AST_Definitions && def.orig[0] instanceof AST_SymbolCatch) {
6903                     body.push(node.init);
6904                 }
6905                 return insert_statements(body, node, in_list);
6906             }
6907             if (node instanceof AST_Import) {
6908                 if (node.properties && node.properties.length == 0) node.properties = null;
6909                 return node;
6910             }
6911             if (node instanceof AST_Sequence) {
6912                 if (node.expressions.length > 1) return;
6913                 return maintain_this_binding(compressor, tt.parent(), node, node.expressions[0]);
6914             }
6915         });
6916         tt.push(compressor.parent());
6917         self.transform(tt);
6918         if (self instanceof AST_Lambda
6919             && self.body.length == 1
6920             && self.body[0] instanceof AST_Directive
6921             && self.body[0].value == "use strict") {
6922             self.body.length = 0;
6923         }
6924         trim_defns.forEach(function(def) {
6925             def.value = null;
6926         });
6927         unused_fn_names.forEach(function(fn) {
6928             fn.name = null;
6929         });
6930         calls_to_drop_args.forEach(function(call) {
6931             drop_unused_call_args(call, compressor, fns_with_marked_args);
6932         });
6933
6934         function log(sym, text) {
6935             AST_Node[sym.definition().references.length > 0 ? "info" : "warn"](text + " [{file}:{line},{col}]", template(sym));
6936         }
6937
6938         function template(sym) {
6939             return {
6940                 name: sym.name,
6941                 file: sym.start.file,
6942                 line: sym.start.line,
6943                 col : sym.start.col,
6944             };
6945         }
6946
6947         function get_rvalue(expr) {
6948             return expr[expr instanceof AST_Assign ? "right" : "value"];
6949         }
6950
6951         function insert_statements(body, orig, in_list) {
6952             switch (body.length) {
6953               case 0:
6954                 return in_list ? List.skip : make_node(AST_EmptyStatement, orig);
6955               case 1:
6956                 return body[0];
6957               default:
6958                 return in_list ? List.splice(body) : make_node(AST_BlockStatement, orig, {
6959                     body: body
6960                 });
6961             }
6962         }
6963
6964         function track_assigns(def, node) {
6965             if (def.scope.resolve() !== self) return false;
6966             if (!def.fixed || !node.fixed) assign_in_use[def.id] = false;
6967             return assign_in_use[def.id] !== false;
6968         }
6969
6970         function add_assigns(def, node) {
6971             if (!assign_in_use[def.id]) assign_in_use[def.id] = [];
6972             if (node.fixed.assigns) push_uniq(assign_in_use[def.id], node.fixed.assigns);
6973         }
6974
6975         function indexOf_assign(def, node) {
6976             var nodes = assign_in_use[def.id];
6977             return nodes && nodes.indexOf(node);
6978         }
6979
6980         function verify_safe_usage(def, read, modified) {
6981             if (def.id in in_use_ids) return;
6982             if (read && modified) {
6983                 in_use_ids[def.id] = read;
6984                 in_use.push(def);
6985             } else {
6986                 value_read[def.id] = read;
6987                 value_modified[def.id] = modified;
6988             }
6989         }
6990
6991         function can_drop_lhs(sym, node) {
6992             var def = sym.definition();
6993             var in_use = in_use_ids[def.id];
6994             if (!in_use) return true;
6995             if (node[node instanceof AST_Assign ? "left" : "expression"] !== sym) return false;
6996             return in_use === sym && def.references.length - def.replaced == 1 || indexOf_assign(def, node) < 0;
6997         }
6998
6999         function get_rhs(assign) {
7000             var rhs = assign.right;
7001             if (!assign.write_only) return rhs;
7002             if (!(rhs instanceof AST_Binary && lazy_op[rhs.operator])) return rhs;
7003             if (!(rhs.left instanceof AST_SymbolRef)) return rhs;
7004             if (!(assign.left instanceof AST_SymbolRef)) return rhs;
7005             var def = assign.left.definition();
7006             if (rhs.left.definition() !== def) return rhs;
7007             if (rhs.right.has_side_effects(compressor)) return rhs;
7008             if (track_assigns(def, rhs.left)) add_assigns(def, rhs.left);
7009             return rhs.right;
7010         }
7011
7012         function get_init_symbol(for_in) {
7013             var init = for_in.init;
7014             if (init instanceof AST_Definitions) {
7015                 init = init.definitions[0].name;
7016                 return init instanceof AST_SymbolDeclaration && init;
7017             }
7018             while (init instanceof AST_PropAccess) init = init.expression.tail_node();
7019             if (init instanceof AST_SymbolRef) return init;
7020         }
7021
7022         function scan_ref_scoped(node, descend, init) {
7023             if (node instanceof AST_Assign && node.left instanceof AST_SymbolRef) {
7024                 var def = node.left.definition();
7025                 if (def.scope.resolve() === self) assignments.add(def.id, node);
7026             }
7027             if (node instanceof AST_Unary && node.expression instanceof AST_SymbolRef) {
7028                 var def = node.expression.definition();
7029                 if (def.scope.resolve() === self) assignments.add(def.id, node);
7030             }
7031             var node_def, props = [], sym = assign_as_unused(node, props);
7032             if (sym && ((node_def = sym.definition()).scope.resolve() === self
7033                     || self.variables.get(sym.name) === node_def)
7034                 && !(is_arguments(node_def) && !all(self.argnames, function(argname) {
7035                     return !argname.match_symbol(function(node) {
7036                         if (node instanceof AST_SymbolFunarg) {
7037                             var def = node.definition();
7038                             return def.references.length > def.replaced;
7039                         }
7040                     }, true);
7041                 }))) {
7042                 if (node.write_only === "p" && node.right.may_throw_on_access(compressor, true)) return;
7043                 var assign = props.assign;
7044                 if (assign) {
7045                     assign.write_only = true;
7046                     assign.walk(tw);
7047                 }
7048                 props.forEach(function(prop) {
7049                     prop.walk(tw);
7050                 });
7051                 if (node instanceof AST_Assign) {
7052                     var right = get_rhs(node), shared = false;
7053                     if (init && node.write_only === true && !right.has_side_effects(compressor)) {
7054                         initializations.add(node_def.id, right);
7055                     } else {
7056                         right.walk(tw);
7057                         shared = right.tail_node().operator == "=";
7058                     }
7059                     if (node.left === sym) {
7060                         if (!node.write_only || shared) {
7061                             verify_safe_usage(node_def, sym, value_modified[node_def.id]);
7062                         }
7063                     } else {
7064                         var fixed = sym.fixed_value();
7065                         if (!fixed || !fixed.is_constant()) {
7066                             verify_safe_usage(node_def, value_read[node_def.id], true);
7067                         }
7068                     }
7069                 }
7070                 if (track_assigns(node_def, sym) && is_lhs(sym, node) !== sym) add_assigns(node_def, sym);
7071                 return true;
7072             }
7073             if (node instanceof AST_ForIn) {
7074                 if (node.init instanceof AST_SymbolRef && scope === self) {
7075                     var id = node.init.definition().id;
7076                     if (!(id in for_ins)) for_ins[id] = node;
7077                 }
7078                 if (!drop_vars || !compressor.option("loops")) return;
7079                 if (!is_empty(node.body)) return;
7080                 if (node.init.has_side_effects(compressor)) return;
7081                 var sym = get_init_symbol(node);
7082                 if (!sym) return;
7083                 var def = sym.definition();
7084                 if (def.scope.resolve() !== self) {
7085                     var d = find_variable(sym.name);
7086                     if (d === def || d && d.redefined() === def) return;
7087                 }
7088                 node.object.walk(tw);
7089                 return true;
7090             }
7091             if (node instanceof AST_SymbolRef) {
7092                 node_def = node.definition();
7093                 if (!(node_def.id in in_use_ids)) {
7094                     in_use_ids[node_def.id] = true;
7095                     in_use.push(node_def);
7096                 }
7097                 if (cross_scope(node_def.scope, node.scope)) {
7098                     var redef = node_def.redefined();
7099                     if (redef && !(redef.id in in_use_ids)) {
7100                         in_use_ids[redef.id] = true;
7101                         in_use.push(redef);
7102                     }
7103                 }
7104                 if (track_assigns(node_def, node)) add_assigns(node_def, node);
7105                 return true;
7106             }
7107             if (node instanceof AST_Scope) {
7108                 var save_scope = scope;
7109                 scope = node;
7110                 descend();
7111                 scope = save_scope;
7112                 return true;
7113             }
7114         }
7115
7116         function is_decl(node) {
7117             return (node instanceof AST_DefaultValue ? node.name : node) instanceof AST_SymbolDeclaration;
7118         }
7119
7120         function trim_default(trimmer, node) {
7121             node.value = node.value.transform(tt);
7122             var name = node.name.transform(trimmer);
7123             if (!name) {
7124                 if (node.name instanceof AST_Destructured) return null;
7125                 var value = node.value.drop_side_effect_free(compressor);
7126                 if (!value) return null;
7127                 log(node.name, "Side effects in default value of unused variable {name}");
7128                 node.name.__unused = null;
7129                 node.value = value;
7130             }
7131             return node;
7132         }
7133
7134         function trim_destructured(node, value, process, drop) {
7135             var trimmer = new TreeTransformer(function(node) {
7136                 if (node instanceof AST_DefaultValue) {
7137                     if (compressor.option("default_values") && value && value.is_defined(compressor)) {
7138                         node = node.name;
7139                     } else {
7140                         var save_drop = drop;
7141                         drop = false;
7142                         var trimmed = trim_default(trimmer, node);
7143                         drop = save_drop;
7144                         if (!trimmed && drop && value) value = value.drop_side_effect_free(compressor);
7145                         return trimmed;
7146                     }
7147                 }
7148                 if (node instanceof AST_DestructuredArray) {
7149                     var save_drop = drop;
7150                     var save_value = value;
7151                     if (value instanceof AST_SymbolRef) {
7152                         drop = false;
7153                         value = value.fixed_value();
7154                     }
7155                     var values = value instanceof AST_Array && value.elements;
7156                     var elements = [], newValues = drop && [], pos = 0;
7157                     node.elements.forEach(function(element, index) {
7158                         value = values && values[index];
7159                         if (value instanceof AST_Hole) {
7160                             value = null;
7161                         } else if (value instanceof AST_Spread) {
7162                             if (drop) {
7163                                 newValues.length = pos;
7164                                 fill_holes(save_value, newValues);
7165                                 [].push.apply(newValues, values.slice(index));
7166                                 save_value.elements = newValues;
7167                             }
7168                             value = values = false;
7169                         }
7170                         element = element.transform(trimmer);
7171                         if (element) elements[pos] = element;
7172                         if (drop && value) newValues[pos] = value;
7173                         if (element || value || !drop || !values) pos++;
7174                     });
7175                     value = values && make_node(AST_Array, save_value, {
7176                         elements: values.slice(node.elements.length),
7177                     });
7178                     if (node.rest) {
7179                         var was_drop = drop;
7180                         drop = false;
7181                         node.rest = node.rest.transform(compressor.option("rests") ? trimmer : tt);
7182                         drop = was_drop;
7183                         if (node.rest) elements.length = pos;
7184                     }
7185                     if (drop) {
7186                         if (value && !node.rest) value = value.drop_side_effect_free(compressor);
7187                         if (value instanceof AST_Array) {
7188                             value = value.elements;
7189                         } else if (value instanceof AST_Sequence) {
7190                             value = value.expressions;
7191                         } else if (value) {
7192                             value = [ value ];
7193                         }
7194                         if (value && value.length) {
7195                             newValues.length = pos;
7196                             [].push.apply(newValues, value);
7197                         }
7198                     }
7199                     value = save_value;
7200                     drop = save_drop;
7201                     if (values && newValues) {
7202                         fill_holes(value, newValues);
7203                         value.elements = newValues;
7204                     }
7205                     if (!node.rest && (value instanceof AST_Array
7206                         || value && value.is_string(compressor))) switch (elements.length) {
7207                       case 0:
7208                         if (drop) value = value.drop_side_effect_free(compressor);
7209                         return null;
7210                       case 1:
7211                         if (!drop) break;
7212                         var sym = elements[0];
7213                         if (sym.has_side_effects(compressor)) break;
7214                         value = make_node(AST_Sub, node, {
7215                             expression: value,
7216                             property: make_node(AST_Number, node, { value: 0 }),
7217                         });
7218                         return sym;
7219                     }
7220                     fill_holes(node, elements);
7221                     node.elements = elements;
7222                     return node;
7223                 }
7224                 if (node instanceof AST_DestructuredObject) {
7225                     var save_drop = drop;
7226                     var save_value = value;
7227                     if (value instanceof AST_SymbolRef) {
7228                         drop = false;
7229                         value = value.fixed_value();
7230                     }
7231                     var prop_keys, prop_map;
7232                     if (value instanceof AST_Object) {
7233                         prop_keys = [];
7234                         prop_map = Object.create(null);
7235                         value.properties.forEach(function(prop, index) {
7236                             if (prop instanceof AST_Spread) return prop_map = false;
7237                             var key = prop.key;
7238                             if (key instanceof AST_Node) key = key.evaluate(compressor, true);
7239                             if (key instanceof AST_Node) {
7240                                 prop_map = false;
7241                             } else if (prop_map && !(prop instanceof AST_ObjectSetter)) {
7242                                 prop_map[key] = prop;
7243                             }
7244                             prop_keys[index] = key;
7245                         });
7246                     }
7247                     if (node.rest) {
7248                         value = false;
7249                         node.rest = node.rest.transform(compressor.option("rests") ? trimmer : tt);
7250                     }
7251                     var can_drop = Object.create(null);
7252                     var drop_keys = drop && Object.create(null);
7253                     var properties = [];
7254                     node.properties.map(function(prop) {
7255                         var key = prop.key;
7256                         if (key instanceof AST_Node) {
7257                             prop.key = key = key.transform(tt);
7258                             key = key.evaluate(compressor, true);
7259                         }
7260                         if (key instanceof AST_Node) {
7261                             drop_keys = false;
7262                         } else {
7263                             can_drop[key] = !(key in can_drop);
7264                         }
7265                         return key;
7266                     }).forEach(function(key, index) {
7267                         var prop = node.properties[index], trimmed;
7268                         if (key instanceof AST_Node) {
7269                             drop = false;
7270                             value = false;
7271                             trimmed = prop.value.transform(trimmer) || retain_lhs(prop.value);
7272                         } else {
7273                             drop = drop_keys && can_drop[key];
7274                             var mapped = prop_map && prop_map[key];
7275                             if (mapped) {
7276                                 value = mapped.value;
7277                                 if (value instanceof AST_Accessor) value = false;
7278                             } else {
7279                                 value = false;
7280                             }
7281                             trimmed = prop.value.transform(trimmer);
7282                             if (!trimmed) {
7283                                 if (node.rest || retain_key(prop)) trimmed = retain_lhs(prop.value);
7284                                 if (drop_keys && !(key in drop_keys)) {
7285                                     if (mapped) {
7286                                         drop_keys[key] = mapped;
7287                                         if (value === null) {
7288                                             prop_map[key] = retain_key(mapped) && make_node(AST_ObjectKeyVal, mapped, {
7289                                                 key: mapped.key,
7290                                                 value: make_node(AST_Number, mapped, { value: 0 }),
7291                                             });
7292                                         }
7293                                     } else {
7294                                         drop_keys[key] = true;
7295                                     }
7296                                 }
7297                             } else if (drop_keys) {
7298                                 drop_keys[key] = false;
7299                             }
7300                             if (value) mapped.value = value;
7301                         }
7302                         if (trimmed) {
7303                             prop.value = trimmed;
7304                             properties.push(prop);
7305                         }
7306                     });
7307                     value = save_value;
7308                     drop = save_drop;
7309                     if (drop_keys && prop_keys) value.properties = List(value.properties, function(prop, index) {
7310                         if (prop instanceof AST_Spread) return prop;
7311                         var key = prop_keys[index];
7312                         if (key instanceof AST_Node) return prop;
7313                         if (key in drop_keys) {
7314                             var mapped = drop_keys[key];
7315                             if (!mapped) return prop;
7316                             if (mapped === prop) return prop_map[key] || List.skip;
7317                         } else if (node.rest) {
7318                             return prop;
7319                         }
7320                         var trimmed = prop.value.drop_side_effect_free(compressor);
7321                         if (trimmed) {
7322                             prop.value = trimmed;
7323                             return prop;
7324                         }
7325                         return retain_key(prop) ? make_node(AST_ObjectKeyVal, prop, {
7326                             key: prop.key,
7327                             value: make_node(AST_Number, prop, { value: 0 }),
7328                         }) : List.skip;
7329                     });
7330                     if (value && !node.rest) switch (properties.length) {
7331                       case 0:
7332                         if (value.may_throw_on_access(compressor, true)) break;
7333                         if (drop) value = value.drop_side_effect_free(compressor);
7334                         return null;
7335                       case 1:
7336                         if (!drop) break;
7337                         var prop = properties[0];
7338                         if (prop.key instanceof AST_Node) break;
7339                         if (prop.value.has_side_effects(compressor)) break;
7340                         value = make_node(AST_Sub, node, {
7341                             expression: value,
7342                             property: make_node_from_constant(prop.key, prop),
7343                         });
7344                         return prop.value;
7345                     }
7346                     node.properties = properties;
7347                     return node;
7348                 }
7349                 if (node instanceof AST_Hole) {
7350                     node = null;
7351                 } else {
7352                     node = process(node);
7353                 }
7354                 if (!node && drop && value) value = value.drop_side_effect_free(compressor);
7355                 return node;
7356             });
7357             return {
7358                 name: node.transform(trimmer),
7359                 value: value,
7360             };
7361
7362             function retain_key(prop) {
7363                 return prop.key instanceof AST_Node && prop.key.has_side_effects(compressor);
7364             }
7365
7366             function retain_lhs(node) {
7367                 if (node instanceof AST_DefaultValue) return retain_lhs(node.name);
7368                 if (node instanceof AST_Destructured) {
7369                     if (value === null) {
7370                         value = make_node(AST_Number, node, { value: 0 });
7371                     } else if (value && (value.tail_node().write_only === true
7372                         || value.may_throw_on_access(compressor, true))) {
7373                         value = make_node(AST_Array, node, {
7374                             elements: value instanceof AST_Sequence ? value.expressions : [ value ],
7375                         });
7376                     }
7377                     return make_node(AST_DestructuredObject, node, { properties: [] });
7378                 }
7379                 node.__unused = null;
7380                 return node;
7381             }
7382         }
7383     });
7384
7385     AST_Scope.DEFMETHOD("hoist_declarations", function(compressor) {
7386         if (compressor.has_directive("use asm")) return;
7387         var hoist_funs = compressor.option("hoist_funs");
7388         var hoist_vars = compressor.option("hoist_vars");
7389         var self = this;
7390         if (hoist_vars) {
7391             // let's count var_decl first, we seem to waste a lot of
7392             // space if we hoist `var` when there's only one.
7393             var var_decl = 0;
7394             self.walk(new TreeWalker(function(node) {
7395                 if (var_decl > 1) return true;
7396                 if (node instanceof AST_ExportDeclaration) return true;
7397                 if (node instanceof AST_Scope && node !== self) return true;
7398                 if (node instanceof AST_Var) {
7399                     var_decl++;
7400                     return true;
7401                 }
7402             }));
7403             if (var_decl <= 1) hoist_vars = false;
7404         }
7405         if (!hoist_funs && !hoist_vars) return;
7406         var consts = Object.create(null);
7407         var dirs = [];
7408         var hoisted = [];
7409         var vars = new Dictionary(), vars_found = 0;
7410         var tt = new TreeTransformer(function(node, descend, in_list) {
7411             if (node === self) return;
7412             if (node instanceof AST_Directive) {
7413                 dirs.push(node);
7414                 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
7415             }
7416             if (node instanceof AST_LambdaDefinition) {
7417                 if (!hoist_funs) return node;
7418                 var p = tt.parent();
7419                 if (p instanceof AST_ExportDeclaration) return node;
7420                 if (p instanceof AST_ExportDefault) return node;
7421                 if (p !== self && compressor.has_directive("use strict")) return node;
7422                 hoisted.push(node);
7423                 return in_list ? List.skip : make_node(AST_EmptyStatement, node);
7424             }
7425             if (node instanceof AST_Var) {
7426                 if (!hoist_vars) return node;
7427                 var p = tt.parent();
7428                 if (p instanceof AST_ExportDeclaration) return node;
7429                 if (!all(node.definitions, function(defn) {
7430                     var sym = defn.name;
7431                     return sym instanceof AST_SymbolVar
7432                         && !consts[sym.name]
7433                         && self.find_variable(sym.name) === sym.definition();
7434                 })) return node;
7435                 node.definitions.forEach(function(def) {
7436                     vars.set(def.name.name, def);
7437                     ++vars_found;
7438                 });
7439                 var seq = node.to_assignments();
7440                 if (p instanceof AST_ForEnumeration && p.init === node) {
7441                     if (seq) return seq;
7442                     var def = node.definitions[0].name;
7443                     return make_node(AST_SymbolRef, def, def);
7444                 }
7445                 if (p instanceof AST_For && p.init === node) return seq;
7446                 if (!seq) return in_list ? List.skip : make_node(AST_EmptyStatement, node);
7447                 return make_node(AST_SimpleStatement, node, { body: seq });
7448             }
7449             if (node instanceof AST_Scope) return node;
7450             if (node instanceof AST_SymbolConst) {
7451                 consts[node.name] = true;
7452                 return node;
7453             }
7454         });
7455         self.transform(tt);
7456         if (vars_found > 0) {
7457             // collect only vars which don't show up in self's arguments list
7458             var defs = [];
7459             if (self instanceof AST_Lambda) self.each_argname(function(argname) {
7460                 vars.del(argname.name);
7461             });
7462             vars.each(function(def, name) {
7463                 def = def.clone();
7464                 def.value = null;
7465                 defs.push(def);
7466                 vars.set(name, def);
7467             });
7468             if (defs.length > 0) {
7469                 // try to merge in assignments
7470                 insert_vars(self.body);
7471                 defs = make_node(AST_Var, self, { definitions: defs });
7472                 hoisted.push(defs);
7473             }
7474         }
7475         self.body = dirs.concat(hoisted, self.body);
7476
7477         function insert_vars(body) {
7478             while (body.length) {
7479                 var stat = body[0];
7480                 if (stat instanceof AST_SimpleStatement) {
7481                     var expr = stat.body, sym, assign;
7482                     if (expr instanceof AST_Assign
7483                         && expr.operator == "="
7484                         && (sym = expr.left) instanceof AST_Symbol
7485                         && vars.has(sym.name)) {
7486                         var def = vars.get(sym.name);
7487                         if (def.value) break;
7488                         var value = expr.right;
7489                         if (value instanceof AST_Sequence) value = value.clone();
7490                         def.value = value;
7491                         remove(defs, def);
7492                         defs.push(def);
7493                         body.shift();
7494                         continue;
7495                     }
7496                     if (expr instanceof AST_Sequence
7497                         && (assign = expr.expressions[0]) instanceof AST_Assign
7498                         && assign.operator == "="
7499                         && (sym = assign.left) instanceof AST_Symbol
7500                         && vars.has(sym.name)) {
7501                         var def = vars.get(sym.name);
7502                         if (def.value) break;
7503                         def.value = assign.right;
7504                         remove(defs, def);
7505                         defs.push(def);
7506                         stat.body = make_sequence(expr, expr.expressions.slice(1));
7507                         continue;
7508                     }
7509                 }
7510                 if (stat instanceof AST_EmptyStatement) {
7511                     body.shift();
7512                     continue;
7513                 }
7514                 if (stat instanceof AST_BlockStatement && !insert_vars(stat.body)) {
7515                     body.shift();
7516                     continue;
7517                 }
7518                 break;
7519             }
7520             return body.length;
7521         }
7522     });
7523
7524     function scan_local_returns(fn, transform) {
7525         fn.walk(new TreeWalker(function(node) {
7526             if (node instanceof AST_Return) {
7527                 transform(node);
7528                 return true;
7529             }
7530             if (node instanceof AST_Scope && node !== fn) return true;
7531         }));
7532     }
7533
7534     function map_bool_returns(fn) {
7535         var map = Object.create(null);
7536         scan_local_returns(fn, function(node) {
7537             var value = node.value;
7538             if (value) value = value.tail_node();
7539             if (value instanceof AST_SymbolRef) {
7540                 var id = value.definition().id;
7541                 map[id] = (map[id] || 0) + 1;
7542             }
7543         });
7544         return map;
7545     }
7546
7547     function all_bool(def, bool_returns, compressor) {
7548         return def.bool_fn + (bool_returns[def.id] || 0) === def.references.length - def.replaced
7549             && !compressor.exposed(def);
7550     }
7551
7552     function process_boolean_returns(fn, compressor) {
7553         scan_local_returns(fn, function(node) {
7554             node.in_bool = true;
7555             var value = node.value;
7556             if (value) {
7557                 var ev = fuzzy_eval(compressor, value);
7558                 if (!ev) {
7559                     value = value.drop_side_effect_free(compressor);
7560                     node.value = value ? make_sequence(node.value, [
7561                         value,
7562                         make_node(AST_Number, node.value, { value: 0 }),
7563                     ]) : null;
7564                 } else if (!(ev instanceof AST_Node)) {
7565                     value = value.drop_side_effect_free(compressor);
7566                     node.value = value ? make_sequence(node.value, [
7567                         value,
7568                         make_node(AST_Number, node.value, { value: 1 }),
7569                     ]) : make_node(AST_Number, node.value, { value: 1 });
7570                 }
7571             }
7572         });
7573     }
7574
7575     AST_Scope.DEFMETHOD("process_boolean_returns", noop);
7576     AST_Defun.DEFMETHOD("process_boolean_returns", function(compressor) {
7577         if (!compressor.option("booleans")) return;
7578         var bool_returns = map_bool_returns(this);
7579         if (!all_bool(this.name.definition(), bool_returns, compressor)) return;
7580         if (compressor.parent() instanceof AST_ExportDefault) return;
7581         process_boolean_returns(this, compressor);
7582     });
7583     AST_Function.DEFMETHOD("process_boolean_returns", function(compressor) {
7584         if (!compressor.option("booleans")) return;
7585         var bool_returns = map_bool_returns(this);
7586         if (this.name && !all_bool(this.name.definition(), bool_returns, compressor)) return;
7587         var parent = compressor.parent();
7588         if (parent instanceof AST_Assign) {
7589             if (parent.operator != "=") return;
7590             var sym = parent.left;
7591             if (!(sym instanceof AST_SymbolRef)) return;
7592             if (!all_bool(sym.definition(), bool_returns, compressor)) return;
7593         } else if (parent instanceof AST_Call && parent.expression !== this) {
7594             var exp = parent.expression;
7595             if (exp instanceof AST_SymbolRef) exp = exp.fixed_value();
7596             if (!(exp instanceof AST_Lambda)) return;
7597             if (exp.uses_arguments || exp.pinned()) return;
7598             var sym = exp.argnames[parent.args.indexOf(this)];
7599             if (sym instanceof AST_DefaultValue) sym = sym.name;
7600             if (sym instanceof AST_SymbolFunarg && !all_bool(sym.definition(), bool_returns, compressor)) return;
7601         } else if (parent.TYPE == "Call") {
7602             compressor.pop();
7603             var in_bool = compressor.in_boolean_context();
7604             compressor.push(this);
7605             if (!in_bool) return;
7606         } else return;
7607         process_boolean_returns(this, compressor);
7608     });
7609
7610     AST_BlockScope.DEFMETHOD("var_names", function() {
7611         var var_names = this._var_names;
7612         if (!var_names) {
7613             this._var_names = var_names = Object.create(null);
7614             this.enclosed.forEach(function(def) {
7615                 var_names[def.name] = true;
7616             });
7617             this.variables.each(function(def, name) {
7618                 var_names[name] = true;
7619             });
7620         }
7621         return var_names;
7622     });
7623
7624     AST_Scope.DEFMETHOD("make_var", function(type, orig, prefix) {
7625         var scopes = [ this ];
7626         if (orig instanceof AST_SymbolDeclaration) orig.definition().references.forEach(function(ref) {
7627             var s = ref.scope;
7628             if (member(s, scopes)) return;
7629             do {
7630                 push_uniq(scopes, s);
7631                 s = s.parent_scope;
7632             } while (s && s !== this);
7633         });
7634         prefix = prefix.replace(/(?:^[^a-z_$]|[^a-z0-9_$])/ig, "_");
7635         var name = prefix;
7636         for (var i = 0; !all(scopes, function(scope) {
7637             return !scope.var_names()[name];
7638         }); i++) name = prefix + "$" + i;
7639         var sym = make_node(type, orig, {
7640             name: name,
7641             scope: this,
7642         });
7643         var def = this.def_variable(sym);
7644         scopes.forEach(function(scope) {
7645             scope.enclosed.push(def);
7646             scope.var_names()[name] = true;
7647         });
7648         return sym;
7649     });
7650
7651     AST_Scope.DEFMETHOD("hoist_properties", function(compressor) {
7652         if (!compressor.option("hoist_props") || compressor.has_directive("use asm")) return;
7653         var self = this;
7654         var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false;
7655         var defs_by_id = Object.create(null);
7656         self.transform(new TreeTransformer(function(node, descend) {
7657             if (node instanceof AST_Assign) {
7658                 if (node.operator != "=") return;
7659                 if (!node.write_only) return;
7660                 if (!can_hoist(node.left, node.right, 1)) return;
7661                 descend(node, this);
7662                 var defs = new Dictionary();
7663                 var assignments = [];
7664                 var decls = [];
7665                 node.right.properties.forEach(function(prop) {
7666                     var decl = make_sym(AST_SymbolVar, node.left, prop.key);
7667                     decls.push(make_node(AST_VarDef, node, {
7668                         name: decl,
7669                         value: null,
7670                     }));
7671                     var sym = make_node(AST_SymbolRef, node, {
7672                         name: decl.name,
7673                         scope: self,
7674                         thedef: decl.definition(),
7675                     });
7676                     sym.reference();
7677                     assignments.push(make_node(AST_Assign, node, {
7678                         operator: "=",
7679                         left: sym,
7680                         right: prop.value,
7681                     }));
7682                 });
7683                 defs.value = node.right;
7684                 defs_by_id[node.left.definition().id] = defs;
7685                 self.body.splice(self.body.indexOf(this.stack[1]) + 1, 0, make_node(AST_Var, node, {
7686                     definitions: decls,
7687                 }));
7688                 return make_sequence(node, assignments);
7689             }
7690             if (node instanceof AST_Scope) return node === self ? undefined : node;
7691             if (node instanceof AST_VarDef) {
7692                 if (!can_hoist(node.name, node.value, 0)) return;
7693                 descend(node, this);
7694                 var defs = new Dictionary();
7695                 var var_defs = [];
7696                 var decl = node.clone();
7697                 decl.value = node.name instanceof AST_SymbolConst ? make_node(AST_Number, node, { value: 0 }) : null;
7698                 var_defs.push(decl);
7699                 node.value.properties.forEach(function(prop) {
7700                     var_defs.push(make_node(AST_VarDef, node, {
7701                         name: make_sym(node.name.CTOR, node.name, prop.key),
7702                         value: prop.value,
7703                     }));
7704                 });
7705                 defs.value = node.value;
7706                 defs_by_id[node.name.definition().id] = defs;
7707                 return List.splice(var_defs);
7708             }
7709
7710             function make_sym(type, sym, key) {
7711                 var new_var = self.make_var(type, sym, sym.name + "_" + key);
7712                 defs.set(key, new_var.definition());
7713                 return new_var;
7714             }
7715         }));
7716         self.transform(new TreeTransformer(function(node, descend) {
7717             if (node instanceof AST_PropAccess) {
7718                 if (!(node.expression instanceof AST_SymbolRef)) return;
7719                 var defs = defs_by_id[node.expression.definition().id];
7720                 if (!defs) return;
7721                 if (node.expression.fixed_value() !== defs.value) return;
7722                 var def = defs.get(node.get_property());
7723                 var sym = make_node(AST_SymbolRef, node, {
7724                     name: def.name,
7725                     scope: node.expression.scope,
7726                     thedef: def,
7727                 });
7728                 sym.reference();
7729                 return sym;
7730             }
7731             if (node instanceof AST_SymbolRef) {
7732                 var defs = defs_by_id[node.definition().id];
7733                 if (!defs) return;
7734                 if (node.fixed_value() !== defs.value) return;
7735                 return make_node(AST_Object, node, { properties: [] });
7736             }
7737         }));
7738
7739         function can_hoist(sym, right, count) {
7740             if (!(sym instanceof AST_Symbol)) return;
7741             var def = sym.definition();
7742             if (def.assignments != count) return;
7743             if (def.references.length - def.replaced == count) return;
7744             if (def.single_use) return;
7745             if (top_retain(def)) return;
7746             if (sym.fixed_value() !== right) return;
7747             var fixed = sym.fixed || def.fixed;
7748             if (fixed.direct_access) return;
7749             if (fixed.escaped && fixed.escaped.depth == 1) return;
7750             return right instanceof AST_Object
7751                 && right.properties.length > 0
7752                 && all(right.properties, can_hoist_property)
7753                 && can_drop_symbol(sym, compressor);
7754         }
7755     });
7756
7757     function fn_name_unused(fn, compressor) {
7758         if (!fn.name || !compressor.option("ie")) return true;
7759         var def = fn.name.definition();
7760         if (compressor.exposed(def)) return false;
7761         return all(def.references, function(sym) {
7762             return !(sym instanceof AST_SymbolRef);
7763         });
7764     }
7765
7766     // drop_side_effect_free()
7767     // remove side-effect-free parts which only affects return value
7768     (function(def) {
7769         // Drop side-effect-free elements from an array of expressions.
7770         // Returns an array of expressions with side-effects or null
7771         // if all elements were dropped. Note: original array may be
7772         // returned if nothing changed.
7773         function trim(nodes, compressor, first_in_statement, spread) {
7774             var len = nodes.length;
7775             var ret = [], changed = false;
7776             for (var i = 0; i < len; i++) {
7777                 var node = nodes[i];
7778                 var trimmed;
7779                 if (spread && node instanceof AST_Spread) {
7780                     trimmed = spread(node, compressor, first_in_statement);
7781                 } else {
7782                     trimmed = node.drop_side_effect_free(compressor, first_in_statement);
7783                 }
7784                 if (trimmed !== node) changed = true;
7785                 if (trimmed) {
7786                     ret.push(trimmed);
7787                     first_in_statement = false;
7788                 }
7789             }
7790             return ret.length ? changed ? ret : nodes : null;
7791         }
7792         function array_spread(node, compressor, first_in_statement) {
7793             var exp = node.expression;
7794             if (!exp.is_string(compressor)) return node;
7795             return exp.drop_side_effect_free(compressor, first_in_statement);
7796         }
7797         function convert_spread(node) {
7798             return node instanceof AST_Spread ? make_node(AST_Array, node, {
7799                 elements: [ node ]
7800             }) : node;
7801         }
7802         def(AST_Node, return_this);
7803         def(AST_Accessor, return_null);
7804         def(AST_Array, function(compressor, first_in_statement) {
7805             var values = trim(this.elements, compressor, first_in_statement, array_spread);
7806             if (!values) return null;
7807             if (values === this.elements && all(values, function(node) {
7808                 return node instanceof AST_Spread;
7809             })) return this;
7810             return make_sequence(this, values.map(convert_spread));
7811         });
7812         def(AST_Assign, function(compressor) {
7813             var left = this.left;
7814             if (left instanceof AST_PropAccess) {
7815                 var expr = left.expression;
7816                 if (expr.may_throw_on_access(compressor, true)) return this;
7817                 if (compressor.has_directive("use strict") && expr.is_constant()) return this;
7818             }
7819             if (left.has_side_effects(compressor)) return this;
7820             if (lazy_op[this.operator.slice(0, -1)]) return this;
7821             this.write_only = true;
7822             if (!root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) return this;
7823             return this.right.drop_side_effect_free(compressor);
7824         });
7825         def(AST_Await, function(compressor) {
7826             if (!compressor.option("awaits")) return this;
7827             var exp = this.expression;
7828             if (!is_primitive(compressor, exp)) return this;
7829             var node = this.clone();
7830             node.expression = exp.drop_side_effect_free(compressor) || make_node(AST_Number, this, { value: 0 });
7831             return node;
7832         });
7833         def(AST_Binary, function(compressor, first_in_statement) {
7834             var left = this.left;
7835             var right = this.right;
7836             var op = this.operator;
7837             if (op == "in" && !is_object(right)) {
7838                 var lhs = left.drop_side_effect_free(compressor, first_in_statement);
7839                 if (lhs === left) return this;
7840                 var node = this.clone();
7841                 node.left = lhs || make_node(AST_Number, left, { value: 0 });
7842                 return node;
7843             }
7844             var rhs = right.drop_side_effect_free(compressor, first_in_statement);
7845             if (!rhs) return left.drop_side_effect_free(compressor, first_in_statement);
7846             if (lazy_op[op] && rhs.has_side_effects(compressor)) {
7847                 var node = this;
7848                 if (rhs !== right) {
7849                     node = node.clone();
7850                     node.right = rhs.drop_side_effect_free(compressor);
7851                 }
7852                 if (op == "??") return node;
7853                 var negated = make_node(AST_Binary, this, {
7854                     operator: op == "&&" ? "||" : "&&",
7855                     left: left.negate(compressor, first_in_statement),
7856                     right: node.right,
7857                 });
7858                 return first_in_statement ? best_of_statement(node, negated) : best_of_expression(node, negated);
7859             }
7860             var lhs = left.drop_side_effect_free(compressor, first_in_statement);
7861             if (!lhs) return rhs;
7862             rhs = rhs.drop_side_effect_free(compressor);
7863             if (!rhs) return lhs;
7864             return make_sequence(this, [ lhs, rhs ]);
7865         });
7866         function drop_returns(compressor, exp) {
7867             var arrow = is_arrow(exp);
7868             var async = is_async(exp);
7869             var drop_body = false;
7870             if (arrow && compressor.option("arrows")) {
7871                 if (!exp.value) {
7872                     drop_body = true;
7873                 } else if (!async || is_primitive(compressor, exp.value)) {
7874                     exp.value = exp.value.drop_side_effect_free(compressor);
7875                 }
7876             } else if (exp instanceof AST_AsyncFunction || exp instanceof AST_Function) {
7877                 if (exp.name) {
7878                     var def = exp.name.definition();
7879                     drop_body = def.references.length == def.replaced;
7880                 } else {
7881                     drop_body = true;
7882                 }
7883             }
7884             if (drop_body) {
7885                 exp.process_expression(false, function(node) {
7886                     var value = node.value;
7887                     if (value) {
7888                         if (async && !is_primitive(compressor, value)) return node;
7889                         value = value.drop_side_effect_free(compressor, true);
7890                     }
7891                     if (!value) return make_node(AST_EmptyStatement, node);
7892                     return make_node(AST_SimpleStatement, node, { body: value });
7893                 });
7894                 scan_local_returns(exp, function(node) {
7895                     var value = node.value;
7896                     if (value) {
7897                         if (async && !is_primitive(compressor, value)) return;
7898                         node.value = value.drop_side_effect_free(compressor);
7899                     }
7900                 });
7901             }
7902             if (async && compressor.option("awaits")) {
7903                 if (drop_body) exp.process_expression("awaits", function(node) {
7904                     var body = node.body;
7905                     if (body instanceof AST_Await) {
7906                         if (is_primitive(compressor, body.expression)) {
7907                             body = body.expression.drop_side_effect_free(compressor, true);
7908                             if (!body) return make_node(AST_EmptyStatement, node);
7909                             node.body = body;
7910                         }
7911                     } else if (body instanceof AST_Sequence) {
7912                         var exprs = body.expressions;
7913                         for (var i = exprs.length; --i >= 0;) {
7914                             var tail = exprs[i];
7915                             if (!(tail instanceof AST_Await)) break;
7916                             if (!is_primitive(compressor, tail.expression)) break;
7917                             if (exprs[i] = tail.expression.drop_side_effect_free(compressor)) break;
7918                         }
7919                         switch (i) {
7920                           case -1:
7921                             return make_node(AST_EmptyStatement, node);
7922                           case 0:
7923                             node.body = exprs[0];
7924                             break;
7925                           default:
7926                             exprs.length = i + 1;
7927                             break;
7928                         }
7929                     }
7930                     return node;
7931                 });
7932                 var abort = !drop_body && exp.name || arrow && exp.value && !is_primitive(compressor, exp.value);
7933                 var tw = new TreeWalker(function(node) {
7934                     if (abort) return true;
7935                     if (tw.parent() === exp && node.may_throw(compressor)) return abort = true;
7936                     if (node instanceof AST_Await) return abort = true;
7937                     if (node instanceof AST_ForAwaitOf) return abort = true;
7938                     if (node instanceof AST_Return) {
7939                         if (node.value && !is_primitive(compressor, node.value)) return abort = true;
7940                         return;
7941                     }
7942                     if (node instanceof AST_Scope && node !== exp) return true;
7943                 });
7944                 exp.walk(tw);
7945                 if (!abort) {
7946                     var ctor;
7947                     switch (exp.CTOR) {
7948                       case AST_AsyncArrow:
7949                         ctor = AST_Arrow;
7950                         break;
7951                       case AST_AsyncFunction:
7952                         ctor = AST_Function;
7953                         break;
7954                       case AST_AsyncGeneratorFunction:
7955                         ctor = AST_GeneratorFunction;
7956                         break;
7957                     }
7958                     return make_node(ctor, exp, exp);
7959                 }
7960             }
7961             return drop_body && exp.clone();
7962         }
7963         def(AST_Call, function(compressor, first_in_statement) {
7964             var self = this;
7965             if (self.is_expr_pure(compressor)) {
7966                 if (self.pure) AST_Node.warn("Dropping __PURE__ call [{file}:{line},{col}]", self.start);
7967                 var args = trim(self.args, compressor, first_in_statement, array_spread);
7968                 return args && make_sequence(self, args.map(convert_spread));
7969             }
7970             var exp = self.expression;
7971             if (self.is_call_pure(compressor)) {
7972                 var exprs = self.args.slice();
7973                 exprs.unshift(exp.expression);
7974                 exprs = trim(exprs, compressor, first_in_statement, array_spread);
7975                 return exprs && make_sequence(self, exprs.map(convert_spread));
7976             }
7977             if (compressor.option("yields") && is_generator(exp)) {
7978                 var call = self.clone();
7979                 call.expression = make_node(AST_Function, exp, exp);
7980                 call.expression.body = [];
7981                 var opt = call.transform(compressor);
7982                 if (opt !== call) return opt.drop_side_effect_free(compressor, first_in_statement);
7983             }
7984             var dropped = drop_returns(compressor, exp);
7985             if (dropped) {
7986                 // always shallow clone to ensure stripping of negated IIFEs
7987                 self = self.clone();
7988                 self.expression = dropped;
7989                 // avoid extraneous traversal
7990                 if (exp._squeezed) self.expression._squeezed = true;
7991             }
7992             if (self instanceof AST_New) {
7993                 var fn = exp;
7994                 if (fn instanceof AST_SymbolRef) fn = fn.fixed_value();
7995                 if (fn instanceof AST_Lambda) {
7996                     if (assign_this_only(fn, compressor)) {
7997                         var exprs = self.args.slice();
7998                         exprs.unshift(exp);
7999                         exprs = trim(exprs, compressor, first_in_statement, array_spread);
8000                         return exprs && make_sequence(self, exprs.map(convert_spread));
8001                     }
8002                     if (!fn.contains_this()) self = make_node(AST_Call, self, self);
8003                 }
8004             }
8005             self.call_only = true;
8006             return self;
8007         });
8008         function assign_this_only(fn, compressor) {
8009             fn.new = true;
8010             var result = all(fn.body, function(stat) {
8011                 return !stat.has_side_effects(compressor);
8012             }) && all(fn.argnames, function(argname) {
8013                 return !argname.match_symbol(return_false);
8014             }) && !(fn.rest && fn.rest.match_symbol(return_false));
8015             delete fn.new;
8016             return result;
8017         }
8018         function drop_class(self, compressor, first_in_statement) {
8019             var exprs = [], values = [];
8020             var props = self.properties;
8021             for (var i = 0; i < props.length; i++) {
8022                 var prop = props[i];
8023                 if (prop.key instanceof AST_Node) exprs.push(prop.key);
8024                 if (prop.static && prop.value
8025                     && prop instanceof AST_ClassField
8026                     && prop.value.has_side_effects(compressor)) {
8027                     if (prop.value.contains_this()) return self;
8028                     values.push(prop.value);
8029                 }
8030             }
8031             var base = self.extends;
8032             if (base) {
8033                 if (base instanceof AST_SymbolRef) base = base.fixed_value();
8034                 base = !safe_for_extends(base);
8035                 if (!base) exprs.unshift(self.extends);
8036             }
8037             exprs = trim(exprs, compressor, first_in_statement);
8038             if (exprs) first_in_statement = false;
8039             values = trim(values, compressor, first_in_statement);
8040             if (!exprs) {
8041                 if (!base && !values) return null;
8042                 exprs = [];
8043             }
8044             if (base) {
8045                 var node = to_class_expr(self, true);
8046                 node.properties = [];
8047                 if (exprs.length) node.properties.push(make_node(AST_ClassMethod, self, {
8048                     key: make_sequence(self, exprs),
8049                     value: make_node(AST_Function, self, {
8050                         argnames: [],
8051                         body: [],
8052                     }).init_vars(node),
8053                 }));
8054                 exprs = [ node ];
8055             }
8056             if (values) exprs.push(make_node(AST_Call, self, {
8057                 expression: make_node(AST_Arrow, self, {
8058                     argnames: [],
8059                     body: [],
8060                     value: make_sequence(self, values),
8061                 }).init_vars(self.parent_scope),
8062                 args: [],
8063             }));
8064             return make_sequence(self, exprs);
8065         }
8066         def(AST_ClassExpression, function(compressor, first_in_statement) {
8067             var self = this;
8068             var name = self.name;
8069             if (name && name.fixed_value() !== self && name.definition().references.length > 0) return self;
8070             return drop_class(self, compressor, first_in_statement);
8071         });
8072         def(AST_Conditional, function(compressor) {
8073             var consequent = this.consequent.drop_side_effect_free(compressor);
8074             var alternative = this.alternative.drop_side_effect_free(compressor);
8075             if (consequent === this.consequent && alternative === this.alternative) return this;
8076             var exprs;
8077             if (compressor.option("ie")) {
8078                 exprs = [];
8079                 if (consequent instanceof AST_Function) {
8080                     exprs.push(consequent);
8081                     consequent = null;
8082                 }
8083                 if (alternative instanceof AST_Function) {
8084                     exprs.push(alternative);
8085                     alternative = null;
8086                 }
8087             }
8088             var node;
8089             if (!consequent) {
8090                 node = alternative ? make_node(AST_Binary, this, {
8091                     operator: "||",
8092                     left: this.condition,
8093                     right: alternative
8094                 }) : this.condition.drop_side_effect_free(compressor);
8095             } else if (!alternative) {
8096                 node = make_node(AST_Binary, this, {
8097                     operator: "&&",
8098                     left: this.condition,
8099                     right: consequent
8100                 });
8101             } else {
8102                 node = this.clone();
8103                 node.consequent = consequent;
8104                 node.alternative = alternative;
8105             }
8106             if (!compressor.option("ie")) return node;
8107             if (node) exprs.push(node);
8108             return exprs.length == 0 ? null : make_sequence(this, exprs);
8109         });
8110         def(AST_Constant, return_null);
8111         def(AST_DefClass, function(compressor, first_in_statement) {
8112             return drop_class(this, compressor, first_in_statement);
8113         });
8114         def(AST_Dot, function(compressor, first_in_statement) {
8115             var expr = this.expression;
8116             if (!this.optional && expr.may_throw_on_access(compressor)) return this;
8117             return expr.drop_side_effect_free(compressor, first_in_statement);
8118         });
8119         def(AST_Function, function(compressor) {
8120             return fn_name_unused(this, compressor) ? null : this;
8121         });
8122         def(AST_LambdaExpression, return_null);
8123         def(AST_Object, function(compressor, first_in_statement) {
8124             var exprs = [];
8125             this.properties.forEach(function(prop) {
8126                 if (prop instanceof AST_Spread) {
8127                     exprs.push(prop);
8128                 } else {
8129                     if (prop.key instanceof AST_Node) exprs.push(prop.key);
8130                     exprs.push(prop.value);
8131                 }
8132             });
8133             var values = trim(exprs, compressor, first_in_statement, function(node, compressor, first_in_statement) {
8134                 var exp = node.expression;
8135                 return spread_side_effects(exp) ? node : exp.drop_side_effect_free(compressor, first_in_statement);
8136             });
8137             if (!values) return null;
8138             if (values === exprs && !all(values, function(node) {
8139                 return !(node instanceof AST_Spread);
8140             })) return this;
8141             return make_sequence(this, values.map(function(node) {
8142                 return node instanceof AST_Spread ? make_node(AST_Object, node, {
8143                     properties: [ node ],
8144                 }) : node;
8145             }));
8146         });
8147         def(AST_ObjectIdentity, return_null);
8148         def(AST_Sequence, function(compressor, first_in_statement) {
8149             var expressions = trim(this.expressions, compressor, first_in_statement);
8150             if (!expressions) return null;
8151             var end = expressions.length - 1;
8152             var last = expressions[end];
8153             if (compressor.option("awaits") && end > 0 && last instanceof AST_Await && last.expression.is_constant()) {
8154                 expressions = expressions.slice(0, -1);
8155                 end--;
8156                 last.expression = expressions[end];
8157                 expressions[end] = last;
8158             }
8159             var assign, cond, lhs;
8160             if (compressor.option("conditionals")
8161                 && end > 0
8162                 && (assign = expressions[end - 1]) instanceof AST_Assign
8163                 && assign.operator == "="
8164                 && (lhs = assign.left) instanceof AST_SymbolRef
8165                 && (cond = to_conditional_assignment(compressor, lhs.definition(), assign.right, last))) {
8166                 assign = assign.clone();
8167                 assign.right = cond;
8168                 expressions = expressions.slice(0, -2);
8169                 expressions.push(assign.drop_side_effect_free(compressor, first_in_statement));
8170             }
8171             return expressions === this.expressions ? this : make_sequence(this, expressions);
8172         });
8173         def(AST_Sub, function(compressor, first_in_statement) {
8174             var expr = this.expression;
8175             var prop = this.property;
8176             if (expr.may_throw_on_access(compressor)) {
8177                 if (!this.optional) return this;
8178                 if (prop.has_side_effects(compressor)) {
8179                     prop = prop.drop_side_effect_free(compressor);
8180                     if (!prop) return expr.drop_side_effect_free(compressor, first_in_statement);
8181                     var node = this.clone();
8182                     node.property = prop;
8183                     return node;
8184                 }
8185             }
8186             expr = expr.drop_side_effect_free(compressor, first_in_statement);
8187             if (!expr) return prop.drop_side_effect_free(compressor, first_in_statement);
8188             prop = prop.drop_side_effect_free(compressor);
8189             if (!prop) return expr;
8190             return make_sequence(this, [ expr, prop ]);
8191         });
8192         def(AST_SymbolRef, function(compressor) {
8193             return this.is_declared(compressor) && can_drop_symbol(this, compressor) ? null : this;
8194         });
8195         def(AST_Template, function(compressor, first_in_statement) {
8196             var self = this;
8197             if (self.is_expr_pure(compressor)) {
8198                 var expressions = self.expressions;
8199                 if (expressions.length == 0) return null;
8200                 return make_sequence(self, expressions).drop_side_effect_free(compressor, first_in_statement);
8201             }
8202             var tag = self.tag;
8203             var dropped = drop_returns(compressor, tag);
8204             if (dropped) {
8205                 // always shallow clone to signal internal changes
8206                 self = self.clone();
8207                 self.tag = dropped;
8208                 // avoid extraneous traversal
8209                 if (tag._squeezed) self.tag._squeezed = true;
8210             }
8211             return self;
8212         });
8213         def(AST_Unary, function(compressor, first_in_statement) {
8214             var exp = this.expression;
8215             if (unary_side_effects[this.operator]) {
8216                 this.write_only = !exp.has_side_effects(compressor);
8217                 return this;
8218             }
8219             if (this.operator == "typeof" && exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor)) {
8220                 return null;
8221             }
8222             var node = exp.drop_side_effect_free(compressor, first_in_statement);
8223             if (first_in_statement && node && is_iife_call(node)) {
8224                 if (node === exp && this.operator == "!") return this;
8225                 return node.negate(compressor, first_in_statement);
8226             }
8227             return node;
8228         });
8229     })(function(node, func) {
8230         node.DEFMETHOD("drop_side_effect_free", func);
8231     });
8232
8233     OPT(AST_SimpleStatement, function(self, compressor) {
8234         if (compressor.option("side_effects")) {
8235             var body = self.body;
8236             var node = body.drop_side_effect_free(compressor, true);
8237             if (!node) {
8238                 AST_Node.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
8239                 return make_node(AST_EmptyStatement, self);
8240             }
8241             if (node !== body) {
8242                 return make_node(AST_SimpleStatement, self, { body: node });
8243             }
8244         }
8245         return self;
8246     });
8247
8248     OPT(AST_While, function(self, compressor) {
8249         return compressor.option("loops") ? make_node(AST_For, self, self).optimize(compressor) : self;
8250     });
8251
8252     function has_loop_control(loop, parent, type) {
8253         if (!type) type = AST_LoopControl;
8254         var found = false;
8255         var tw = new TreeWalker(function(node) {
8256             if (found || node instanceof AST_Scope) return true;
8257             if (node instanceof type && tw.loopcontrol_target(node) === loop) {
8258                 return found = true;
8259             }
8260         });
8261         if (parent instanceof AST_LabeledStatement) tw.push(parent);
8262         tw.push(loop);
8263         loop.body.walk(tw);
8264         return found;
8265     }
8266
8267     OPT(AST_Do, function(self, compressor) {
8268         if (!compressor.option("loops")) return self;
8269         var cond = fuzzy_eval(compressor, self.condition);
8270         if (!(cond instanceof AST_Node)) {
8271             if (cond && !has_loop_control(self, compressor.parent(), AST_Continue)) return make_node(AST_For, self, {
8272                 body: make_node(AST_BlockStatement, self.body, {
8273                     body: [
8274                         self.body,
8275                         make_node(AST_SimpleStatement, self.condition, {
8276                             body: self.condition
8277                         }),
8278                     ]
8279                 })
8280             }).optimize(compressor);
8281             if (!has_loop_control(self, compressor.parent())) return make_node(AST_BlockStatement, self.body, {
8282                 body: [
8283                     self.body,
8284                     make_node(AST_SimpleStatement, self.condition, {
8285                         body: self.condition
8286                     }),
8287                 ]
8288             }).optimize(compressor);
8289         }
8290         if (self.body instanceof AST_BlockStatement && !has_loop_control(self, compressor.parent(), AST_Continue)) {
8291             var body = self.body.body;
8292             for (var i = body.length; --i >= 0;) {
8293                 var stat = body[i];
8294                 if (stat instanceof AST_If
8295                     && !stat.alternative
8296                     && stat.body instanceof AST_Break
8297                     && compressor.loopcontrol_target(stat.body) === self) {
8298                     if (has_block_scope_refs(stat.condition)) break;
8299                     self.condition = make_node(AST_Binary, self, {
8300                         operator: "&&",
8301                         left: stat.condition.negate(compressor),
8302                         right: self.condition,
8303                     });
8304                     body.splice(i, 1);
8305                 } else if (stat instanceof AST_SimpleStatement) {
8306                     if (has_block_scope_refs(stat.body)) break;
8307                     self.condition = make_sequence(self, [
8308                         stat.body,
8309                         self.condition,
8310                     ]);
8311                     body.splice(i, 1);
8312                 } else if (!is_declaration(stat, true)) {
8313                     break;
8314                 }
8315             }
8316             self.body = trim_block(self.body, compressor.parent());
8317         }
8318         if (self.body instanceof AST_EmptyStatement) return make_node(AST_For, self, self).optimize(compressor);
8319         if (self.body instanceof AST_SimpleStatement) return make_node(AST_For, self, {
8320             condition: make_sequence(self.condition, [
8321                 self.body.body,
8322                 self.condition
8323             ]),
8324             body: make_node(AST_EmptyStatement, self)
8325         }).optimize(compressor);
8326         return self;
8327
8328         function has_block_scope_refs(node) {
8329             var found = false;
8330             node.walk(new TreeWalker(function(node) {
8331                 if (found) return true;
8332                 if (node instanceof AST_SymbolRef) {
8333                     if (!member(node.definition(), self.enclosed)) found = true;
8334                     return true;
8335                 }
8336             }));
8337             return found;
8338         }
8339     });
8340
8341     function if_break_in_loop(self, compressor) {
8342         var first = first_statement(self.body);
8343         if (compressor.option("dead_code")
8344             && (first instanceof AST_Break
8345                 || first instanceof AST_Continue && external_target(first)
8346                 || first instanceof AST_Exit)) {
8347             var body = [];
8348             if (is_statement(self.init)) {
8349                 body.push(self.init);
8350             } else if (self.init) {
8351                 body.push(make_node(AST_SimpleStatement, self.init, {
8352                     body: self.init
8353                 }));
8354             }
8355             var retain = external_target(first) || first instanceof AST_Exit;
8356             if (self.condition && retain) {
8357                 body.push(make_node(AST_If, self, {
8358                     condition: self.condition,
8359                     body: first,
8360                     alternative: null
8361                 }));
8362             } else if (self.condition) {
8363                 body.push(make_node(AST_SimpleStatement, self.condition, {
8364                     body: self.condition
8365                 }));
8366             } else if (retain) {
8367                 body.push(first);
8368             }
8369             extract_declarations_from_unreachable_code(compressor, self.body, body);
8370             return make_node(AST_BlockStatement, self, {
8371                 body: body
8372             });
8373         }
8374         if (first instanceof AST_If) {
8375             var ab = first_statement(first.body);
8376             if (ab instanceof AST_Break && !external_target(ab)) {
8377                 if (self.condition) {
8378                     self.condition = make_node(AST_Binary, self.condition, {
8379                         left: self.condition,
8380                         operator: "&&",
8381                         right: first.condition.negate(compressor),
8382                     });
8383                 } else {
8384                     self.condition = first.condition.negate(compressor);
8385                 }
8386                 var body = as_statement_array(first.alternative);
8387                 extract_declarations_from_unreachable_code(compressor, first.body, body);
8388                 return drop_it(body);
8389             }
8390             ab = first_statement(first.alternative);
8391             if (ab instanceof AST_Break && !external_target(ab)) {
8392                 if (self.condition) {
8393                     self.condition = make_node(AST_Binary, self.condition, {
8394                         left: self.condition,
8395                         operator: "&&",
8396                         right: first.condition,
8397                     });
8398                 } else {
8399                     self.condition = first.condition;
8400                 }
8401                 var body = as_statement_array(first.body);
8402                 extract_declarations_from_unreachable_code(compressor, first.alternative, body);
8403                 return drop_it(body);
8404             }
8405         }
8406         return self;
8407
8408         function first_statement(body) {
8409             return body instanceof AST_BlockStatement ? body.body[0] : body;
8410         }
8411
8412         function external_target(node) {
8413             return compressor.loopcontrol_target(node) !== compressor.self();
8414         }
8415
8416         function drop_it(rest) {
8417             if (self.body instanceof AST_BlockStatement) {
8418                 self.body = self.body.clone();
8419                 self.body.body = rest.concat(self.body.body.slice(1));
8420                 self.body = self.body.transform(compressor);
8421             } else {
8422                 self.body = make_node(AST_BlockStatement, self.body, {
8423                     body: rest
8424                 }).transform(compressor);
8425             }
8426             return if_break_in_loop(self, compressor);
8427         }
8428     }
8429
8430     OPT(AST_For, function(self, compressor) {
8431         if (!compressor.option("loops")) return self;
8432         if (compressor.option("side_effects")) {
8433             if (self.init) self.init = self.init.drop_side_effect_free(compressor);
8434             if (self.step) self.step = self.step.drop_side_effect_free(compressor);
8435         }
8436         if (self.condition) {
8437             var cond = fuzzy_eval(compressor, self.condition);
8438             if (!cond) {
8439                 if (compressor.option("dead_code")) {
8440                     var body = [];
8441                     if (is_statement(self.init)) {
8442                         body.push(self.init);
8443                     } else if (self.init) {
8444                         body.push(make_node(AST_SimpleStatement, self.init, { body: self.init }));
8445                     }
8446                     body.push(make_node(AST_SimpleStatement, self.condition, { body: self.condition }));
8447                     extract_declarations_from_unreachable_code(compressor, self.body, body);
8448                     return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
8449                 }
8450             } else if (!(cond instanceof AST_Node)) {
8451                 self.body = make_node(AST_BlockStatement, self.body, {
8452                     body: [
8453                         make_node(AST_SimpleStatement, self.condition, { body: self.condition }),
8454                         self.body,
8455                     ],
8456                 });
8457                 self.condition = null;
8458             }
8459         }
8460         return if_break_in_loop(self, compressor);
8461     });
8462
8463     OPT(AST_ForEnumeration, function(self, compressor) {
8464         if (compressor.option("varify") && is_lexical_definition(self.init)) {
8465             var name = self.init.definitions[0].name;
8466             if ((name instanceof AST_Destructured || name instanceof AST_SymbolLet)
8467                 && !name.match_symbol(function(node) {
8468                     if (node instanceof AST_SymbolDeclaration) {
8469                         var def = node.definition();
8470                         return !same_scope(def) || may_overlap(compressor, def);
8471                     }
8472                 }, true)) {
8473                 self.init = to_var(self.init);
8474             }
8475         }
8476         return self;
8477     });
8478
8479     function mark_locally_defined(condition, consequent, alternative) {
8480         if (!(condition instanceof AST_Binary)) return;
8481         if (!(condition.left instanceof AST_String)) {
8482             switch (condition.operator) {
8483               case "&&":
8484                 mark_locally_defined(condition.left, consequent);
8485                 mark_locally_defined(condition.right, consequent);
8486                 break;
8487               case "||":
8488                 mark_locally_defined(negate(condition.left), alternative);
8489                 mark_locally_defined(negate(condition.right), alternative);
8490                 break;
8491             }
8492             return;
8493         }
8494         if (!(condition.right instanceof AST_UnaryPrefix)) return;
8495         if (condition.right.operator != "typeof") return;
8496         var sym = condition.right.expression;
8497         if (!is_undeclared_ref(sym)) return;
8498         var body;
8499         var undef = condition.left.value == "undefined";
8500         switch (condition.operator) {
8501           case "==":
8502             body = undef ? alternative : consequent;
8503             break;
8504           case "!=":
8505             body = undef ? consequent : alternative;
8506             break;
8507           default:
8508             return;
8509         }
8510         if (!body) return;
8511         var def = sym.definition();
8512         var tw = new TreeWalker(function(node) {
8513             if (node instanceof AST_Scope) {
8514                 var parent = tw.parent();
8515                 if (parent instanceof AST_Call && parent.expression === node) return;
8516                 return true;
8517             }
8518             if (node instanceof AST_SymbolRef && node.definition() === def) node.defined = true;
8519         });
8520         body.walk(tw);
8521
8522         function negate(node) {
8523             if (!(node instanceof AST_Binary)) return;
8524             switch (node.operator) {
8525               case "==":
8526                 node = node.clone();
8527                 node.operator = "!=";
8528                 return node;
8529               case "!=":
8530                 node = node.clone();
8531                 node.operator = "==";
8532                 return node;
8533             }
8534         }
8535     }
8536
8537     function fuzzy_eval(compressor, node, nullish) {
8538         if (node.truthy) return true;
8539         if (node.falsy && !nullish) return false;
8540         if (node.is_truthy()) return true;
8541         return node.evaluate(compressor, true);
8542     }
8543
8544     function mark_duplicate_condition(compressor, node) {
8545         var child;
8546         var level = 0;
8547         var negated = false;
8548         var parent = compressor.self();
8549         if (!is_statement(parent)) while (true) {
8550             child = parent;
8551             parent = compressor.parent(level++);
8552             if (parent instanceof AST_Binary) {
8553                 var op = parent.operator;
8554                 if (!lazy_op[op]) return;
8555                 var left = parent.left;
8556                 if (left === child) continue;
8557                 if (match(left)) switch (op) {
8558                   case "&&":
8559                     node[negated ? "falsy" : "truthy"] = true;
8560                     break;
8561                   case "||":
8562                   case "??":
8563                     node[negated ? "truthy" : "falsy"] = true;
8564                     break;
8565                 }
8566             } else if (parent instanceof AST_Conditional) {
8567                 var cond = parent.condition;
8568                 if (cond === child) continue;
8569                 if (match(cond)) switch (child) {
8570                   case parent.consequent:
8571                     node[negated ? "falsy" : "truthy"] = true;
8572                     break;
8573                   case parent.alternative:
8574                     node[negated ? "truthy" : "falsy"] = true;
8575                     break;
8576                 }
8577             } else if (parent instanceof AST_Exit) {
8578                 break;
8579             } else if (parent instanceof AST_If) {
8580                 break;
8581             } else if (parent instanceof AST_Sequence) {
8582                 if (parent.expressions[0] === child) continue;
8583             } else if (parent instanceof AST_SimpleStatement) {
8584                 break;
8585             }
8586             return;
8587         }
8588         while (true) {
8589             child = parent;
8590             parent = compressor.parent(level++);
8591             if (parent instanceof AST_BlockStatement) {
8592                 if (parent.body[0] === child) continue;
8593             } else if (parent instanceof AST_If) {
8594                 if (match(parent.condition)) switch (child) {
8595                   case parent.body:
8596                     node[negated ? "falsy" : "truthy"] = true;
8597                     break;
8598                   case parent.alternative:
8599                     node[negated ? "truthy" : "falsy"] = true;
8600                     break;
8601                 }
8602             }
8603             return;
8604         }
8605
8606         function match(cond) {
8607             if (node.equivalent_to(cond)) return true;
8608             if (!(cond instanceof AST_UnaryPrefix)) return false;
8609             if (cond.operator != "!") return false;
8610             if (!node.equivalent_to(cond.expression)) return false;
8611             negated = true;
8612             return true;
8613         }
8614     }
8615
8616     OPT(AST_If, function(self, compressor) {
8617         if (is_empty(self.alternative)) self.alternative = null;
8618
8619         if (!compressor.option("conditionals")) return self;
8620         if (compressor.option("booleans") && !self.condition.has_side_effects(compressor)) {
8621             mark_duplicate_condition(compressor, self.condition);
8622         }
8623         // if condition can be statically determined, warn and drop
8624         // one of the blocks.  note, statically determined implies
8625         // “has no side effects”; also it doesn't work for cases like
8626         // `x && true`, though it probably should.
8627         if (compressor.option("dead_code")) {
8628             var cond = fuzzy_eval(compressor, self.condition);
8629             if (!cond) {
8630                 AST_Node.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
8631                 var body = [ make_node(AST_SimpleStatement, self.condition, { body: self.condition }) ];
8632                 extract_declarations_from_unreachable_code(compressor, self.body, body);
8633                 if (self.alternative) body.push(self.alternative);
8634                 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
8635             } else if (!(cond instanceof AST_Node)) {
8636                 AST_Node.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
8637                 var body = [
8638                     make_node(AST_SimpleStatement, self.condition, { body: self.condition }),
8639                     self.body,
8640                 ];
8641                 if (self.alternative) extract_declarations_from_unreachable_code(compressor, self.alternative, body);
8642                 return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
8643             }
8644         }
8645         var negated = self.condition.negate(compressor);
8646         var self_condition_length = self.condition.print_to_string().length;
8647         var negated_length = negated.print_to_string().length;
8648         var negated_is_best = negated_length < self_condition_length;
8649         if (self.alternative && negated_is_best) {
8650             negated_is_best = false; // because we already do the switch here.
8651             // no need to swap values of self_condition_length and negated_length
8652             // here because they are only used in an equality comparison later on.
8653             self.condition = negated;
8654             var tmp = self.body;
8655             self.body = self.alternative || make_node(AST_EmptyStatement, self);
8656             self.alternative = tmp;
8657         }
8658         var body = [], var_defs = [], refs = [];
8659         var body_exprs = sequencesize(self.body, body, var_defs, refs);
8660         var alt_exprs = sequencesize(self.alternative, body, var_defs, refs);
8661         if (body_exprs && alt_exprs) {
8662             if (var_defs.length > 0) body.push(make_node(AST_Var, self, { definitions: var_defs }));
8663             if (body_exprs.length == 0) {
8664                 body.push(make_node(AST_SimpleStatement, self.condition, {
8665                     body: alt_exprs.length > 0 ? make_node(AST_Binary, self, {
8666                         operator : "||",
8667                         left     : self.condition,
8668                         right    : make_sequence(self.alternative, alt_exprs)
8669                     }).transform(compressor) : self.condition.clone()
8670                 }).optimize(compressor));
8671             } else if (alt_exprs.length == 0) {
8672                 if (self_condition_length === negated_length && !negated_is_best
8673                     && self.condition instanceof AST_Binary && self.condition.operator == "||") {
8674                     // although the code length of self.condition and negated are the same,
8675                     // negated does not require additional surrounding parentheses.
8676                     // see https://github.com/mishoo/UglifyJS/issues/979
8677                     negated_is_best = true;
8678                 }
8679                 body.push(make_node(AST_SimpleStatement, self, {
8680                     body: make_node(AST_Binary, self, {
8681                         operator : negated_is_best ? "||" : "&&",
8682                         left     : negated_is_best ? negated : self.condition,
8683                         right    : make_sequence(self.body, body_exprs)
8684                     }).transform(compressor)
8685                 }).optimize(compressor));
8686             } else {
8687                 body.push(make_node(AST_SimpleStatement, self, {
8688                     body: make_node(AST_Conditional, self, {
8689                         condition   : self.condition,
8690                         consequent  : make_sequence(self.body, body_exprs),
8691                         alternative : make_sequence(self.alternative, alt_exprs)
8692                     })
8693                 }).optimize(compressor));
8694             }
8695             refs.forEach(function(ref) {
8696                 ref.definition().references.push(ref);
8697             });
8698             return make_node(AST_BlockStatement, self, {
8699                 body: body
8700             }).optimize(compressor);
8701         }
8702         if (is_empty(self.body)) {
8703             self = make_node(AST_If, self, {
8704                 condition: negated,
8705                 body: self.alternative,
8706                 alternative: null
8707             });
8708         }
8709         if (self.body instanceof AST_Exit
8710             && self.alternative instanceof AST_Exit
8711             && self.body.TYPE == self.alternative.TYPE) {
8712             var exit = make_node(self.body.CTOR, self, {
8713                 value: make_node(AST_Conditional, self, {
8714                     condition   : self.condition,
8715                     consequent  : self.body.value || make_node(AST_Undefined, self.body).transform(compressor),
8716                     alternative : self.alternative.value || make_node(AST_Undefined, self.alternative).transform(compressor)
8717                 })
8718             });
8719             if (exit instanceof AST_Return) {
8720                 exit.in_bool = self.body.in_bool || self.alternative.in_bool;
8721             }
8722             return exit;
8723         }
8724         if (self.body instanceof AST_If
8725             && !self.body.alternative
8726             && !self.alternative) {
8727             self = make_node(AST_If, self, {
8728                 condition: make_node(AST_Binary, self.condition, {
8729                     operator: "&&",
8730                     left: self.condition,
8731                     right: self.body.condition
8732                 }),
8733                 body: self.body.body,
8734                 alternative: null
8735             });
8736         }
8737         if (aborts(self.body)) {
8738             if (self.alternative) {
8739                 var alt = self.alternative;
8740                 self.alternative = null;
8741                 return make_node(AST_BlockStatement, self, {
8742                     body: [ self, alt ]
8743                 }).optimize(compressor);
8744             }
8745         }
8746         if (aborts(self.alternative)) {
8747             var body = self.body;
8748             self.body = self.alternative;
8749             self.condition = negated_is_best ? negated : self.condition.negate(compressor);
8750             self.alternative = null;
8751             return make_node(AST_BlockStatement, self, {
8752                 body: [ self, body ]
8753             }).optimize(compressor);
8754         }
8755         if (compressor.option("typeofs")) mark_locally_defined(self.condition, self.body, self.alternative);
8756         return self;
8757
8758         function sequencesize(stat, defuns, var_defs, refs) {
8759             if (stat == null) return [];
8760             if (stat instanceof AST_BlockStatement) {
8761                 var exprs = [];
8762                 for (var i = 0; i < stat.body.length; i++) {
8763                     var line = stat.body[i];
8764                     if (line instanceof AST_LambdaDefinition) {
8765                         defuns.push(line);
8766                     } else if (line instanceof AST_EmptyStatement) {
8767                         continue;
8768                     } else if (line instanceof AST_SimpleStatement) {
8769                         if (!compressor.option("sequences") && exprs.length > 0) return;
8770                         exprs.push(line.body);
8771                     } else if (line instanceof AST_Var) {
8772                         if (!compressor.option("sequences") && exprs.length > 0) return;
8773                         line.remove_initializers(compressor, var_defs);
8774                         line.definitions.forEach(process_var_def);
8775                     } else {
8776                         return;
8777                     }
8778                 }
8779                 return exprs;
8780             }
8781             if (stat instanceof AST_LambdaDefinition) {
8782                 defuns.push(stat);
8783                 return [];
8784             }
8785             if (stat instanceof AST_EmptyStatement) return [];
8786             if (stat instanceof AST_SimpleStatement) return [ stat.body ];
8787             if (stat instanceof AST_Var) {
8788                 var exprs = [];
8789                 stat.remove_initializers(compressor, var_defs);
8790                 stat.definitions.forEach(process_var_def);
8791                 return exprs;
8792             }
8793
8794             function process_var_def(var_def) {
8795                 if (!var_def.value) return;
8796                 exprs.push(make_node(AST_Assign, var_def, {
8797                     operator: "=",
8798                     left: var_def.name.convert_symbol(AST_SymbolRef, function(ref) {
8799                         refs.push(ref);
8800                     }),
8801                     right: var_def.value
8802                 }));
8803             }
8804         }
8805     });
8806
8807     OPT(AST_Switch, function(self, compressor) {
8808         if (!compressor.option("switches")) return self;
8809         if (!compressor.option("dead_code")) return self;
8810         var body = [];
8811         var branch;
8812         var decl = [];
8813         var default_branch;
8814         var exact_match;
8815         var side_effects = [];
8816         for (var i = 0, len = self.body.length; i < len; i++) {
8817             branch = self.body[i];
8818             if (branch instanceof AST_Default) {
8819                 var prev = body[body.length - 1];
8820                 if (default_branch || is_break(branch.body[0], compressor) && (!prev || aborts(prev))) {
8821                     eliminate_branch(branch, prev);
8822                     continue;
8823                 } else {
8824                     default_branch = branch;
8825                 }
8826             } else {
8827                 var exp = branch.expression;
8828                 var equals = make_node(AST_Binary, self, {
8829                     operator: "===",
8830                     left: self.expression,
8831                     right: exp,
8832                 }).evaluate(compressor, true);
8833                 if (!equals) {
8834                     if (exp.has_side_effects(compressor)) side_effects.push(exp);
8835                     eliminate_branch(branch, body[body.length - 1]);
8836                     continue;
8837                 }
8838                 if (!(equals instanceof AST_Node)) {
8839                     if (default_branch) {
8840                         var default_index = body.indexOf(default_branch);
8841                         body.splice(default_index, 1);
8842                         eliminate_branch(default_branch, body[default_index - 1]);
8843                         default_branch = null;
8844                     }
8845                     if (exp.has_side_effects(compressor)) {
8846                         exact_match = branch;
8847                     } else {
8848                         default_branch = branch = make_node(AST_Default, branch, branch);
8849                     }
8850                     while (++i < len) eliminate_branch(self.body[i], branch);
8851                 }
8852             }
8853             if (i + 1 >= len || aborts(branch)) {
8854                 var prev = body[body.length - 1];
8855                 var statements = branch.body;
8856                 if (aborts(prev)) switch (prev.body.length - statements.length) {
8857                   case 1:
8858                     var stat = prev.body[prev.body.length - 1];
8859                     if (!is_break(stat, compressor)) break;
8860                     statements = statements.concat(stat);
8861                   case 0:
8862                     var prev_block = make_node(AST_BlockStatement, prev, prev);
8863                     var next_block = make_node(AST_BlockStatement, branch, { body: statements });
8864                     if (prev_block.equivalent_to(next_block)) prev.body = [];
8865                 }
8866             }
8867             if (side_effects.length) {
8868                 if (branch instanceof AST_Default) {
8869                     body.push(make_node(AST_Case, self, { expression: make_sequence(self, side_effects), body: [] }));
8870                 } else {
8871                     side_effects.push(branch.expression);
8872                     branch.expression = make_sequence(self, side_effects);
8873                 }
8874                 side_effects = [];
8875             }
8876             body.push(branch);
8877         }
8878         if (side_effects.length && !exact_match) {
8879             body.push(make_node(AST_Case, self, { expression: make_sequence(self, side_effects), body: [] }));
8880         }
8881         while (branch = body[body.length - 1]) {
8882             var stat = branch.body[branch.body.length - 1];
8883             if (is_break(stat, compressor)) branch.body.pop();
8884             if (branch === default_branch) {
8885                 if (!has_declarations_only(branch)) break;
8886             } else if (branch.expression.has_side_effects(compressor)) {
8887                 break;
8888             } else if (default_branch) {
8889                 if (!has_declarations_only(default_branch)) break;
8890                 if (body[body.length - 2] !== default_branch) break;
8891                 default_branch.body = default_branch.body.concat(branch.body);
8892                 branch.body = [];
8893             } else if (!has_declarations_only(branch)) break;
8894             eliminate_branch(branch);
8895             if (body.pop() === default_branch) default_branch = null;
8896         }
8897         if (!branch) {
8898             decl.push(make_node(AST_SimpleStatement, self.expression, { body: self.expression }));
8899             if (side_effects.length) decl.push(make_node(AST_SimpleStatement, self, {
8900                 body: make_sequence(self, side_effects),
8901             }));
8902             return make_node(AST_BlockStatement, self, { body: decl }).optimize(compressor);
8903         }
8904         if (branch === default_branch) while (branch = body[body.length - 2]) {
8905             if (branch instanceof AST_Default) break;
8906             if (!has_declarations_only(branch)) break;
8907             var exp = branch.expression;
8908             if (exp.has_side_effects(compressor)) {
8909                 var prev = body[body.length - 3];
8910                 if (prev && !aborts(prev)) break;
8911                 default_branch.body.unshift(make_node(AST_SimpleStatement, self, { body: exp }));
8912             }
8913             eliminate_branch(branch);
8914             body.splice(-2, 1);
8915         }
8916         body[0].body = decl.concat(body[0].body);
8917         self.body = body;
8918         if (compressor.option("conditionals")) switch (body.length) {
8919           case 1:
8920             if (!no_break(body[0])) break;
8921             var exp = body[0].expression;
8922             var statements = body[0].body.slice();
8923             if (body[0] !== default_branch && body[0] !== exact_match) return make_node(AST_If, self, {
8924                 condition: make_node(AST_Binary, self, {
8925                     operator: "===",
8926                     left: self.expression,
8927                     right: exp,
8928                 }),
8929                 body: make_node(AST_BlockStatement, self, {
8930                     body: statements,
8931                 }),
8932                 alternative: null,
8933             }).optimize(compressor);
8934             if (exp) statements.unshift(make_node(AST_SimpleStatement, exp, {
8935                 body: exp,
8936             }));
8937             statements.unshift(make_node(AST_SimpleStatement, self.expression, {
8938                 body:self.expression,
8939             }));
8940             return make_node(AST_BlockStatement, self, {
8941                 body: statements,
8942             }).optimize(compressor);
8943           case 2:
8944             if (!member(default_branch, body) || !no_break(body[1])) break;
8945             var statements = body[0].body.slice();
8946             var exclusive = statements.length && is_break(statements[statements.length - 1], compressor);
8947             if (exclusive) statements.pop();
8948             if (!all(statements, no_break)) break;
8949             var alternative = body[1].body.length && make_node(AST_BlockStatement, body[1], body[1]);
8950             var node = make_node(AST_If, self, {
8951                 condition: make_node(AST_Binary, self, body[0] === default_branch ? {
8952                     operator: "!==",
8953                     left: self.expression,
8954                     right: body[1].expression,
8955                 } : {
8956                     operator: "===",
8957                     left: self.expression,
8958                     right: body[0].expression,
8959                 }),
8960                 body: make_node(AST_BlockStatement, body[0], {
8961                     body: statements,
8962                 }),
8963                 alternative: exclusive && alternative || null,
8964             });
8965             if (!exclusive && alternative) node = make_node(AST_BlockStatement, self, {
8966                 body: [ node, alternative ],
8967             });
8968             return node.optimize(compressor);
8969         }
8970         return self;
8971
8972         function is_break(node, tw) {
8973             return node instanceof AST_Break && tw.loopcontrol_target(node) === self;
8974         }
8975
8976         function no_break(node) {
8977             var found = false;
8978             var tw = new TreeWalker(function(node) {
8979                 if (found
8980                     || node instanceof AST_Lambda
8981                     || node instanceof AST_SimpleStatement) return true;
8982                 if (is_break(node, tw)) found = true;
8983             });
8984             tw.push(self);
8985             node.walk(tw);
8986             return !found;
8987         }
8988
8989         function eliminate_branch(branch, prev) {
8990             if (prev && !aborts(prev)) {
8991                 prev.body = prev.body.concat(branch.body);
8992             } else {
8993                 extract_declarations_from_unreachable_code(compressor, branch, decl);
8994             }
8995         }
8996     });
8997
8998     OPT(AST_Try, function(self, compressor) {
8999         self.body = tighten_body(self.body, compressor);
9000         if (compressor.option("dead_code")) {
9001             if (has_declarations_only(self)
9002                 && !(self.bcatch && self.bcatch.argname && self.bcatch.argname.match_symbol(function(node) {
9003                     return node instanceof AST_SymbolCatch && !can_drop_symbol(node);
9004                 }, true))) {
9005                 var body = [];
9006                 if (self.bcatch) {
9007                     extract_declarations_from_unreachable_code(compressor, self.bcatch, body);
9008                     body.forEach(function(stat) {
9009                         if (!(stat instanceof AST_Var)) return;
9010                         stat.definitions.forEach(function(var_def) {
9011                             var def = var_def.name.definition().redefined();
9012                             if (!def) return;
9013                             var_def.name = var_def.name.clone();
9014                             var_def.name.thedef = def;
9015                         });
9016                     });
9017                 }
9018                 body.unshift(make_node(AST_BlockStatement, self, self).optimize(compressor));
9019                 if (self.bfinally) {
9020                     body.push(make_node(AST_BlockStatement, self.bfinally, self.bfinally).optimize(compressor));
9021                 }
9022                 return make_node(AST_BlockStatement, self, {
9023                     body: body
9024                 }).optimize(compressor);
9025             }
9026             if (self.bfinally && has_declarations_only(self.bfinally)) {
9027                 var body = make_node(AST_BlockStatement, self.bfinally, self.bfinally).optimize(compressor);
9028                 body = self.body.concat(body);
9029                 if (!self.bcatch) return make_node(AST_BlockStatement, self, {
9030                     body: body
9031                 }).optimize(compressor);
9032                 self.body = body;
9033                 self.bfinally = null;
9034             }
9035         }
9036         return self;
9037     });
9038
9039     function remove_initializers(make_value) {
9040         return function(compressor, defns) {
9041             var dropped = false;
9042             this.definitions.forEach(function(defn) {
9043                 if (defn.value) dropped = true;
9044                 defn.name.match_symbol(function(node) {
9045                     if (node instanceof AST_SymbolDeclaration) defns.push(make_node(AST_VarDef, node, {
9046                         name: node,
9047                         value: make_value(compressor, node)
9048                     }));
9049                 }, true);
9050             });
9051             return dropped;
9052         };
9053     }
9054
9055     AST_Const.DEFMETHOD("remove_initializers", remove_initializers(function(compressor, node) {
9056         return make_node(AST_Undefined, node).optimize(compressor);
9057     }));
9058     AST_Let.DEFMETHOD("remove_initializers", remove_initializers(return_null));
9059     AST_Var.DEFMETHOD("remove_initializers", remove_initializers(return_null));
9060
9061     AST_Definitions.DEFMETHOD("to_assignments", function() {
9062         var assignments = this.definitions.reduce(function(a, defn) {
9063             var def = defn.name.definition();
9064             var value = defn.value;
9065             if (value) {
9066                 if (value instanceof AST_Sequence) value = value.clone();
9067                 var name = make_node(AST_SymbolRef, defn.name, defn.name);
9068                 var assign = make_node(AST_Assign, defn, {
9069                     operator: "=",
9070                     left: name,
9071                     right: value,
9072                 });
9073                 a.push(assign);
9074                 var fixed = function() {
9075                     return assign.right;
9076                 };
9077                 fixed.assigns = [ assign ];
9078                 fixed.direct_access = def.direct_access;
9079                 fixed.escaped = def.escaped;
9080                 name.fixed = fixed;
9081                 def.references.forEach(function(ref) {
9082                     var assigns = ref.fixed && ref.fixed.assigns;
9083                     if (assigns && assigns[0] === defn) assigns[0] = assign;
9084                 });
9085                 def.references.push(name);
9086             }
9087             def.eliminated++;
9088             def.single_use = false;
9089             return a;
9090         }, []);
9091         if (assignments.length == 0) return null;
9092         return make_sequence(this, assignments);
9093     });
9094
9095     function is_safe_lexical(def) {
9096         return def.name != "arguments" && def.orig.length < (def.orig[0] instanceof AST_SymbolLambda ? 3 : 2);
9097     }
9098
9099     function may_overlap(compressor, def) {
9100         if (compressor.exposed(def)) return true;
9101         var scope = def.scope.resolve();
9102         for (var s = def.scope; s !== scope;) {
9103             s = s.parent_scope;
9104             if (s.var_names()[def.name]) return true;
9105         }
9106     }
9107
9108     function to_var(stat) {
9109         return make_node(AST_Var, stat, {
9110             definitions: stat.definitions.map(function(defn) {
9111                 return make_node(AST_VarDef, defn, {
9112                     name: defn.name.convert_symbol(AST_SymbolVar, function(name, node) {
9113                         var def = name.definition();
9114                         def.orig[def.orig.indexOf(node)] = name;
9115                         var scope = def.scope.resolve();
9116                         if (def.scope === scope) return;
9117                         def.scope = scope;
9118                         scope.variables.set(def.name, def);
9119                         scope.enclosed.push(def);
9120                         scope.var_names()[def.name] = true;
9121                     }),
9122                     value: defn.value,
9123                 });
9124             })
9125         });
9126     }
9127
9128     function can_varify(compressor, sym) {
9129         if (!sym.fixed_value()) return false;
9130         var def = sym.definition();
9131         return is_safe_lexical(def) && same_scope(def) && !may_overlap(compressor, def);
9132     }
9133
9134     function varify(self, compressor) {
9135         return compressor.option("varify") && all(self.definitions, function(defn) {
9136             return !defn.name.match_symbol(function(node) {
9137                 if (node instanceof AST_SymbolDeclaration) return !can_varify(compressor, node);
9138             }, true);
9139         }) ? to_var(self) : self;
9140     }
9141
9142     OPT(AST_Const, varify);
9143     OPT(AST_Let, varify);
9144
9145     function trim_optional_chain(node, compressor) {
9146         if (!compressor.option("optional_chains")) return;
9147         if (node.terminal) do {
9148             var expr = node.expression;
9149             if (node.optional) {
9150                 var ev = fuzzy_eval(compressor, expr, true);
9151                 if (ev == null) return make_node(AST_UnaryPrefix, node, {
9152                     operator: "void",
9153                     expression: expr,
9154                 }).optimize(compressor);
9155                 if (!(ev instanceof AST_Node)) node.optional = false;
9156             }
9157             node = expr;
9158         } while ((node.TYPE == "Call" || node instanceof AST_PropAccess) && !node.terminal);
9159     }
9160
9161     function lift_sequence_in_expression(node, compressor) {
9162         var exp = node.expression;
9163         if (!(exp instanceof AST_Sequence)) return node;
9164         var x = exp.expressions.slice();
9165         var e = node.clone();
9166         e.expression = x.pop();
9167         x.push(e);
9168         return make_sequence(node, x);
9169     }
9170
9171     function drop_unused_call_args(call, compressor, fns_with_marked_args) {
9172         var exp = call.expression;
9173         var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
9174         if (!(fn instanceof AST_Lambda)) return;
9175         if (fn.uses_arguments) return;
9176         if (fn.pinned()) return;
9177         if (fns_with_marked_args && fns_with_marked_args.indexOf(fn) < 0) return;
9178         var args = call.args;
9179         if (!all(args, function(arg) {
9180             return !(arg instanceof AST_Spread);
9181         })) return;
9182         var argnames = fn.argnames;
9183         var is_iife = fn === exp && !fn.name;
9184         if (fn.rest) {
9185             if (!(is_iife && compressor.option("rests"))) return;
9186             var insert = argnames.length;
9187             args = args.slice(0, insert);
9188             while (args.length < insert) args.push(make_node(AST_Undefined, call).optimize(compressor));
9189             args.push(make_node(AST_Array, call, { elements: call.args.slice(insert) }));
9190             argnames = argnames.concat(fn.rest);
9191             fn.rest = null;
9192         } else {
9193             args = args.slice();
9194             argnames = argnames.slice();
9195         }
9196         var pos = 0, last = 0;
9197         var drop_defaults = is_iife && compressor.option("default_values");
9198         var drop_fargs = is_iife && compressor.drop_fargs(fn, call) ? function(argname, arg) {
9199             if (!argname) return true;
9200             if (argname instanceof AST_DestructuredArray) {
9201                 return argname.elements.length == 0 && !argname.rest && arg instanceof AST_Array;
9202             }
9203             if (argname instanceof AST_DestructuredObject) {
9204                 return argname.properties.length == 0 && !argname.rest && arg && !arg.may_throw_on_access(compressor);
9205             }
9206             return argname.__unused;
9207         } : return_false;
9208         var side_effects = [];
9209         for (var i = 0; i < args.length; i++) {
9210             var argname = argnames[i];
9211             if (drop_defaults && argname instanceof AST_DefaultValue && args[i].is_defined(compressor)) {
9212                 argnames[i] = argname = argname.name;
9213             }
9214             if (!argname || "__unused" in argname) {
9215                 var node = args[i].drop_side_effect_free(compressor);
9216                 if (drop_fargs(argname)) {
9217                     if (argname) argnames.splice(i, 1);
9218                     args.splice(i, 1);
9219                     if (node) side_effects.push(node);
9220                     i--;
9221                     continue;
9222                 } else if (node) {
9223                     side_effects.push(node);
9224                     args[pos++] = make_sequence(call, side_effects);
9225                     side_effects = [];
9226                 } else if (argname) {
9227                     if (side_effects.length) {
9228                         args[pos++] = make_sequence(call, side_effects);
9229                         side_effects = [];
9230                     } else {
9231                         args[pos++] = make_node(AST_Number, args[i], {
9232                             value: 0
9233                         });
9234                         continue;
9235                     }
9236                 }
9237             } else if (drop_fargs(argname, args[i])) {
9238                 var node = args[i].drop_side_effect_free(compressor);
9239                 argnames.splice(i, 1);
9240                 args.splice(i, 1);
9241                 if (node) side_effects.push(node);
9242                 i--;
9243                 continue;
9244             } else {
9245                 side_effects.push(args[i]);
9246                 args[pos++] = make_sequence(call, side_effects);
9247                 side_effects = [];
9248             }
9249             last = pos;
9250         }
9251         for (; i < argnames.length; i++) {
9252             if (drop_fargs(argnames[i])) argnames.splice(i--, 1);
9253         }
9254         fn.argnames = argnames;
9255         args.length = last;
9256         call.args = args;
9257         if (!side_effects.length) return;
9258         var arg = make_sequence(call, side_effects);
9259         args.push(args.length < argnames.length ? make_node(AST_UnaryPrefix, call, {
9260             operator: "void",
9261             expression: arg,
9262         }) : arg);
9263     }
9264
9265     function avoid_await_yield(parent_scope) {
9266         var avoid = [];
9267         if (is_async(parent_scope)) avoid.push("await");
9268         if (is_generator(parent_scope)) avoid.push("yield");
9269         return avoid.length && makePredicate(avoid);
9270     }
9271
9272     OPT(AST_Call, function(self, compressor) {
9273         var exp = self.expression;
9274         var terminated = trim_optional_chain(self, compressor);
9275         if (terminated) return terminated;
9276         if (compressor.option("sequences")) {
9277             if (exp instanceof AST_PropAccess) {
9278                 var seq = lift_sequence_in_expression(exp, compressor);
9279                 if (seq !== exp) {
9280                     var call = self.clone();
9281                     call.expression = seq.expressions.pop();
9282                     seq.expressions.push(call);
9283                     return seq.optimize(compressor);
9284                 }
9285             } else if (!needs_unbinding(compressor, exp.tail_node())) {
9286                 var seq = lift_sequence_in_expression(self, compressor);
9287                 if (seq !== self) return seq.optimize(compressor);
9288             }
9289         }
9290         if (compressor.option("unused")) drop_unused_call_args(self, compressor);
9291         if (compressor.option("unsafe")) {
9292             if (is_undeclared_ref(exp)) switch (exp.name) {
9293               case "Array":
9294                 // Array(n) ---> [ , , ... , ]
9295                 if (self.args.length == 1) {
9296                     var first = self.args[0];
9297                     if (first instanceof AST_Number) try {
9298                         var length = first.value;
9299                         if (length > 6) break;
9300                         var elements = Array(length);
9301                         for (var i = 0; i < length; i++) elements[i] = make_node(AST_Hole, self);
9302                         return make_node(AST_Array, self, { elements: elements });
9303                     } catch (ex) {
9304                         AST_Node.warn("Invalid array length: {length} [{file}:{line},{col}]", {
9305                             length: length,
9306                             file: self.start.file,
9307                             line: self.start.line,
9308                             col: self.start.col,
9309                         });
9310                         break;
9311                     }
9312                     if (!first.is_boolean(compressor) && !first.is_string(compressor)) break;
9313                 }
9314                 // Array(...) ---> [ ... ]
9315                 return make_node(AST_Array, self, { elements: self.args });
9316               case "Object":
9317                 // Object() ---> {}
9318                 if (self.args.length == 0) return make_node(AST_Object, self, { properties: [] });
9319                 break;
9320               case "String":
9321                 // String() ---> ""
9322                 if (self.args.length == 0) return make_node(AST_String, self, { value: "" });
9323                 // String(x) ---> "" + x
9324                 if (self.args.length == 1) return make_node(AST_Binary, self, {
9325                     operator: "+",
9326                     left: make_node(AST_String, self, { value: "" }),
9327                     right: self.args[0],
9328                 }).optimize(compressor);
9329                 break;
9330               case "Number":
9331                 // Number() ---> 0
9332                 if (self.args.length == 0) return make_node(AST_Number, self, { value: 0 });
9333                 // Number(x) ---> +("" + x)
9334                 if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
9335                     operator: "+",
9336                     expression: make_node(AST_Binary, self, {
9337                         operator: "+",
9338                         left: make_node(AST_String, self, { value: "" }),
9339                         right: self.args[0],
9340                     }),
9341                 }).optimize(compressor);
9342                 break;
9343               case "Boolean":
9344                 // Boolean() ---> false
9345                 if (self.args.length == 0) return make_node(AST_False, self).optimize(compressor);
9346                 // Boolean(x) ---> !!x
9347                 if (self.args.length == 1) return make_node(AST_UnaryPrefix, self, {
9348                     operator: "!",
9349                     expression: make_node(AST_UnaryPrefix, self, {
9350                         operator: "!",
9351                         expression: self.args[0],
9352                     }),
9353                 }).optimize(compressor);
9354                 break;
9355               case "RegExp":
9356                 // attempt to convert RegExp(...) to literal
9357                 var params = [];
9358                 if (all(self.args, function(arg) {
9359                     var value = arg.evaluate(compressor);
9360                     params.unshift(value);
9361                     return arg !== value;
9362                 })) try {
9363                     return best_of(compressor, self, make_node(AST_RegExp, self, {
9364                         value: RegExp.apply(RegExp, params),
9365                     }));
9366                 } catch (ex) {
9367                     AST_Node.warn("Error converting {expr} [{file}:{line},{col}]", {
9368                         expr: self,
9369                         file: self.start.file,
9370                         line: self.start.line,
9371                         col: self.start.col,
9372                     });
9373                 }
9374                 break;
9375             } else if (exp instanceof AST_Dot) switch (exp.property) {
9376               case "toString":
9377                 // x.toString() ---> "" + x
9378                 var expr = exp.expression;
9379                 if (self.args.length == 0 && !(expr.may_throw_on_access(compressor) || expr instanceof AST_Super)) {
9380                     return make_node(AST_Binary, self, {
9381                         operator: "+",
9382                         left: make_node(AST_String, self, { value: "" }),
9383                         right: expr,
9384                     }).optimize(compressor);
9385                 }
9386                 break;
9387               case "join":
9388                 if (exp.expression instanceof AST_Array && self.args.length < 2) EXIT: {
9389                     var separator = self.args[0];
9390                     // [].join() ---> ""
9391                     // [].join(x) ---> (x, "")
9392                     if (exp.expression.elements.length == 0 && !(separator instanceof AST_Spread)) {
9393                         return separator ? make_sequence(self, [
9394                             separator,
9395                             make_node(AST_String, self, { value: "" }),
9396                         ]).optimize(compressor) : make_node(AST_String, self, { value: "" });
9397                     }
9398                     if (separator) {
9399                         separator = separator.evaluate(compressor);
9400                         if (separator instanceof AST_Node) break EXIT; // not a constant
9401                     }
9402                     var elements = [];
9403                     var consts = [];
9404                     for (var i = 0; i < exp.expression.elements.length; i++) {
9405                         var el = exp.expression.elements[i];
9406                         var value = el.evaluate(compressor);
9407                         if (value !== el) {
9408                             consts.push(value);
9409                         } else if (el instanceof AST_Spread) {
9410                             break EXIT;
9411                         } else {
9412                             if (consts.length > 0) {
9413                                 elements.push(make_node(AST_String, self, {
9414                                     value: consts.join(separator),
9415                                 }));
9416                                 consts.length = 0;
9417                             }
9418                             elements.push(el);
9419                         }
9420                     }
9421                     if (consts.length > 0) elements.push(make_node(AST_String, self, {
9422                         value: consts.join(separator),
9423                     }));
9424                     // [ x ].join() ---> "" + x
9425                     // [ x ].join(".") ---> "" + x
9426                     // [ 1, 2, 3 ].join() ---> "1,2,3"
9427                     // [ 1, 2, 3 ].join(".") ---> "1.2.3"
9428                     if (elements.length == 1) {
9429                         if (elements[0].is_string(compressor)) return elements[0];
9430                         return make_node(AST_Binary, elements[0], {
9431                             operator: "+",
9432                             left: make_node(AST_String, self, { value: "" }),
9433                             right: elements[0],
9434                         });
9435                     }
9436                     // [ 1, 2, a, 3 ].join("") ---> "12" + a + "3"
9437                     if (separator == "") {
9438                         var first;
9439                         if (elements[0].is_string(compressor) || elements[1].is_string(compressor)) {
9440                             first = elements.shift();
9441                         } else {
9442                             first = make_node(AST_String, self, { value: "" });
9443                         }
9444                         return elements.reduce(function(prev, el) {
9445                             return make_node(AST_Binary, el, {
9446                                 operator: "+",
9447                                 left: prev,
9448                                 right: el,
9449                             });
9450                         }, first).optimize(compressor);
9451                     }
9452                     // [ x, "foo", "bar", y ].join() ---> [ x, "foo,bar", y ].join()
9453                     // [ x, "foo", "bar", y ].join("-") ---> [ x, "foo-bar", y ].join("-")
9454                     // need this awkward cloning to not affect original element
9455                     // best_of will decide which one to get through.
9456                     var node = self.clone();
9457                     node.expression = node.expression.clone();
9458                     node.expression.expression = node.expression.expression.clone();
9459                     node.expression.expression.elements = elements;
9460                     return best_of(compressor, self, node);
9461                 }
9462                 break;
9463               case "charAt":
9464                 if (self.args.length < 2) {
9465                     var node = make_node(AST_Binary, self, {
9466                         operator: "||",
9467                         left: make_node(AST_Sub, self, {
9468                             expression: exp.expression,
9469                             property: self.args.length ? make_node(AST_Binary, self.args[0], {
9470                                 operator: "|",
9471                                 left: make_node(AST_Number, self, { value: 0 }),
9472                                 right: self.args[0],
9473                             }) : make_node(AST_Number, self, { value: 0 }),
9474                         }).optimize(compressor),
9475                         right: make_node(AST_String, self, { value: "" }),
9476                     });
9477                     node.is_string = return_true;
9478                     return node.optimize(compressor);
9479                 }
9480                 break;
9481               case "apply":
9482                 if (self.args.length == 2 && self.args[1] instanceof AST_Array) {
9483                     var args = self.args[1].elements.slice();
9484                     args.unshift(self.args[0]);
9485                     return make_node(AST_Call, self, {
9486                         expression: make_node(AST_Dot, exp, {
9487                             expression: exp.expression,
9488                             property: "call",
9489                         }),
9490                         args: args
9491                     }).optimize(compressor);
9492                 }
9493                 break;
9494               case "call":
9495                 var func = exp.expression;
9496                 if (func instanceof AST_SymbolRef) {
9497                     func = func.fixed_value();
9498                 }
9499                 if (func instanceof AST_Lambda && !func.contains_this()) {
9500                     return (self.args.length ? make_sequence(this, [
9501                         self.args[0],
9502                         make_node(AST_Call, self, {
9503                             expression: exp.expression,
9504                             args: self.args.slice(1)
9505                         })
9506                     ]) : make_node(AST_Call, self, {
9507                         expression: exp.expression,
9508                         args: []
9509                     })).optimize(compressor);
9510                 }
9511                 break;
9512             }
9513         }
9514         if (compressor.option("unsafe_Function")
9515             && is_undeclared_ref(exp)
9516             && exp.name == "Function") {
9517             // new Function() ---> function(){}
9518             if (self.args.length == 0) return make_node(AST_Function, self, {
9519                 argnames: [],
9520                 body: []
9521             }).init_vars(exp.scope);
9522             if (all(self.args, function(x) {
9523                 return x instanceof AST_String;
9524             })) {
9525                 // quite a corner-case, but we can handle it:
9526                 //   https://github.com/mishoo/UglifyJS/issues/203
9527                 // if the code argument is a constant, then we can minify it.
9528                 try {
9529                     var code = "n(function(" + self.args.slice(0, -1).map(function(arg) {
9530                         return arg.value;
9531                     }).join() + "){" + self.args[self.args.length - 1].value + "})";
9532                     var ast = parse(code);
9533                     var mangle = { ie: compressor.option("ie") };
9534                     ast.figure_out_scope(mangle);
9535                     var comp = new Compressor(compressor.options);
9536                     ast = ast.transform(comp);
9537                     ast.figure_out_scope(mangle);
9538                     ast.compute_char_frequency(mangle);
9539                     ast.mangle_names(mangle);
9540                     var fun;
9541                     ast.walk(new TreeWalker(function(node) {
9542                         if (fun) return true;
9543                         if (node instanceof AST_Lambda) {
9544                             fun = node;
9545                             return true;
9546                         }
9547                     }));
9548                     var code = OutputStream();
9549                     AST_BlockStatement.prototype._codegen.call(fun, code);
9550                     self.args = [
9551                         make_node(AST_String, self, {
9552                             value: fun.argnames.map(function(arg) {
9553                                 return arg.print_to_string();
9554                             }).join(),
9555                         }),
9556                         make_node(AST_String, self.args[self.args.length - 1], {
9557                             value: code.get().replace(/^\{|\}$/g, "")
9558                         })
9559                     ];
9560                     return self;
9561                 } catch (ex) {
9562                     if (ex instanceof JS_Parse_Error) {
9563                         AST_Node.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
9564                         AST_Node.warn(ex.toString());
9565                     } else {
9566                         throw ex;
9567                     }
9568                 }
9569             }
9570         }
9571         var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
9572         var parent = compressor.parent(), current = compressor.self();
9573         var is_func = fn instanceof AST_Lambda
9574             && (!is_async(fn) || compressor.option("awaits") && parent instanceof AST_Await)
9575             && (!is_generator(fn) || compressor.option("yields") && current instanceof AST_Yield && current.nested);
9576         var stat = is_func && fn.first_statement();
9577         var has_default = 0, has_destructured = false;
9578         var has_spread = !all(self.args, function(arg) {
9579             return !(arg instanceof AST_Spread);
9580         });
9581         var can_drop = is_func && all(fn.argnames, function(argname, index) {
9582             if (has_default == 1 && self.args[index] instanceof AST_Spread) has_default = 2;
9583             if (argname instanceof AST_DefaultValue) {
9584                 if (!has_default) has_default = 1;
9585                 var arg = has_default == 1 && self.args[index];
9586                 if (arg && !is_undefined(arg)) has_default = 2;
9587                 if (has_arg_refs(argname.value)) return false;
9588                 argname = argname.name;
9589             }
9590             if (argname instanceof AST_Destructured) {
9591                 has_destructured = true;
9592                 if (has_arg_refs(argname)) return false;
9593             }
9594             return true;
9595         }) && !(fn.rest instanceof AST_Destructured && has_arg_refs(fn.rest));
9596         var can_inline = can_drop && compressor.option("inline") && !self.is_expr_pure(compressor);
9597         if (can_inline && stat instanceof AST_Return) {
9598             var value = stat.value;
9599             if (exp === fn
9600                 && !fn.name
9601                 && (!value || value.is_constant_expression())
9602                 && safe_from_await_yield(fn, compressor.find_parent(AST_Scope))) {
9603                 return make_sequence(self, convert_args(value)).optimize(compressor);
9604             }
9605         }
9606         if (is_func) {
9607             var def, value, var_assigned = false;
9608             if (can_inline
9609                 && !fn.uses_arguments
9610                 && !fn.pinned()
9611                 && !(fn.name && fn instanceof AST_LambdaExpression)
9612                 && (exp === fn || !recursive_ref(compressor, def = exp.definition(), fn)
9613                     && fn.is_constant_expression(find_scope(compressor)))
9614                 && !has_spread
9615                 && (value = can_flatten_body(stat))
9616                 && !fn.contains_this()) {
9617                 var replacing = exp === fn || def.single_use && def.references.length - def.replaced == 1;
9618                 if (can_substitute_directly()) {
9619                     var args = self.args.slice();
9620                     var refs = [];
9621                     var retValue = value.clone(true).transform(new TreeTransformer(function(node) {
9622                         if (node instanceof AST_SymbolRef) {
9623                             var def = node.definition();
9624                             if (fn.variables.get(node.name) !== def) {
9625                                 refs.push(node);
9626                                 return node;
9627                             }
9628                             var index = resolve_index(def);
9629                             var arg = args[index];
9630                             if (!arg) return make_node(AST_Undefined, self);
9631                             args[index] = null;
9632                             var parent = this.parent();
9633                             return parent ? maintain_this_binding(compressor, parent, node, arg) : arg;
9634                         }
9635                     }));
9636                     var save_inlined = fn.inlined;
9637                     if (exp !== fn) fn.inlined = true;
9638                     var exprs = [];
9639                     args.forEach(function(arg) {
9640                         if (!arg) return;
9641                         arg = arg.clone(true);
9642                         arg.walk(new TreeWalker(function(node) {
9643                             if (node instanceof AST_SymbolRef) refs.push(node);
9644                         }));
9645                         exprs.push(arg);
9646                     }, []);
9647                     exprs.push(retValue);
9648                     var node = make_sequence(self, exprs).optimize(compressor);
9649                     fn.inlined = save_inlined;
9650                     node = maintain_this_binding(compressor, parent, current, node);
9651                     if (replacing || best_of_expression(node, self) === node) {
9652                         refs.forEach(function(ref) {
9653                             ref.scope = exp === fn ? fn.parent_scope : exp.scope;
9654                             ref.reference();
9655                             var def = ref.definition();
9656                             if (replacing) def.replaced++;
9657                             def.single_use = false;
9658                         });
9659                         return node;
9660                     } else if (!node.has_side_effects(compressor)) {
9661                         self.drop_side_effect_free = function(compressor, first_in_statement) {
9662                             var self = this;
9663                             var exprs = self.args.slice();
9664                             exprs.unshift(self.expression);
9665                             return make_sequence(self, exprs).drop_side_effect_free(compressor, first_in_statement);
9666                         };
9667                     }
9668                 }
9669                 var arg_used, insert, in_loop, scope;
9670                 if (replacing && can_inject_symbols()) {
9671                     fn._squeezed = true;
9672                     if (exp !== fn) fn.parent_scope = exp.scope;
9673                     var node = make_sequence(self, flatten_fn()).optimize(compressor);
9674                     return maintain_this_binding(compressor, parent, current, node);
9675                 }
9676             }
9677             if (compressor.option("side_effects")
9678                 && can_drop
9679                 && all(fn.body, is_empty)
9680                 && (fn === exp ? fn_name_unused(fn, compressor) : !has_default && !has_destructured && !fn.rest)
9681                 && !(is_arrow(fn) && fn.value)
9682                 && safe_from_await_yield(fn, compressor.find_parent(AST_Scope))) {
9683                 return make_sequence(self, convert_args()).optimize(compressor);
9684             }
9685         }
9686         if (compressor.option("drop_console")) {
9687             if (exp instanceof AST_PropAccess) {
9688                 var name = exp.expression;
9689                 while (name.expression) {
9690                     name = name.expression;
9691                 }
9692                 if (is_undeclared_ref(name) && name.name == "console") {
9693                     return make_node(AST_Undefined, self).optimize(compressor);
9694                 }
9695             }
9696         }
9697         if (compressor.option("negate_iife") && parent instanceof AST_SimpleStatement && is_iife_call(current)) {
9698             return self.negate(compressor, true);
9699         }
9700         return try_evaluate(compressor, self);
9701
9702         function has_arg_refs(node) {
9703             var found = false;
9704             node.walk(new TreeWalker(function(node) {
9705                 if (found) return true;
9706                 if (node instanceof AST_SymbolRef && fn.variables.get(node.name) === node.definition()) {
9707                     return found = true;
9708                 }
9709             }));
9710             return found;
9711         }
9712
9713         function make_void_lhs(orig) {
9714             return make_node(AST_Dot, orig, {
9715                 expression: make_node(AST_Array, orig, { elements: [] }),
9716                 property: "e",
9717             });
9718         }
9719
9720         function convert_args(value) {
9721             var args = self.args.slice();
9722             var destructured = has_default > 1 || has_destructured || fn.rest;
9723             if (destructured || has_spread) args = [ make_node(AST_Array, self, { elements: args }) ];
9724             if (destructured) {
9725                 var tt = new TreeTransformer(function(node, descend) {
9726                     if (node instanceof AST_DefaultValue) return make_node(AST_DefaultValue, node, {
9727                         name: node.name.transform(tt) || make_void_lhs(node),
9728                         value: node.value,
9729                     });
9730                     if (node instanceof AST_DestructuredArray) {
9731                         var elements = [];
9732                         node.elements.forEach(function(node, index) {
9733                             node = node.transform(tt);
9734                             if (node) elements[index] = node;
9735                         });
9736                         fill_holes(node, elements);
9737                         return make_node(AST_DestructuredArray, node, { elements: elements });
9738                     }
9739                     if (node instanceof AST_DestructuredObject) {
9740                         var properties = [], side_effects = [];
9741                         node.properties.forEach(function(prop) {
9742                             var key = prop.key;
9743                             var value = prop.value.transform(tt);
9744                             if (value) {
9745                                 if (side_effects.length) {
9746                                     if (!(key instanceof AST_Node)) key = make_node_from_constant(key, prop);
9747                                     side_effects.push(key);
9748                                     key = make_sequence(node, side_effects);
9749                                     side_effects = [];
9750                                 }
9751                                 properties.push(make_node(AST_DestructuredKeyVal, prop, {
9752                                     key: key,
9753                                     value: value,
9754                                 }));
9755                             } else if (key instanceof AST_Node) {
9756                                 side_effects.push(key);
9757                             }
9758                         });
9759                         if (side_effects.length) properties.push(make_node(AST_DestructuredKeyVal, node, {
9760                             key: make_sequence(node, side_effects),
9761                             value: make_void_lhs(node),
9762                         }));
9763                         return make_node(AST_DestructuredObject, node, { properties: properties });
9764                     }
9765                     if (node instanceof AST_SymbolFunarg) return null;
9766                 });
9767                 var lhs = [];
9768                 fn.argnames.forEach(function(argname, index) {
9769                     argname = argname.transform(tt);
9770                     if (argname) lhs[index] = argname;
9771                 });
9772                 var rest = fn.rest && fn.rest.transform(tt);
9773                 if (rest) lhs.length = fn.argnames.length;
9774                 fill_holes(fn, lhs);
9775                 args[0] = make_node(AST_Assign, self, {
9776                     operator: "=",
9777                     left: make_node(AST_DestructuredArray, fn, {
9778                         elements: lhs,
9779                         rest: rest,
9780                     }),
9781                     right: args[0],
9782                 });
9783             } else fn.argnames.forEach(function(argname) {
9784                 if (argname instanceof AST_DefaultValue) args.push(argname.value);
9785             });
9786             args.push(value || make_node(AST_Undefined, self));
9787             return args;
9788         }
9789
9790         function safe_from_await_yield(node, scope) {
9791             var avoid = avoid_await_yield(scope);
9792             if (!avoid) return true;
9793             var safe = true;
9794             var tw = new TreeWalker(function(node) {
9795                 if (!safe) return true;
9796                 if (node instanceof AST_Scope) {
9797                     if (node === fn) return;
9798                     if (is_arrow(node)) {
9799                         for (var i = 0; safe && i < node.argnames.length; i++) node.argnames[i].walk(tw);
9800                     } else if (node instanceof AST_LambdaDefinition && avoid[node.name.name]) {
9801                         safe = false;
9802                     }
9803                     return true;
9804                 }
9805                 if (node instanceof AST_Symbol && avoid[node.name] && node !== fn.name) safe = false;
9806             });
9807             node.walk(tw);
9808             return safe;
9809         }
9810
9811         function noop_value() {
9812             return self.call_only ? make_node(AST_Number, self, { value: 0 }) : make_node(AST_Undefined, self);
9813         }
9814
9815         function return_value(stat) {
9816             if (!stat) return noop_value();
9817             if (stat instanceof AST_Return) return stat.value || noop_value();
9818             if (stat instanceof AST_SimpleStatement) {
9819                 return self.call_only ? stat.body : make_node(AST_UnaryPrefix, stat, {
9820                     operator: "void",
9821                     expression: stat.body,
9822                 });
9823             }
9824         }
9825
9826         function can_flatten_body(stat) {
9827             var len = fn.body.length;
9828             if (len < 2) {
9829                 stat = return_value(stat);
9830                 if (stat) return stat;
9831             }
9832             if (compressor.option("inline") < 3) return false;
9833             stat = null;
9834             for (var i = 0; i < len; i++) {
9835                 var line = fn.body[i];
9836                 if (line instanceof AST_Var) {
9837                     var assigned = var_assigned || !declarations_only(line);
9838                     if (assigned) {
9839                         var_assigned = true;
9840                         if (stat) return false;
9841                     }
9842                 } else if (line instanceof AST_AsyncDefun
9843                     || line instanceof AST_Defun
9844                     || line instanceof AST_EmptyStatement) {
9845                     continue;
9846                 } else if (stat) {
9847                     return false;
9848                 } else {
9849                     stat = line;
9850                 }
9851             }
9852             return return_value(stat);
9853         }
9854
9855         function resolve_index(def) {
9856             for (var i = fn.argnames.length; --i >= 0;) {
9857                 if (fn.argnames[i].definition() === def) return i;
9858             }
9859         }
9860
9861         function can_substitute_directly() {
9862             if (has_default || has_destructured || var_assigned || fn.rest) return;
9863             if (compressor.option("inline") < 2 && fn.argnames.length) return;
9864             if (!fn.variables.all(function(def) {
9865                 return def.references.length - def.replaced < 2 && def.orig[0] instanceof AST_SymbolFunarg;
9866             })) return;
9867             var scope = compressor.find_parent(AST_Scope);
9868             var abort = false;
9869             var avoid = avoid_await_yield(scope);
9870             var begin;
9871             var in_order = [];
9872             var side_effects = false;
9873             value.walk(new TreeWalker(function(node, descend) {
9874                 if (abort) return true;
9875                 if (node instanceof AST_Binary && lazy_op[node.operator]
9876                     || node instanceof AST_Conditional) {
9877                     in_order = null;
9878                     return;
9879                 }
9880                 if (node instanceof AST_Scope) return abort = true;
9881                 if (avoid && node instanceof AST_Symbol && avoid[node.name]) return abort = true;
9882                 if (node instanceof AST_SymbolRef) {
9883                     var def = node.definition();
9884                     if (fn.variables.get(node.name) !== def) {
9885                         in_order = null;
9886                         return;
9887                     }
9888                     if (def.init instanceof AST_LambdaDefinition) return abort = true;
9889                     if (is_lhs(node, this.parent())) return abort = true;
9890                     var index = resolve_index(def);
9891                     if (!(begin < index)) begin = index;
9892                     if (!in_order) return;
9893                     if (side_effects) {
9894                         in_order = null;
9895                     } else {
9896                         in_order.push(fn.argnames[index]);
9897                     }
9898                     return;
9899                 }
9900                 if (node.has_side_effects(compressor)) {
9901                     descend();
9902                     side_effects = true;
9903                     return true;
9904                 }
9905             }));
9906             if (abort) return;
9907             var end = self.args.length;
9908             if (in_order && fn.argnames.length >= end) {
9909                 end = fn.argnames.length;
9910                 while (end-- > begin && fn.argnames[end] === in_order.pop());
9911                 end++;
9912             }
9913             return end <= begin || all(self.args.slice(begin, end), side_effects && !in_order ? function(funarg) {
9914                 return funarg.is_constant_expression(scope);
9915             } : function(funarg) {
9916                 return !funarg.has_side_effects(compressor);
9917             });
9918         }
9919
9920         function var_exists(defined, name) {
9921             return defined[name] || identifier_atom[name] || scope.var_names()[name];
9922         }
9923
9924         function can_inject_args(defined, used, safe_to_inject) {
9925             var abort = false;
9926             fn.each_argname(function(arg) {
9927                 if (abort) return;
9928                 if (arg.__unused) return;
9929                 if (!safe_to_inject || var_exists(defined, arg.name)) return abort = true;
9930                 used[arg.name] = true;
9931                 if (in_loop) in_loop.push(arg.definition());
9932             });
9933             return !abort;
9934         }
9935
9936         function can_inject_vars(defined, used, safe_to_inject) {
9937             for (var i = 0; i < fn.body.length; i++) {
9938                 var stat = fn.body[i];
9939                 if (stat instanceof AST_LambdaDefinition) {
9940                     if (!safe_to_inject || var_exists(used, stat.name.name)) return false;
9941                     if (!all(stat.enclosed, function(def) {
9942                         return def.scope === stat || !defined[def.name];
9943                     })) return false;
9944                     if (in_loop) in_loop.push(stat.name.definition());
9945                     continue;
9946                 }
9947                 if (!(stat instanceof AST_Var)) continue;
9948                 if (!safe_to_inject) return false;
9949                 for (var j = stat.definitions.length; --j >= 0;) {
9950                     var name = stat.definitions[j].name;
9951                     if (var_exists(defined, name.name)) return false;
9952                     if (in_loop) in_loop.push(name.definition());
9953                 }
9954             }
9955             return true;
9956         }
9957
9958         function can_inject_symbols() {
9959             var defined = Object.create(null);
9960             var level = 0, child;
9961             scope = current;
9962             do {
9963                 if (scope.variables) scope.variables.each(function(def) {
9964                     defined[def.name] = true;
9965                 });
9966                 child = scope;
9967                 scope = compressor.parent(level++);
9968                 if (scope instanceof AST_DWLoop) {
9969                     in_loop = [];
9970                 } else if (scope instanceof AST_For) {
9971                     if (scope.init === child) continue;
9972                     in_loop = [];
9973                 } else if (scope instanceof AST_ForEnumeration) {
9974                     if (scope.init === child) continue;
9975                     if (scope.object === child) continue;
9976                     in_loop = [];
9977                 } else if (scope instanceof AST_SymbolRef) {
9978                     if (scope.fixed_value() instanceof AST_Scope) return false;
9979                 }
9980             } while (!(scope instanceof AST_Scope));
9981             insert = scope.body.indexOf(child) + 1;
9982             if (!insert) return false;
9983             if (!safe_from_await_yield(fn, scope)) return false;
9984             var safe_to_inject = exp !== fn || fn.parent_scope.resolve() === scope;
9985             if (scope instanceof AST_Toplevel) {
9986                 if (compressor.toplevel.vars) {
9987                     defined["arguments"] = true;
9988                 } else {
9989                     safe_to_inject = false;
9990                 }
9991             }
9992             var inline = compressor.option("inline");
9993             arg_used = Object.create(defined);
9994             if (!can_inject_args(defined, arg_used, inline >= 2 && safe_to_inject)) return false;
9995             var used = Object.create(arg_used);
9996             if (!can_inject_vars(defined, used, inline >= 3 && safe_to_inject)) return false;
9997             return !in_loop || in_loop.length == 0 || !is_reachable(fn, in_loop);
9998         }
9999
10000         function append_var(decls, expressions, name, value) {
10001             var def = name.definition();
10002             if (!scope.var_names()[name.name]) {
10003                 scope.var_names()[name.name] = true;
10004                 decls.push(make_node(AST_VarDef, name, {
10005                     name: name,
10006                     value: null,
10007                 }));
10008             }
10009             scope.variables.set(name.name, def);
10010             scope.enclosed.push(def);
10011             if (!value) return;
10012             var sym = make_node(AST_SymbolRef, name, name);
10013             def.references.push(sym);
10014             expressions.push(make_node(AST_Assign, self, {
10015                 operator: "=",
10016                 left: sym,
10017                 right: value,
10018             }));
10019         }
10020
10021         function flatten_args(decls, expressions) {
10022             var len = fn.argnames.length;
10023             for (var i = self.args.length; --i >= len;) {
10024                 expressions.push(self.args[i]);
10025             }
10026             var default_args = [];
10027             for (i = len; --i >= 0;) {
10028                 var argname = fn.argnames[i];
10029                 var name;
10030                 if (argname instanceof AST_DefaultValue) {
10031                     default_args.push(argname);
10032                     name = argname.name;
10033                 } else {
10034                     name = argname;
10035                 }
10036                 var value = self.args[i];
10037                 if (name.__unused || scope.var_names()[name.name]) {
10038                     if (value) expressions.push(value);
10039                 } else {
10040                     var symbol = make_node(AST_SymbolVar, name, name);
10041                     name.definition().orig.push(symbol);
10042                     if ("__unused" in name) {
10043                         append_var(decls, expressions, symbol);
10044                         if (value) expressions.push(value);
10045                     } else {
10046                         if (!value && in_loop && argname === name) value = make_node(AST_Undefined, self);
10047                         append_var(decls, expressions, symbol, value);
10048                     }
10049                 }
10050             }
10051             decls.reverse();
10052             expressions.reverse();
10053             for (i = default_args.length; --i >= 0;) {
10054                 var node = default_args[i];
10055                 if ("__unused" in node.name) {
10056                     expressions.push(node.value);
10057                 } else {
10058                     var sym = make_node(AST_SymbolRef, node.name, node.name);
10059                     node.name.definition().references.push(sym);
10060                     expressions.push(make_node(AST_Assign, node, {
10061                         operator: "=",
10062                         left: sym,
10063                         right: node.value,
10064                     }));
10065                 }
10066             }
10067         }
10068
10069         function flatten_destructured(decls, expressions) {
10070             expressions.push(make_node(AST_Assign, self, {
10071                 operator: "=",
10072                 left: make_node(AST_DestructuredArray, self, {
10073                     elements: fn.argnames.map(function(argname) {
10074                         if (argname.__unused) return make_node(AST_Hole, argname);
10075                         return argname.convert_symbol(AST_SymbolRef, process);
10076                     }),
10077                     rest: fn.rest && fn.rest.convert_symbol(AST_SymbolRef, process),
10078                 }),
10079                 right: make_node(AST_Array, self, { elements: self.args.slice() }),
10080             }));
10081
10082             function process(ref, name) {
10083                 var def = name.definition();
10084                 def.references.push(ref);
10085                 var symbol = make_node(AST_SymbolVar, name, name);
10086                 def.orig.push(symbol);
10087                 append_var(decls, expressions, symbol);
10088             }
10089         }
10090
10091         function flatten_var(name) {
10092             var redef = name.definition().redefined();
10093             if (redef) {
10094                 name = name.clone();
10095                 name.thedef = redef;
10096             }
10097             return name;
10098         }
10099
10100         function flatten_vars(decls, expressions) {
10101             var args = [ insert, 0 ];
10102             var decl_var = [], expr_var = [], expr_loop = [];
10103             for (var i = 0; i < fn.body.length; i++) {
10104                 var stat = fn.body[i];
10105                 if (stat instanceof AST_LambdaDefinition) {
10106                     if (in_loop) {
10107                         var name = make_node(AST_SymbolVar, stat.name, flatten_var(stat.name));
10108                         name.definition().orig.push(name);
10109                         append_var(decls, expressions, name, to_func_expr(stat, true));
10110                     } else {
10111                         var def = stat.name.definition();
10112                         scope.functions.set(def.name, def);
10113                         scope.variables.set(def.name, def);
10114                         scope.enclosed.push(def);
10115                         scope.var_names()[def.name] = true;
10116                         args.push(stat);
10117                     }
10118                     continue;
10119                 }
10120                 if (!(stat instanceof AST_Var)) continue;
10121                 for (var j = 0; j < stat.definitions.length; j++) {
10122                     var var_def = stat.definitions[j];
10123                     var name = flatten_var(var_def.name);
10124                     append_var(decl_var, expr_var, name, var_def.value);
10125                     if (in_loop && !HOP(arg_used, name.name)) {
10126                         var def = fn.variables.get(name.name);
10127                         var sym = make_node(AST_SymbolRef, name, name);
10128                         def.references.push(sym);
10129                         expr_loop.push(make_node(AST_Assign, var_def, {
10130                             operator: "=",
10131                             left: sym,
10132                             right: make_node(AST_Undefined, name),
10133                         }));
10134                     }
10135                 }
10136             }
10137             [].push.apply(decls, decl_var);
10138             [].push.apply(expressions, expr_loop);
10139             [].push.apply(expressions, expr_var);
10140             return args;
10141         }
10142
10143         function flatten_fn() {
10144             var decls = [];
10145             var expressions = [];
10146             if (has_default > 1 || has_destructured || fn.rest) {
10147                 flatten_destructured(decls, expressions);
10148             } else {
10149                 flatten_args(decls, expressions);
10150             }
10151             var args = flatten_vars(decls, expressions);
10152             expressions.push(value);
10153             if (decls.length) args.push(make_node(AST_Var, fn, {
10154                 definitions: decls
10155             }));
10156             [].splice.apply(scope.body, args);
10157             fn.enclosed.forEach(function(def) {
10158                 if (scope.var_names()[def.name]) return;
10159                 scope.enclosed.push(def);
10160                 scope.var_names()[def.name] = true;
10161             });
10162             return expressions;
10163         }
10164     });
10165
10166     OPT(AST_New, function(self, compressor) {
10167         if (compressor.option("sequences")) {
10168             var seq = lift_sequence_in_expression(self, compressor);
10169             if (seq !== self) return seq.optimize(compressor);
10170         }
10171         if (compressor.option("unused")) drop_unused_call_args(self, compressor);
10172         if (compressor.option("unsafe")) {
10173             var exp = self.expression;
10174             if (is_undeclared_ref(exp)) {
10175                 switch (exp.name) {
10176                   case "Object":
10177                   case "RegExp":
10178                   case "Function":
10179                   case "Error":
10180                   case "Array":
10181                     return make_node(AST_Call, self, self).transform(compressor);
10182                 }
10183             }
10184         }
10185         return self;
10186     });
10187
10188     // (a = b, x && a = c) ---> a = x ? c : b
10189     // (a = b, x || a = c) ---> a = x ? b : c
10190     function to_conditional_assignment(compressor, def, value, node) {
10191         if (!(node instanceof AST_Binary)) return;
10192         if (!(node.operator == "&&" || node.operator == "||")) return;
10193         if (!(node.right instanceof AST_Assign)) return;
10194         if (node.right.operator != "=") return;
10195         if (!(node.right.left instanceof AST_SymbolRef)) return;
10196         if (node.right.left.definition() !== def) return;
10197         if (value.has_side_effects(compressor)) return;
10198         if (!safe_from_assignment(node.left)) return;
10199         if (!safe_from_assignment(node.right.right)) return;
10200         def.replaced++;
10201         return node.operator == "&&" ? make_node(AST_Conditional, node, {
10202             condition: node.left,
10203             consequent: node.right.right,
10204             alternative: value
10205         }) : make_node(AST_Conditional, node, {
10206             condition: node.left,
10207             consequent: value,
10208             alternative: node.right.right
10209         });
10210
10211         function safe_from_assignment(node) {
10212             if (node.has_side_effects(compressor)) return;
10213             var hit = false;
10214             node.walk(new TreeWalker(function(node) {
10215                 if (hit) return true;
10216                 if (node instanceof AST_SymbolRef && node.definition() === def) return hit = true;
10217             }));
10218             return !hit;
10219         }
10220     }
10221
10222     OPT(AST_Sequence, function(self, compressor) {
10223         var expressions = filter_for_side_effects();
10224         var end = expressions.length - 1;
10225         merge_assignments();
10226         trim_right_for_undefined();
10227         if (end == 0) {
10228             self = maintain_this_binding(compressor, compressor.parent(), compressor.self(), expressions[0]);
10229             if (!(self instanceof AST_Sequence)) self = self.optimize(compressor);
10230             return self;
10231         }
10232         self.expressions = expressions;
10233         return self;
10234
10235         function filter_for_side_effects() {
10236             if (!compressor.option("side_effects")) return self.expressions;
10237             var expressions = [];
10238             var first = first_in_statement(compressor);
10239             var last = self.expressions.length - 1;
10240             self.expressions.forEach(function(expr, index) {
10241                 if (index < last) expr = expr.drop_side_effect_free(compressor, first);
10242                 if (expr) {
10243                     merge_sequence(expressions, expr);
10244                     first = false;
10245                 }
10246             });
10247             return expressions;
10248         }
10249
10250         function trim_right_for_undefined() {
10251             if (!compressor.option("side_effects")) return;
10252             while (end > 0 && is_undefined(expressions[end], compressor)) end--;
10253             if (end < expressions.length - 1) {
10254                 expressions[end] = make_node(AST_UnaryPrefix, self, {
10255                     operator   : "void",
10256                     expression : expressions[end]
10257                 });
10258                 expressions.length = end + 1;
10259             }
10260         }
10261
10262         function is_simple_assign(node) {
10263             return node instanceof AST_Assign
10264                 && node.operator == "="
10265                 && node.left instanceof AST_SymbolRef
10266                 && node.left.definition();
10267         }
10268
10269         function merge_assignments() {
10270             for (var i = 1; i < end; i++) {
10271                 var prev = expressions[i - 1];
10272                 var def = is_simple_assign(prev);
10273                 if (!def) continue;
10274                 var expr = expressions[i];
10275                 if (compressor.option("conditionals")) {
10276                     var cond = to_conditional_assignment(compressor, def, prev.right, expr);
10277                     if (cond) {
10278                         prev.right = cond;
10279                         expressions.splice(i--, 1);
10280                         end--;
10281                         continue;
10282                     }
10283                 }
10284                 if (compressor.option("dead_code")
10285                     && is_simple_assign(expr) === def
10286                     && expr.right.is_constant_expression(def.scope.resolve())) {
10287                     expressions[--i] = prev.right;
10288                 }
10289             }
10290         }
10291     });
10292
10293     OPT(AST_UnaryPostfix, function(self, compressor) {
10294         if (compressor.option("sequences")) {
10295             var seq = lift_sequence_in_expression(self, compressor);
10296             if (seq !== self) return seq.optimize(compressor);
10297         }
10298         return try_evaluate(compressor, self);
10299     });
10300
10301     var SIGN_OPS = makePredicate("+ -");
10302     var MULTIPLICATIVE_OPS = makePredicate("* / %");
10303     OPT(AST_UnaryPrefix, function(self, compressor) {
10304         var op = self.operator;
10305         var exp = self.expression;
10306         if (compressor.option("evaluate") && op == "delete" && !may_not_delete(exp)) {
10307             return make_sequence(self, [ exp, make_node(AST_True, self) ]).optimize(compressor);
10308         }
10309         if (compressor.option("sequences") && can_lift()) {
10310             var seq = lift_sequence_in_expression(self, compressor);
10311             if (seq !== self) return seq.optimize(compressor);
10312         }
10313         if (compressor.option("side_effects") && op == "void") {
10314             exp = exp.drop_side_effect_free(compressor);
10315             if (!exp) return make_node(AST_Undefined, self).optimize(compressor);
10316             self.expression = exp;
10317             return self;
10318         }
10319         if (compressor.option("booleans")) {
10320             if (op == "!" && exp.is_truthy()) {
10321                 return make_sequence(self, [ exp, make_node(AST_False, self) ]).optimize(compressor);
10322             } else if (compressor.in_boolean_context()) switch (op) {
10323               case "!":
10324                 if (exp instanceof AST_UnaryPrefix && exp.operator == "!") {
10325                     // !!foo ---> foo, if we're in boolean context
10326                     return exp.expression;
10327                 }
10328                 if (exp instanceof AST_Binary) {
10329                     self = best_of(compressor, self, exp.negate(compressor, first_in_statement(compressor)));
10330                 }
10331                 break;
10332               case "typeof":
10333                 // typeof always returns a non-empty string, thus it's
10334                 // always true in booleans
10335                 AST_Node.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
10336                 var exprs = [ make_node(AST_True, self) ];
10337                 if (!(exp instanceof AST_SymbolRef && can_drop_symbol(exp, compressor))) exprs.unshift(exp);
10338                 return make_sequence(self, exprs).optimize(compressor);
10339             }
10340         }
10341         if (op == "-" && exp instanceof AST_Infinity) exp = exp.transform(compressor);
10342         if (compressor.option("evaluate")
10343             && exp instanceof AST_Binary
10344             && SIGN_OPS[op]
10345             && MULTIPLICATIVE_OPS[exp.operator]
10346             && (exp.left.is_constant() || !exp.right.has_side_effects(compressor))) {
10347             return make_node(AST_Binary, self, {
10348                 operator: exp.operator,
10349                 left: make_node(AST_UnaryPrefix, exp.left, {
10350                     operator: op,
10351                     expression: exp.left
10352                 }),
10353                 right: exp.right
10354             });
10355         }
10356         // avoids infinite recursion of numerals
10357         return op == "-" && (exp instanceof AST_Number || exp instanceof AST_Infinity)
10358             ? self : try_evaluate(compressor, self);
10359
10360         function may_not_delete(node) {
10361             return node instanceof AST_Infinity
10362                 || node instanceof AST_NaN
10363                 || node instanceof AST_NewTarget
10364                 || node instanceof AST_PropAccess
10365                 || node instanceof AST_SymbolRef
10366                 || node instanceof AST_Undefined;
10367         }
10368
10369         function can_lift() {
10370             switch (op) {
10371               case "delete":
10372                 return !may_not_delete(exp.tail_node());
10373               case "typeof":
10374                 return !is_undeclared_ref(exp.tail_node());
10375               default:
10376                 return true;
10377             }
10378         }
10379     });
10380
10381     OPT(AST_Await, function(self, compressor) {
10382         if (!compressor.option("awaits")) return self;
10383         if (compressor.option("sequences")) {
10384             var seq = lift_sequence_in_expression(self, compressor);
10385             if (seq !== self) return seq.optimize(compressor);
10386         }
10387         if (compressor.option("side_effects")) {
10388             var exp = self.expression;
10389             if (exp instanceof AST_Await) return exp.optimize(compressor);
10390             if (exp instanceof AST_UnaryPrefix) {
10391                 if (exp.expression instanceof AST_Await) return exp.optimize(compressor);
10392                 if (exp.operator == "void") return make_node(AST_UnaryPrefix, self, {
10393                     operator: "void",
10394                     expression: make_node(AST_Await, self, { expression: exp.expression }),
10395                 }).optimize(compressor);
10396             }
10397             for (var level = 0, node = self, parent; parent = compressor.parent(level++); node = parent) {
10398                 if (is_arrow(parent)) {
10399                     if (parent.value === node) return exp.optimize(compressor);
10400                 } else if (parent instanceof AST_Return) {
10401                     var drop = true;
10402                     do {
10403                         node = parent;
10404                         parent = compressor.parent(level++);
10405                         if (parent instanceof AST_Try && (parent.bfinally || parent.bcatch) !== node) {
10406                             drop = false;
10407                             break;
10408                         }
10409                     } while (parent && !(parent instanceof AST_Scope));
10410                     if (drop) return exp.optimize(compressor);
10411                 } else if (parent instanceof AST_Sequence) {
10412                     if (parent.tail_node() === node) continue;
10413                 }
10414                 break;
10415             }
10416         }
10417         return self;
10418     });
10419
10420     OPT(AST_Yield, function(self, compressor) {
10421         if (!compressor.option("yields")) return self;
10422         if (compressor.option("sequences")) {
10423             var seq = lift_sequence_in_expression(self, compressor);
10424             if (seq !== self) return seq.optimize(compressor);
10425         }
10426         var exp = self.expression;
10427         if (self.nested && exp.TYPE == "Call") {
10428             var inlined = exp.clone().optimize(compressor);
10429             if (inlined.TYPE != "Call") return inlined;
10430         }
10431         return self;
10432     });
10433
10434     AST_Binary.DEFMETHOD("lift_sequences", function(compressor) {
10435         if (this.left instanceof AST_PropAccess) {
10436             if (!(this.left.expression instanceof AST_Sequence)) return this;
10437             var x = this.left.expression.expressions.slice();
10438             var e = this.clone();
10439             e.left = e.left.clone();
10440             e.left.expression = x.pop();
10441             x.push(e);
10442             return make_sequence(this, x);
10443         }
10444         if (this.left instanceof AST_Sequence) {
10445             var x = this.left.expressions.slice();
10446             var e = this.clone();
10447             e.left = x.pop();
10448             x.push(e);
10449             return make_sequence(this, x);
10450         }
10451         if (this.right instanceof AST_Sequence) {
10452             if (this.left.has_side_effects(compressor)) return this;
10453             var assign = this.operator == "=" && this.left instanceof AST_SymbolRef;
10454             var x = this.right.expressions;
10455             var last = x.length - 1;
10456             for (var i = 0; i < last; i++) {
10457                 if (!assign && x[i].has_side_effects(compressor)) break;
10458             }
10459             if (i == last) {
10460                 x = x.slice();
10461                 var e = this.clone();
10462                 e.right = x.pop();
10463                 x.push(e);
10464                 return make_sequence(this, x);
10465             }
10466             if (i > 0) {
10467                 var e = this.clone();
10468                 e.right = make_sequence(this.right, x.slice(i));
10469                 x = x.slice(0, i);
10470                 x.push(e);
10471                 return make_sequence(this, x);
10472             }
10473         }
10474         return this;
10475     });
10476
10477     var indexFns = makePredicate("indexOf lastIndexOf");
10478     var commutativeOperators = makePredicate("== === != !== * & | ^");
10479     function is_object(node) {
10480         if (node instanceof AST_Assign) return node.operator == "=" && is_object(node.right);
10481         if (node instanceof AST_Sequence) return is_object(node.tail_node());
10482         if (node instanceof AST_SymbolRef) return is_object(node.fixed_value());
10483         return node instanceof AST_Array
10484             || node instanceof AST_Class
10485             || node instanceof AST_Lambda
10486             || node instanceof AST_New
10487             || node instanceof AST_Object;
10488     }
10489
10490     function is_primitive(compressor, node) {
10491         if (node.is_constant()) return true;
10492         if (node instanceof AST_Assign) return node.operator != "=" || is_primitive(compressor, node.right);
10493         if (node instanceof AST_Binary) {
10494             return !lazy_op[node.operator]
10495                 || is_primitive(compressor, node.left) && is_primitive(compressor, node.right);
10496         }
10497         if (node instanceof AST_Conditional) {
10498             return is_primitive(compressor, node.consequent) && is_primitive(compressor, node.alternative);
10499         }
10500         if (node instanceof AST_Sequence) return is_primitive(compressor, node.tail_node());
10501         if (node instanceof AST_SymbolRef) {
10502             var fixed = node.fixed_value();
10503             return fixed && is_primitive(compressor, fixed);
10504         }
10505         if (node instanceof AST_Template) return !node.tag || is_raw_tag(compressor, node.tag);
10506         if (node instanceof AST_Unary) return true;
10507     }
10508
10509     function repeatable(compressor, node) {
10510         if (node instanceof AST_Dot) return repeatable(compressor, node.expression);
10511         if (node instanceof AST_Sub) {
10512             return repeatable(compressor, node.expression) && repeatable(compressor, node.property);
10513         }
10514         if (node instanceof AST_Symbol) return true;
10515         return !node.has_side_effects(compressor);
10516     }
10517
10518     OPT(AST_Binary, function(self, compressor) {
10519         function reversible() {
10520             return self.left.is_constant()
10521                 || self.right.is_constant()
10522                 || !self.left.has_side_effects(compressor)
10523                     && !self.right.has_side_effects(compressor);
10524         }
10525         function reverse(op) {
10526             if (reversible()) {
10527                 if (op) self.operator = op;
10528                 var tmp = self.left;
10529                 self.left = self.right;
10530                 self.right = tmp;
10531             }
10532         }
10533         function swap_chain() {
10534             var rhs = self.right;
10535             self.left = make_node(AST_Binary, self, {
10536                 operator: self.operator,
10537                 left: self.left,
10538                 right: rhs.left,
10539                 start: self.left.start,
10540                 end: rhs.left.end
10541             });
10542             self.right = rhs.right;
10543             self.left = self.left.transform(compressor);
10544         }
10545         if (commutativeOperators[self.operator]
10546             && self.right.is_constant()
10547             && !self.left.is_constant()
10548             && !(self.left instanceof AST_Binary
10549                 && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
10550             // if right is a constant, whatever side effects the
10551             // left side might have could not influence the
10552             // result.  hence, force switch.
10553             reverse();
10554         }
10555         if (compressor.option("sequences")) {
10556             var seq = self.lift_sequences(compressor);
10557             if (seq !== self) return seq.optimize(compressor);
10558         }
10559         if (compressor.option("assignments") && lazy_op[self.operator]) {
10560             var assign = self.right;
10561             // a || (a = x) ---> a = a || x
10562             // a && (a = x) ---> a = a && x
10563             if (self.left instanceof AST_SymbolRef
10564                 && assign instanceof AST_Assign
10565                 && assign.operator == "="
10566                 && self.left.equivalent_to(assign.left)) {
10567                 self.right = assign.right;
10568                 assign.right = self;
10569                 return assign;
10570             }
10571         }
10572         if (compressor.option("comparisons")) switch (self.operator) {
10573           case "===":
10574           case "!==":
10575             if (is_undefined(self.left, compressor) && self.right.is_defined(compressor)) {
10576                 AST_Node.warn("Expression always defined [{file}:{line},{col}]", self.start);
10577                 return make_sequence(self, [
10578                     self.right,
10579                     make_node(self.operator == "===" ? AST_False : AST_True, self)
10580                 ]).optimize(compressor);
10581             }
10582             var is_strict_comparison = true;
10583             if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
10584                 (self.left.is_number(compressor) && self.right.is_number(compressor)) ||
10585                 (self.left.is_boolean(compressor) && self.right.is_boolean(compressor)) ||
10586                 repeatable(compressor, self.left) && self.left.equivalent_to(self.right)) {
10587                 self.operator = self.operator.slice(0, 2);
10588             }
10589             // XXX: intentionally falling down to the next case
10590           case "==":
10591           case "!=":
10592             // void 0 == x ---> null == x
10593             if (!is_strict_comparison && is_undefined(self.left, compressor)) {
10594                 self.left = make_node(AST_Null, self.left);
10595             }
10596             // "undefined" == typeof x ---> undefined === x
10597             else if (compressor.option("typeofs")
10598                 && self.left instanceof AST_String
10599                 && self.left.value == "undefined"
10600                 && self.right instanceof AST_UnaryPrefix
10601                 && self.right.operator == "typeof") {
10602                 var expr = self.right.expression;
10603                 if (expr instanceof AST_SymbolRef ? expr.is_declared(compressor)
10604                     : !(expr instanceof AST_PropAccess && compressor.option("ie"))) {
10605                     self.right = expr;
10606                     self.left = make_node(AST_Undefined, self.left).optimize(compressor);
10607                     if (self.operator.length == 2) self.operator += "=";
10608                 }
10609             }
10610             // obj !== obj ---> false
10611             else if (self.left instanceof AST_SymbolRef
10612                 && self.right instanceof AST_SymbolRef
10613                 && self.left.definition() === self.right.definition()
10614                 && is_object(self.left)) {
10615                 return make_node(self.operator[0] == "=" ? AST_True : AST_False, self).optimize(compressor);
10616             }
10617             break;
10618           case "&&":
10619           case "||":
10620             // void 0 !== x && null !== x ---> null != x
10621             // void 0 === x || null === x ---> null == x
10622             var lhs = self.left;
10623             if (lhs.operator == self.operator) {
10624                 lhs = lhs.right;
10625             }
10626             if (lhs instanceof AST_Binary
10627                 && lhs.operator == (self.operator == "&&" ? "!==" : "===")
10628                 && self.right instanceof AST_Binary
10629                 && lhs.operator == self.right.operator
10630                 && (is_undefined(lhs.left, compressor) && self.right.left instanceof AST_Null
10631                     || lhs.left instanceof AST_Null && is_undefined(self.right.left, compressor))
10632                 && !lhs.right.has_side_effects(compressor)
10633                 && lhs.right.equivalent_to(self.right.right)) {
10634                 var combined = make_node(AST_Binary, self, {
10635                     operator: lhs.operator.slice(0, -1),
10636                     left: make_node(AST_Null, self),
10637                     right: lhs.right
10638                 });
10639                 if (lhs !== self.left) {
10640                     combined = make_node(AST_Binary, self, {
10641                         operator: self.operator,
10642                         left: self.left.left,
10643                         right: combined
10644                     });
10645                 }
10646                 return combined;
10647             }
10648             break;
10649         }
10650         var in_bool = false;
10651         var parent = compressor.parent();
10652         if (compressor.option("booleans")) {
10653             var lhs = self.left;
10654             if (lazy_op[self.operator] && !lhs.has_side_effects(compressor)) {
10655                 if (lhs.equivalent_to(self.right)) {
10656                     return maintain_this_binding(compressor, parent, compressor.self(), lhs).optimize(compressor);
10657                 }
10658                 mark_duplicate_condition(compressor, lhs);
10659             }
10660             in_bool = compressor.in_boolean_context();
10661         }
10662         if (in_bool) switch (self.operator) {
10663           case "+":
10664             var ll = self.left.evaluate(compressor);
10665             var rr = self.right.evaluate(compressor);
10666             if (ll && typeof ll == "string") {
10667                 AST_Node.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
10668                 return make_sequence(self, [
10669                     self.right,
10670                     make_node(AST_True, self)
10671                 ]).optimize(compressor);
10672             }
10673             if (rr && typeof rr == "string") {
10674                 AST_Node.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
10675                 return make_sequence(self, [
10676                     self.left,
10677                     make_node(AST_True, self)
10678                 ]).optimize(compressor);
10679             }
10680             break;
10681           case "==":
10682             if (self.left instanceof AST_String && self.left.value == "" && self.right.is_string(compressor)) {
10683                 return make_node(AST_UnaryPrefix, self, {
10684                     operator: "!",
10685                     expression: self.right
10686                 }).optimize(compressor);
10687             }
10688             break;
10689           case "!=":
10690             if (self.left instanceof AST_String && self.left.value == "" && self.right.is_string(compressor)) {
10691                 return self.right.optimize(compressor);
10692             }
10693             break;
10694         }
10695         if (compressor.option("comparisons") && self.is_boolean(compressor)) {
10696             if (!(parent instanceof AST_Binary) || parent instanceof AST_Assign) {
10697                 var negated = best_of(compressor, self, make_node(AST_UnaryPrefix, self, {
10698                     operator: "!",
10699                     expression: self.negate(compressor, first_in_statement(compressor))
10700                 }));
10701                 if (negated !== self) return negated;
10702             }
10703             switch (self.operator) {
10704               case ">": reverse("<"); break;
10705               case ">=": reverse("<="); break;
10706             }
10707         }
10708         // x && (y && z) ---> x && y && z
10709         // x || (y || z) ---> x || y || z
10710         if (compressor.option("conditionals")
10711             && lazy_op[self.operator]
10712             && self.right instanceof AST_Binary
10713             && self.operator == self.right.operator) {
10714             swap_chain();
10715         }
10716         if (compressor.option("strings") && self.operator == "+") {
10717             // "foo" + 42 + "" ---> "foo" + 42
10718             if (self.right instanceof AST_String
10719                 && self.right.value == ""
10720                 && self.left.is_string(compressor)) {
10721                 return self.left.optimize(compressor);
10722             }
10723             // "" + ("foo" + 42) ---> "foo" + 42
10724             if (self.left instanceof AST_String
10725                 && self.left.value == ""
10726                 && self.right.is_string(compressor)) {
10727                 return self.right.optimize(compressor);
10728             }
10729             // "" + 42 + "foo" ---> 42 + "foo"
10730             if (self.left instanceof AST_Binary
10731                 && self.left.operator == "+"
10732                 && self.left.left instanceof AST_String
10733                 && self.left.left.value == ""
10734                 && self.right.is_string(compressor)
10735                 && (self.left.right.is_constant() || !self.right.has_side_effects(compressor))) {
10736                 self.left = self.left.right;
10737                 return self.optimize(compressor);
10738             }
10739             // "x" + (y + "z") ---> "x" + y + "z"
10740             // x + ("y" + z) ---> x + "y" + z
10741             if (self.right instanceof AST_Binary
10742                 && self.operator == self.right.operator
10743                 && (self.left.is_string(compressor) && self.right.is_string(compressor)
10744                     || self.right.left.is_string(compressor)
10745                         && (self.left.is_constant() || !self.right.right.has_side_effects(compressor)))) {
10746                 swap_chain();
10747             }
10748         }
10749         if (compressor.option("evaluate")) {
10750             var associative = true;
10751             switch (self.operator) {
10752               case "&&":
10753                 var ll = fuzzy_eval(compressor, self.left);
10754                 if (!ll) {
10755                     AST_Node.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
10756                     return maintain_this_binding(compressor, parent, compressor.self(), self.left).optimize(compressor);
10757                 } else if (!(ll instanceof AST_Node)) {
10758                     AST_Node.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
10759                     return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
10760                 }
10761                 var rr = self.right.evaluate(compressor);
10762                 if (!rr) {
10763                     if (in_bool) {
10764                         AST_Node.warn("Boolean && always false [{file}:{line},{col}]", self.start);
10765                         return make_sequence(self, [
10766                             self.left,
10767                             make_node(AST_False, self)
10768                         ]).optimize(compressor);
10769                     } else self.falsy = true;
10770                 } else if (!(rr instanceof AST_Node)) {
10771                     if (in_bool || parent.operator == "&&" && parent.left === compressor.self()) {
10772                         AST_Node.warn("Dropping side-effect-free && [{file}:{line},{col}]", self.start);
10773                         return self.left.optimize(compressor);
10774                     }
10775                 }
10776                 // (x || false) && y ---> x ? y : false
10777                 if (self.left.operator == "||") {
10778                     var lr = fuzzy_eval(compressor, self.left.right);
10779                     if (!lr) return make_node(AST_Conditional, self, {
10780                         condition: self.left.left,
10781                         consequent: self.right,
10782                         alternative: self.left.right
10783                     }).optimize(compressor);
10784                 }
10785                 break;
10786               case "??":
10787                 var nullish = true;
10788               case "||":
10789                 var ll = fuzzy_eval(compressor, self.left, nullish);
10790                 if (nullish ? ll == null : !ll) {
10791                     AST_Node.warn("Condition left of {operator} always {value} [{file}:{line},{col}]", {
10792                         operator: self.operator,
10793                         value: nullish ? "nulish" : "false",
10794                         file: self.start.file,
10795                         line: self.start.line,
10796                         col: self.start.col,
10797                     });
10798                     return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
10799                 } else if (!(ll instanceof AST_Node)) {
10800                     AST_Node.warn("Condition left of {operator} always {value} [{file}:{line},{col}]", {
10801                         operator: self.operator,
10802                         value: nullish ? "defined" : "true",
10803                         file: self.start.file,
10804                         line: self.start.line,
10805                         col: self.start.col,
10806                     });
10807                     return maintain_this_binding(compressor, parent, compressor.self(), self.left).optimize(compressor);
10808                 }
10809                 var rr = self.right.evaluate(compressor);
10810                 if (!rr) {
10811                     if (in_bool || parent.operator == "||" && parent.left === compressor.self()) {
10812                         AST_Node.warn("Dropping side-effect-free {operator} [{file}:{line},{col}]", {
10813                             operator: self.operator,
10814                             file: self.start.file,
10815                             line: self.start.line,
10816                             col: self.start.col,
10817                         });
10818                         return self.left.optimize(compressor);
10819                     }
10820                 } else if (!nullish && !(rr instanceof AST_Node)) {
10821                     if (in_bool) {
10822                         AST_Node.warn("Boolean || always true [{file}:{line},{col}]", self.start);
10823                         return make_sequence(self, [
10824                             self.left,
10825                             make_node(AST_True, self)
10826                         ]).optimize(compressor);
10827                     } else self.truthy = true;
10828                 }
10829                 // x && true || y ---> x ? true : y
10830                 if (!nullish && self.left.operator == "&&") {
10831                     var lr = fuzzy_eval(compressor, self.left.right);
10832                     if (lr && !(lr instanceof AST_Node)) return make_node(AST_Conditional, self, {
10833                         condition: self.left.left,
10834                         consequent: self.left.right,
10835                         alternative: self.right
10836                     }).optimize(compressor);
10837                 }
10838                 break;
10839               case "+":
10840                 // "foo" + ("bar" + x) ---> "foobar" + x
10841                 if (self.left instanceof AST_Constant
10842                     && self.right instanceof AST_Binary
10843                     && self.right.operator == "+"
10844                     && self.right.left instanceof AST_Constant
10845                     && self.right.is_string(compressor)) {
10846                     self = make_node(AST_Binary, self, {
10847                         operator: "+",
10848                         left: make_node(AST_String, self.left, {
10849                             value: "" + self.left.value + self.right.left.value,
10850                             start: self.left.start,
10851                             end: self.right.left.end
10852                         }),
10853                         right: self.right.right
10854                     });
10855                 }
10856                 // (x + "foo") + "bar" ---> x + "foobar"
10857                 if (self.right instanceof AST_Constant
10858                     && self.left instanceof AST_Binary
10859                     && self.left.operator == "+"
10860                     && self.left.right instanceof AST_Constant
10861                     && self.left.is_string(compressor)) {
10862                     self = make_node(AST_Binary, self, {
10863                         operator: "+",
10864                         left: self.left.left,
10865                         right: make_node(AST_String, self.right, {
10866                             value: "" + self.left.right.value + self.right.value,
10867                             start: self.left.right.start,
10868                             end: self.right.end
10869                         })
10870                     });
10871                 }
10872                 // a + -b ---> a - b
10873                 if (self.right instanceof AST_UnaryPrefix
10874                     && self.right.operator == "-"
10875                     && self.left.is_number(compressor)) {
10876                     self = make_node(AST_Binary, self, {
10877                         operator: "-",
10878                         left: self.left,
10879                         right: self.right.expression
10880                     });
10881                     break;
10882                 }
10883                 // -a + b ---> b - a
10884                 if (self.left instanceof AST_UnaryPrefix
10885                     && self.left.operator == "-"
10886                     && reversible()
10887                     && self.right.is_number(compressor)) {
10888                     self = make_node(AST_Binary, self, {
10889                         operator: "-",
10890                         left: self.right,
10891                         right: self.left.expression
10892                     });
10893                     break;
10894                 }
10895                 // (a + b) + 3 ---> 3 + (a + b)
10896                 if (compressor.option("unsafe_math")
10897                     && self.left instanceof AST_Binary
10898                     && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]
10899                     && self.right.is_constant()
10900                     && (self.right.is_boolean(compressor) || self.right.is_number(compressor))
10901                     && self.left.is_number(compressor)
10902                     && !self.left.right.is_constant()
10903                     && (self.left.left.is_boolean(compressor) || self.left.left.is_number(compressor))) {
10904                     self = make_node(AST_Binary, self, {
10905                         operator: self.left.operator,
10906                         left: make_node(AST_Binary, self, {
10907                             operator: self.operator,
10908                             left: self.right,
10909                             right: self.left.left
10910                         }),
10911                         right: self.left.right
10912                     });
10913                     break;
10914                 }
10915               case "-":
10916                 // a - -b ---> a + b
10917                 if (self.right instanceof AST_UnaryPrefix
10918                     && self.right.operator == "-"
10919                     && self.left.is_number(compressor)
10920                     && self.right.expression.is_number(compressor)) {
10921                     self = make_node(AST_Binary, self, {
10922                         operator: "+",
10923                         left: self.left,
10924                         right: self.right.expression
10925                     });
10926                     break;
10927                 }
10928               case "*":
10929               case "/":
10930                 associative = compressor.option("unsafe_math");
10931                 // +a - b ---> a - b
10932                 // a - +b ---> a - b
10933                 if (self.operator != "+") [ "left", "right" ].forEach(function(operand) {
10934                     var node = self[operand];
10935                     if (node instanceof AST_UnaryPrefix && node.operator == "+") {
10936                         var exp = node.expression;
10937                         if (exp.is_boolean(compressor) || exp.is_number(compressor) || exp.is_string(compressor)) {
10938                             self[operand] = exp;
10939                         }
10940                     }
10941                 });
10942               case "&":
10943               case "|":
10944               case "^":
10945                 // a + +b ---> +b + a
10946                 if (self.operator != "-"
10947                     && self.operator != "/"
10948                     && (self.left.is_boolean(compressor) || self.left.is_number(compressor))
10949                     && (self.right.is_boolean(compressor) || self.right.is_number(compressor))
10950                     && reversible()
10951                     && !(self.left instanceof AST_Binary
10952                         && self.left.operator != self.operator
10953                         && PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
10954                     var reversed = make_node(AST_Binary, self, {
10955                         operator: self.operator,
10956                         left: self.right,
10957                         right: self.left
10958                     });
10959                     if (self.right instanceof AST_Constant
10960                         && !(self.left instanceof AST_Constant)) {
10961                         self = best_of(compressor, reversed, self);
10962                     } else {
10963                         self = best_of(compressor, self, reversed);
10964                     }
10965                 }
10966                 if (!associative || !self.is_number(compressor)) break;
10967                 // a + (b + c) ---> (a + b) + c
10968                 if (self.right instanceof AST_Binary
10969                     && self.right.operator != "%"
10970                     && PRECEDENCE[self.right.operator] == PRECEDENCE[self.operator]
10971                     && self.right.is_number(compressor)
10972                     && (self.operator != "+"
10973                         || self.right.left.is_boolean(compressor)
10974                         || self.right.left.is_number(compressor))
10975                     && (self.operator != "-" || !self.left.is_negative_zero())
10976                     && (self.right.left.is_constant_expression()
10977                         || !self.right.right.has_side_effects(compressor))
10978                     && !is_modify_array(self.right.right)) {
10979                     self = make_node(AST_Binary, self, {
10980                         operator: align(self.operator, self.right.operator),
10981                         left: make_node(AST_Binary, self.left, {
10982                             operator: self.operator,
10983                             left: self.left,
10984                             right: self.right.left,
10985                             start: self.left.start,
10986                             end: self.right.left.end
10987                         }),
10988                         right: self.right.right
10989                     });
10990                     if (self.operator == "+"
10991                         && !self.right.is_boolean(compressor)
10992                         && !self.right.is_number(compressor)) {
10993                         self.right = make_node(AST_UnaryPrefix, self.right, {
10994                             operator: "+",
10995                             expression: self.right
10996                         });
10997                     }
10998                 }
10999                 // (2 * n) * 3 ---> 6 * n
11000                 // (n + 2) + 3 ---> n + 5
11001                 if (self.right instanceof AST_Constant
11002                     && self.left instanceof AST_Binary
11003                     && self.left.operator != "%"
11004                     && PRECEDENCE[self.left.operator] == PRECEDENCE[self.operator]
11005                     && self.left.is_number(compressor)) {
11006                     if (self.left.left instanceof AST_Constant) {
11007                         var lhs = make_binary(self.left, self.operator, self.left.left, self.right, self.left.left.start, self.right.end);
11008                         self = make_binary(self, self.left.operator, try_evaluate(compressor, lhs), self.left.right);
11009                     } else if (self.left.right instanceof AST_Constant) {
11010                         var op = align(self.left.operator, self.operator);
11011                         var rhs = try_evaluate(compressor, make_binary(self.left, op, self.left.right, self.right));
11012                         if (rhs.is_constant()
11013                             && !(self.left.operator == "-"
11014                                 && self.right.value != 0
11015                                 && +rhs.value == 0
11016                                 && self.left.left.is_negative_zero())) {
11017                             self = make_binary(self, self.left.operator, self.left.left, rhs);
11018                         }
11019                     }
11020                 }
11021                 break;
11022             }
11023             if (!(parent instanceof AST_UnaryPrefix && parent.operator == "delete")) {
11024                 if (self.left instanceof AST_Number && !self.right.is_constant()) switch (self.operator) {
11025                   // 0 + n ---> n
11026                   case "+":
11027                     if (self.left.value == 0) {
11028                         if (self.right.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, {
11029                             operator: "+",
11030                             expression: self.right
11031                         }).optimize(compressor);
11032                         if (self.right.is_number(compressor) && !self.right.is_negative_zero()) return self.right;
11033                     }
11034                     break;
11035                   // 1 * n ---> n
11036                   case "*":
11037                     if (self.left.value == 1) {
11038                         return self.right.is_number(compressor) ? self.right : make_node(AST_UnaryPrefix, self, {
11039                             operator: "+",
11040                             expression: self.right
11041                         }).optimize(compressor);
11042                     }
11043                     break;
11044                 }
11045                 if (self.right instanceof AST_Number && !self.left.is_constant()) switch (self.operator) {
11046                   // n + 0 ---> n
11047                   case "+":
11048                     if (self.right.value == 0) {
11049                         if (self.left.is_boolean(compressor)) return make_node(AST_UnaryPrefix, self, {
11050                             operator: "+",
11051                             expression: self.left
11052                         }).optimize(compressor);
11053                         if (self.left.is_number(compressor) && !self.left.is_negative_zero()) return self.left;
11054                     }
11055                     break;
11056                   // n - 0 ---> n
11057                   case "-":
11058                     if (self.right.value == 0) {
11059                         return self.left.is_number(compressor) ? self.left : make_node(AST_UnaryPrefix, self, {
11060                             operator: "+",
11061                             expression: self.left
11062                         }).optimize(compressor);
11063                     }
11064                     break;
11065                   // n / 1 ---> n
11066                   case "/":
11067                     if (self.right.value == 1) {
11068                         return self.left.is_number(compressor) ? self.left : make_node(AST_UnaryPrefix, self, {
11069                             operator: "+",
11070                             expression: self.left
11071                         }).optimize(compressor);
11072                     }
11073                     break;
11074                 }
11075             }
11076         }
11077         if (compressor.option("typeofs")) switch (self.operator) {
11078           case "&&":
11079             mark_locally_defined(self.left, self.right, null);
11080             break;
11081           case "||":
11082             mark_locally_defined(self.left, null, self.right);
11083             break;
11084         }
11085         if (compressor.option("unsafe")) {
11086             var indexRight = is_indexFn(self.right);
11087             if (in_bool
11088                 && indexRight
11089                 && (self.operator == "==" || self.operator == "!=")
11090                 && self.left instanceof AST_Number
11091                 && self.left.value == 0) {
11092                 return (self.operator == "==" ? make_node(AST_UnaryPrefix, self, {
11093                     operator: "!",
11094                     expression: self.right
11095                 }) : self.right).optimize(compressor);
11096             }
11097             var indexLeft = is_indexFn(self.left);
11098             if (compressor.option("comparisons") && is_indexOf_match_pattern()) {
11099                 var node = make_node(AST_UnaryPrefix, self, {
11100                     operator: "!",
11101                     expression: make_node(AST_UnaryPrefix, self, {
11102                         operator: "~",
11103                         expression: indexLeft ? self.left : self.right
11104                     })
11105                 });
11106                 switch (self.operator) {
11107                   case "<":
11108                     if (indexLeft) break;
11109                   case "<=":
11110                   case "!=":
11111                     node = make_node(AST_UnaryPrefix, self, {
11112                         operator: "!",
11113                         expression: node
11114                     });
11115                     break;
11116                 }
11117                 return node.optimize(compressor);
11118             }
11119         }
11120         return try_evaluate(compressor, self);
11121
11122         function is_modify_array(node) {
11123             var found = false;
11124             node.walk(new TreeWalker(function(node) {
11125                 if (found) return true;
11126                 if (node instanceof AST_Assign) {
11127                     if (node.left instanceof AST_PropAccess) return found = true;
11128                 } else if (node instanceof AST_Unary) {
11129                     if (unary_side_effects[node.operator] && node.expression instanceof AST_PropAccess) {
11130                         return found = true;
11131                     }
11132                 }
11133             }));
11134             return found;
11135         }
11136
11137         function align(ref, op) {
11138             switch (ref) {
11139               case "-":
11140                 return op == "+" ? "-" : "+";
11141               case "/":
11142                 return op == "*" ? "/" : "*";
11143               default:
11144                 return op;
11145             }
11146         }
11147
11148         function make_binary(orig, op, left, right, start, end) {
11149             if (op == "+") {
11150                 if (!left.is_boolean(compressor) && !left.is_number(compressor)) {
11151                     left = make_node(AST_UnaryPrefix, left, {
11152                         operator: "+",
11153                         expression: left
11154                     });
11155                 }
11156                 if (!right.is_boolean(compressor) && !right.is_number(compressor)) {
11157                     right = make_node(AST_UnaryPrefix, right, {
11158                         operator: "+",
11159                         expression: right
11160                     });
11161                 }
11162             }
11163             return make_node(AST_Binary, orig, {
11164                 operator: op,
11165                 left: left,
11166                 right: right,
11167                 start: start,
11168                 end: end
11169             });
11170         }
11171
11172         function is_indexFn(node) {
11173             return node.TYPE == "Call"
11174                 && node.expression instanceof AST_Dot
11175                 && indexFns[node.expression.property];
11176         }
11177
11178         function is_indexOf_match_pattern() {
11179             switch (self.operator) {
11180               case "<=":
11181                 // 0 <= array.indexOf(string) ---> !!~array.indexOf(string)
11182                 return indexRight && self.left instanceof AST_Number && self.left.value == 0;
11183               case "<":
11184                 // array.indexOf(string) < 0 ---> !~array.indexOf(string)
11185                 if (indexLeft && self.right instanceof AST_Number && self.right.value == 0) return true;
11186                 // -1 < array.indexOf(string) ---> !!~array.indexOf(string)
11187               case "==":
11188               case "!=":
11189                 // -1 == array.indexOf(string) ---> !~array.indexOf(string)
11190                 // -1 != array.indexOf(string) ---> !!~array.indexOf(string)
11191                 if (!indexRight) return false;
11192                 return self.left instanceof AST_Number && self.left.value == -1
11193                     || self.left instanceof AST_UnaryPrefix && self.left.operator == "-"
11194                         && self.left.expression instanceof AST_Number && self.left.expression.value == 1;
11195             }
11196         }
11197     });
11198
11199     OPT(AST_SymbolExport, function(self) {
11200         return self;
11201     });
11202
11203     function recursive_ref(compressor, def, fn) {
11204         var level = 0, node = compressor.self();
11205         do {
11206             if (node === fn) return node;
11207             if (is_lambda(node) && node.name && node.name.definition() === def) return node;
11208         } while (node = compressor.parent(level++));
11209     }
11210
11211     function same_scope(def) {
11212         var scope = def.scope.resolve();
11213         return all(def.references, function(ref) {
11214             return scope === ref.scope.resolve();
11215         });
11216     }
11217
11218     OPT(AST_SymbolRef, function(self, compressor) {
11219         if (!compressor.option("ie")
11220             && is_undeclared_ref(self)
11221             // testing against `self.scope.uses_with` is an optimization
11222             && !(self.scope.resolve().uses_with && compressor.find_parent(AST_With))) {
11223             switch (self.name) {
11224               case "undefined":
11225                 return make_node(AST_Undefined, self).optimize(compressor);
11226               case "NaN":
11227                 return make_node(AST_NaN, self).optimize(compressor);
11228               case "Infinity":
11229                 return make_node(AST_Infinity, self).optimize(compressor);
11230             }
11231         }
11232         var parent = compressor.parent();
11233         if (compressor.option("reduce_vars") && is_lhs(compressor.self(), parent) !== compressor.self()) {
11234             var def = self.definition();
11235             var fixed = self.fixed_value();
11236             var single_use = def.single_use && !(parent instanceof AST_Call && parent.is_expr_pure(compressor));
11237             if (single_use) {
11238                 if (is_lambda(fixed)) {
11239                     if ((def.scope !== self.scope.resolve() || def.in_loop)
11240                         && (!compressor.option("reduce_funcs") || def.escaped.depth == 1 || fixed.inlined)) {
11241                         single_use = false;
11242                     } else if (recursive_ref(compressor, def, fixed)) {
11243                         single_use = false;
11244                     } else if (fixed.name && fixed.name.definition() !== def) {
11245                         single_use = false;
11246                     } else if (fixed.parent_scope !== self.scope || is_funarg(def)) {
11247                         single_use = fixed.is_constant_expression(self.scope);
11248                         if (single_use == "f") {
11249                             var scope = self.scope;
11250                             do {
11251                                 if (scope instanceof AST_LambdaDefinition || scope instanceof AST_LambdaExpression) {
11252                                     scope.inlined = true;
11253                                 }
11254                             } while (scope = scope.parent_scope);
11255                         }
11256                     } else if (fixed.name && (fixed.name.name == "await" && is_async(fixed)
11257                         || fixed.name.name == "yield" && is_generator(fixed))) {
11258                         single_use = false;
11259                     } else if (fixed.has_side_effects(compressor)) {
11260                         single_use = false;
11261                     } else if (compressor.option("ie") && fixed instanceof AST_Class) {
11262                         single_use = false;
11263                     }
11264                     if (single_use) fixed.parent_scope = self.scope;
11265                 } else if (!fixed
11266                     || def.recursive_refs > 0
11267                     || !fixed.is_constant_expression()
11268                     || fixed.drop_side_effect_free(compressor)) {
11269                     single_use = false;
11270                 }
11271             }
11272             if (single_use) {
11273                 def.single_use = false;
11274                 fixed._squeezed = true;
11275                 fixed.single_use = true;
11276                 if (fixed instanceof AST_DefClass) fixed = to_class_expr(fixed);
11277                 if (fixed instanceof AST_LambdaDefinition) fixed = to_func_expr(fixed);
11278                 if (is_lambda(fixed)) {
11279                     var scope = self.scope.resolve();
11280                     fixed.enclosed.forEach(function(def) {
11281                         if (fixed.variables.has(def.name)) return;
11282                         if (scope.var_names()[def.name]) return;
11283                         scope.enclosed.push(def);
11284                         scope.var_names()[def.name] = true;
11285                     });
11286                 }
11287                 var value;
11288                 if (def.recursive_refs > 0) {
11289                     value = fixed.clone(true);
11290                     var defun_def = value.name.definition();
11291                     var lambda_def = value.variables.get(value.name.name);
11292                     var name = lambda_def && lambda_def.orig[0];
11293                     var def_fn_name, symbol_type;
11294                     if (value instanceof AST_Class) {
11295                         def_fn_name = "def_function";
11296                         symbol_type = AST_SymbolClass;
11297                     } else {
11298                         def_fn_name = "def_variable";
11299                         symbol_type = AST_SymbolLambda;
11300                     }
11301                     if (!(name instanceof symbol_type)) {
11302                         name = make_node(symbol_type, value.name, value.name);
11303                         name.scope = value;
11304                         value.name = name;
11305                         lambda_def = value[def_fn_name](name);
11306                         lambda_def.recursive_refs = def.recursive_refs;
11307                     }
11308                     value.walk(new TreeWalker(function(node) {
11309                         if (node instanceof AST_SymbolDeclaration) {
11310                             if (node !== name) {
11311                                 var def = node.definition();
11312                                 def.orig.push(node);
11313                                 def.eliminated++;
11314                             }
11315                             return;
11316                         }
11317                         if (!(node instanceof AST_SymbolRef)) return;
11318                         var def = node.definition();
11319                         if (def === defun_def) {
11320                             node.thedef = def = lambda_def;
11321                         } else {
11322                             def.single_use = false;
11323                             var fn = node.fixed_value();
11324                             if (is_lambda(fn)
11325                                 && fn.name
11326                                 && fn.name.definition() === def
11327                                 && def.scope === fn.name.scope
11328                                 && fixed.variables.get(fn.name.name) === def) {
11329                                 fn.name = fn.name.clone();
11330                                 node.thedef = def = value.variables.get(fn.name.name) || value[def_fn_name](fn.name);
11331                             }
11332                         }
11333                         def.references.push(node);
11334                     }));
11335                 } else {
11336                     if (fixed instanceof AST_Scope) {
11337                         compressor.push(fixed);
11338                         value = fixed.optimize(compressor);
11339                         compressor.pop();
11340                     } else {
11341                         value = fixed.optimize(compressor);
11342                     }
11343                     value = value.transform(new TreeTransformer(function(node, descend) {
11344                         if (node instanceof AST_Scope) return node;
11345                         node = node.clone();
11346                         descend(node, this);
11347                         return node;
11348                     }));
11349                 }
11350                 def.replaced++;
11351                 return value;
11352             }
11353             var local = self.fixed !== def.fixed;
11354             if (fixed && (local || def.should_replace !== false)) {
11355                 var ev, init;
11356                 if (fixed instanceof AST_This) {
11357                     if (!is_funarg(def) && same_scope(def)) init = fixed;
11358                 } else if ((ev = fixed.evaluate(compressor, true)) !== fixed
11359                     && typeof ev != "function"
11360                     && (ev === null
11361                         || typeof ev != "object"
11362                         || compressor.option("unsafe_regexp")
11363                             && ev instanceof RegExp && !def.cross_loop && same_scope(def))) {
11364                     init = make_node_from_constant(ev, fixed);
11365                 }
11366                 if (init) {
11367                     if (!local && def.should_replace === undefined) {
11368                         var value_length = init.optimize(compressor).print_to_string().length;
11369                         if (!has_symbol_ref(fixed)) {
11370                             value_length = Math.min(value_length, fixed.print_to_string().length);
11371                         }
11372                         var name_length = def.name.length;
11373                         if (compressor.option("unused") && !compressor.exposed(def)) {
11374                             var referenced = def.references.length - def.replaced;
11375                             name_length += (name_length + 2 + value_length) / (referenced - def.assignments);
11376                         }
11377                         var delta = value_length - Math.floor(name_length);
11378                         def.should_replace = delta < compressor.eval_threshold;
11379                     }
11380                     if (local || def.should_replace) {
11381                         var value;
11382                         if (has_symbol_ref(fixed)) {
11383                             value = init.optimize(compressor);
11384                             if (value === init) value = value.clone(true);
11385                         } else {
11386                             value = best_of_expression(init.optimize(compressor), fixed);
11387                             if (value === init || value === fixed) value = value.clone(true);
11388                         }
11389                         def.replaced++;
11390                         return value;
11391                     }
11392                 }
11393             }
11394         }
11395         return self;
11396
11397         function has_symbol_ref(value) {
11398             var found;
11399             value.walk(new TreeWalker(function(node) {
11400                 if (node instanceof AST_SymbolRef) found = true;
11401                 if (found) return true;
11402             }));
11403             return found;
11404         }
11405     });
11406
11407     function is_raw_tag(compressor, tag) {
11408         return compressor.option("unsafe")
11409             && tag instanceof AST_Dot
11410             && tag.property == "raw"
11411             && is_undeclared_ref(tag.expression)
11412             && tag.expression.name == "String";
11413     }
11414
11415     function decode_template(str) {
11416         var malformed = false;
11417         str = str.replace(/\\(u\{[^{}]*\}?|u[\s\S]{0,4}|x[\s\S]{0,2}|[0-9]+|[\s\S])/g, function(match, seq) {
11418             var ch = decode_escape_sequence(seq);
11419             if (typeof ch == "string") return ch;
11420             malformed = true;
11421         });
11422         if (!malformed) return str;
11423     }
11424
11425     OPT(AST_Template, function(self, compressor) {
11426         if (!compressor.option("templates")) return self;
11427         var tag = self.tag;
11428         if (!tag || is_raw_tag(compressor, tag)) {
11429             var exprs = [];
11430             var strs = [];
11431             for (var i = 0, status; i < self.strings.length; i++) {
11432                 var str = self.strings[i];
11433                 if (!tag) {
11434                     var trimmed = decode_template(str);
11435                     if (trimmed) str = escape_literal(trimmed);
11436                 }
11437                 if (i > 0) {
11438                     var node = self.expressions[i - 1];
11439                     var value = should_join(node);
11440                     if (value) {
11441                         var prev = strs[strs.length - 1];
11442                         var joined = prev + value + str;
11443                         var decoded;
11444                         if (tag || typeof (decoded = decode_template(joined)) == status) {
11445                             strs[strs.length - 1] = decoded ? escape_literal(decoded) : joined;
11446                             continue;
11447                         }
11448                     }
11449                     exprs.push(node);
11450                 }
11451                 strs.push(str);
11452                 if (!tag) status = typeof trimmed;
11453             }
11454             if (!tag && strs.length > 1) {
11455                 if (strs[strs.length - 1] == "") return make_node(AST_Binary, self, {
11456                     operator: "+",
11457                     left: make_node(AST_Template, self, {
11458                         expressions: exprs.slice(0, -1),
11459                         strings: strs.slice(0, -1),
11460                         tag: tag,
11461                     }).transform(compressor),
11462                     right: exprs[exprs.length - 1],
11463                 }).optimize(compressor);
11464                 if (strs[0] == "") {
11465                     var left = make_node(AST_Binary, self, {
11466                         operator: "+",
11467                         left: make_node(AST_String, self, { value: "" }),
11468                         right: exprs[0],
11469                     });
11470                     for (var i = 1; strs[i] == "" && i < exprs.length; i++) {
11471                         left = make_node(AST_Binary, self, {
11472                             operator: "+",
11473                             left: left,
11474                             right: exprs[i],
11475                         });
11476                     }
11477                     return best_of(compressor, self, make_node(AST_Binary, self, {
11478                         operator: "+",
11479                         left: left.transform(compressor),
11480                         right: make_node(AST_Template, self, {
11481                             expressions: exprs.slice(i),
11482                             strings: strs.slice(i),
11483                             tag: tag,
11484                         }).transform(compressor),
11485                     }).optimize(compressor));
11486                 }
11487             }
11488             self.expressions = exprs;
11489             self.strings = strs;
11490         }
11491         return try_evaluate(compressor, self);
11492
11493         function escape_literal(str) {
11494             return str.replace(/\r|\\|`|\${/g, function(s) {
11495                 return "\\" + (s == "\r" ? "r" : s);
11496             });
11497         }
11498
11499         function should_join(node) {
11500             var ev = node.evaluate(compressor);
11501             if (ev === node) return;
11502             if (tag && /\r|\\|`/.test(ev)) return;
11503             ev = escape_literal("" + ev);
11504             if (ev.length > node.print_to_string().length + "${}".length) return;
11505             return ev;
11506         }
11507     });
11508
11509     function is_atomic(lhs, self) {
11510         return lhs instanceof AST_SymbolRef || lhs.TYPE === self.TYPE;
11511     }
11512
11513     OPT(AST_Undefined, function(self, compressor) {
11514         if (compressor.option("unsafe_undefined")) {
11515             var undef = find_scope(compressor).find_variable("undefined");
11516             if (undef) {
11517                 var ref = make_node(AST_SymbolRef, self, {
11518                     name   : "undefined",
11519                     scope  : undef.scope,
11520                     thedef : undef
11521                 });
11522                 ref.is_undefined = true;
11523                 return ref;
11524             }
11525         }
11526         var lhs = is_lhs(compressor.self(), compressor.parent());
11527         if (lhs && is_atomic(lhs, self)) return self;
11528         return make_node(AST_UnaryPrefix, self, {
11529             operator: "void",
11530             expression: make_node(AST_Number, self, {
11531                 value: 0
11532             })
11533         });
11534     });
11535
11536     OPT(AST_Infinity, function(self, compressor) {
11537         var lhs = is_lhs(compressor.self(), compressor.parent());
11538         if (lhs && is_atomic(lhs, self)) return self;
11539         if (compressor.option("keep_infinity") && !lhs && !find_scope(compressor).find_variable("Infinity")) {
11540             return self;
11541         }
11542         return make_node(AST_Binary, self, {
11543             operator: "/",
11544             left: make_node(AST_Number, self, {
11545                 value: 1
11546             }),
11547             right: make_node(AST_Number, self, {
11548                 value: 0
11549             })
11550         });
11551     });
11552
11553     OPT(AST_NaN, function(self, compressor) {
11554         var lhs = is_lhs(compressor.self(), compressor.parent());
11555         if (lhs && is_atomic(lhs, self)) return self;
11556         if (!lhs && !find_scope(compressor).find_variable("NaN")) return self;
11557         return make_node(AST_Binary, self, {
11558             operator: "/",
11559             left: make_node(AST_Number, self, {
11560                 value: 0
11561             }),
11562             right: make_node(AST_Number, self, {
11563                 value: 0
11564             })
11565         });
11566     });
11567
11568     function is_reachable(self, defs) {
11569         var reachable = false;
11570         var find_ref = new TreeWalker(function(node) {
11571             if (reachable) return true;
11572             if (node instanceof AST_SymbolRef && member(node.definition(), defs)) {
11573                 return reachable = true;
11574             }
11575         });
11576         var scan_scope = new TreeWalker(function(node) {
11577             if (reachable) return true;
11578             if (node instanceof AST_Lambda && node !== self) {
11579                 if (!(node.name || is_async(node) || is_generator(node))) {
11580                     var parent = scan_scope.parent();
11581                     if (parent instanceof AST_Call && parent.expression === node) return;
11582                 }
11583                 node.walk(find_ref);
11584                 return true;
11585             }
11586         });
11587         self.walk(scan_scope);
11588         return reachable;
11589     }
11590
11591     var ASSIGN_OPS = makePredicate("+ - * / % >> << >>> | ^ &");
11592     var ASSIGN_OPS_COMMUTATIVE = makePredicate("* | ^ &");
11593     OPT(AST_Assign, function(self, compressor) {
11594         if (compressor.option("dead_code")) {
11595             if (self.left instanceof AST_PropAccess) {
11596                 if (self.operator == "=") {
11597                     if (self.__drop) {
11598                         var exprs = [ self.left.expression ];
11599                         if (self.left instanceof AST_Sub) exprs.push(self.left.property);
11600                         exprs.push(self.right);
11601                         return make_sequence(self, exprs).optimize(compressor);
11602                     }
11603                     if (self.left.equivalent_to(self.right) && !self.left.has_side_effects(compressor)) {
11604                         return self.right;
11605                     }
11606                     var exp = self.left.expression;
11607                     if (exp instanceof AST_Lambda
11608                         || !compressor.has_directive("use strict")
11609                             && exp instanceof AST_Constant
11610                             && !exp.may_throw_on_access(compressor)) {
11611                         return self.left instanceof AST_Dot ? self.right : make_sequence(self, [
11612                             self.left.property,
11613                             self.right
11614                         ]).optimize(compressor);
11615                     }
11616                 }
11617             } else if (self.left instanceof AST_SymbolRef && can_drop_symbol(self.left, compressor)) {
11618                 var parent;
11619                 if (self.operator == "=" && self.left.equivalent_to(self.right)
11620                     && !((parent = compressor.parent()) instanceof AST_UnaryPrefix && parent.operator == "delete")) {
11621                     return self.right;
11622                 }
11623                 if (self.left.is_immutable()) return strip_assignment();
11624                 var def = self.left.definition();
11625                 var scope = def.scope.resolve();
11626                 var local = scope === compressor.find_parent(AST_Lambda);
11627                 var level = 0, node;
11628                 parent = compressor.self();
11629                 if (!(scope.uses_arguments && is_funarg(def)) || compressor.has_directive("use strict")) do {
11630                     node = parent;
11631                     parent = compressor.parent(level++);
11632                     if (parent instanceof AST_Assign) {
11633                         if (parent.left instanceof AST_SymbolRef && parent.left.definition() === def) {
11634                             if (in_try(level, parent)) break;
11635                             return strip_assignment(def);
11636                         }
11637                         if (parent.left.match_symbol(function(node) {
11638                             if (node instanceof AST_PropAccess) return true;
11639                         })) break;
11640                         continue;
11641                     }
11642                     if (parent instanceof AST_Exit) {
11643                         if (!local) break;
11644                         if (in_try(level, parent)) break;
11645                         if (is_reachable(scope, [ def ])) break;
11646                         return strip_assignment(def);
11647                     }
11648                     if (parent instanceof AST_SimpleStatement) {
11649                         if (!local) break;
11650                         if (is_reachable(scope, [ def ])) break;
11651                         var stat;
11652                         do {
11653                             stat = parent;
11654                             parent = compressor.parent(level++);
11655                             if (parent === scope && is_last_statement(parent.body, stat)) return strip_assignment(def);
11656                         } while (is_tail_block(stat, parent));
11657                         break;
11658                     }
11659                     if (parent instanceof AST_VarDef) {
11660                         if (!(parent.name instanceof AST_SymbolDeclaration)) continue;
11661                         if (parent.name.definition() !== def) continue;
11662                         if (in_try(level, parent)) break;
11663                         return strip_assignment(def);
11664                     }
11665                 } while (is_tail(node, parent));
11666             }
11667         }
11668         if (compressor.option("sequences")) {
11669             var seq = self.lift_sequences(compressor);
11670             if (seq !== self) return seq.optimize(compressor);
11671         }
11672         if (compressor.option("assignments")) {
11673             if (self.operator == "=" && self.left instanceof AST_SymbolRef && self.right instanceof AST_Binary) {
11674                 // x = expr1 OP expr2
11675                 if (self.right.left instanceof AST_SymbolRef
11676                     && self.right.left.name == self.left.name
11677                     && ASSIGN_OPS[self.right.operator]) {
11678                     // x = x - 2 ---> x -= 2
11679                     return make_node(AST_Assign, self, {
11680                         operator: self.right.operator + "=",
11681                         left: self.left,
11682                         right: self.right.right,
11683                     });
11684                 }
11685                 if (self.right.right instanceof AST_SymbolRef
11686                     && self.right.right.name == self.left.name
11687                     && ASSIGN_OPS_COMMUTATIVE[self.right.operator]
11688                     && !self.right.left.has_side_effects(compressor)) {
11689                     // x = 2 & x ---> x &= 2
11690                     return make_node(AST_Assign, self, {
11691                         operator: self.right.operator + "=",
11692                         left: self.left,
11693                         right: self.right.left,
11694                     });
11695                 }
11696             }
11697             if ((self.operator == "-=" || self.operator == "+="
11698                     && (self.left.is_boolean(compressor) || self.left.is_number(compressor)))
11699                 && self.right instanceof AST_Number
11700                 && self.right.value == 1) {
11701                 var op = self.operator.slice(0, -1);
11702                 return make_node(AST_UnaryPrefix, self, {
11703                     operator: op + op,
11704                     expression: self.left
11705                 });
11706             }
11707         }
11708         return try_evaluate(compressor, self);
11709
11710         function is_tail(node, parent) {
11711             if (parent instanceof AST_Binary) {
11712                 return parent.right === node || parent.right.is_constant_expression(scope);
11713             }
11714             if (parent instanceof AST_Conditional) {
11715                 return parent.condition !== node
11716                     || parent.consequent.is_constant_expression(scope)
11717                         && parent.alternative.is_constant_expression(scope);
11718             }
11719             if (parent instanceof AST_Sequence) {
11720                 var exprs = parent.expressions;
11721                 var stop = exprs.indexOf(node);
11722                 if (stop < 0) return false;
11723                 for (var i = exprs.length; --i > stop;) {
11724                     if (!exprs[i].is_constant_expression(scope)) return false;
11725                 }
11726                 return true;
11727             }
11728             if (parent instanceof AST_UnaryPrefix) return true;
11729         }
11730
11731         function is_tail_block(stat, parent) {
11732             if (parent instanceof AST_BlockStatement) return is_last_statement(parent.body, stat);
11733             if (parent instanceof AST_Catch) return is_last_statement(parent.body, stat);
11734             if (parent instanceof AST_Finally) return is_last_statement(parent.body, stat);
11735             if (parent instanceof AST_If) return parent.body === stat || parent.alternative === stat;
11736             if (parent instanceof AST_Try) return parent.bfinally ? parent.bfinally === stat : parent.bcatch === stat;
11737         }
11738
11739         function in_try(level, node) {
11740             var right = self.right;
11741             self.right = make_node(AST_Null, right);
11742             var may_throw = node.may_throw(compressor);
11743             self.right = right;
11744             for (var parent; parent = compressor.parent(level++); node = parent) {
11745                 if (parent === scope) return false;
11746                 if (parent instanceof AST_Try) {
11747                     if (parent.bfinally && parent.bfinally !== node) return true;
11748                     if (may_throw && parent.bcatch && parent.bcatch !== node) return true;
11749                 }
11750             }
11751         }
11752
11753         function strip_assignment(def) {
11754             if (def) def.fixed = false;
11755             return (self.operator != "=" ? make_node(AST_Binary, self, {
11756                 operator: self.operator.slice(0, -1),
11757                 left: self.left,
11758                 right: self.right,
11759             }) : maintain_this_binding(compressor, compressor.parent(), self, self.right)).optimize(compressor);
11760         }
11761     });
11762
11763     OPT(AST_Conditional, function(self, compressor) {
11764         if (compressor.option("sequences") && self.condition instanceof AST_Sequence) {
11765             var expressions = self.condition.expressions.slice();
11766             self.condition = expressions.pop();
11767             expressions.push(self);
11768             return make_sequence(self, expressions);
11769         }
11770         if (!compressor.option("conditionals")) return self;
11771         var condition = self.condition;
11772         if (compressor.option("booleans") && !condition.has_side_effects(compressor)) {
11773             mark_duplicate_condition(compressor, condition);
11774         }
11775         condition = fuzzy_eval(compressor, condition);
11776         if (!condition) {
11777             AST_Node.warn("Condition always false [{file}:{line},{col}]", self.start);
11778             return make_sequence(self, [ self.condition, self.alternative ]).optimize(compressor);
11779         } else if (!(condition instanceof AST_Node)) {
11780             AST_Node.warn("Condition always true [{file}:{line},{col}]", self.start);
11781             return make_sequence(self, [ self.condition, self.consequent ]).optimize(compressor);
11782         }
11783         var negated = condition.negate(compressor, first_in_statement(compressor));
11784         if (best_of(compressor, condition, negated) === negated) {
11785             self = make_node(AST_Conditional, self, {
11786                 condition: negated,
11787                 consequent: self.alternative,
11788                 alternative: self.consequent
11789             });
11790             negated = condition;
11791             condition = self.condition;
11792         }
11793         var consequent = self.consequent;
11794         var alternative = self.alternative;
11795         if (repeatable(compressor, condition)) {
11796             // x ? x : y ---> x || y
11797             if (condition.equivalent_to(consequent)) return make_node(AST_Binary, self, {
11798                 operator: "||",
11799                 left: condition,
11800                 right: alternative,
11801             }).optimize(compressor);
11802             // x ? y : x ---> x && y
11803             if (condition.equivalent_to(alternative)) return make_node(AST_Binary, self, {
11804                 operator: "&&",
11805                 left: condition,
11806                 right: consequent,
11807             }).optimize(compressor);
11808         }
11809         // if (foo) exp = something; else exp = something_else;
11810         //                   |
11811         //                   v
11812         // exp = foo ? something : something_else;
11813         var seq_tail = consequent.tail_node();
11814         if (seq_tail instanceof AST_Assign) {
11815             var is_eq = seq_tail.operator == "=";
11816             var alt_tail = is_eq ? alternative.tail_node() : alternative;
11817             if ((is_eq || consequent === seq_tail)
11818                 && alt_tail instanceof AST_Assign
11819                 && seq_tail.operator == alt_tail.operator
11820                 && seq_tail.left.equivalent_to(alt_tail.left)
11821                 && (is_eq && seq_tail.left instanceof AST_SymbolRef
11822                     || !condition.has_side_effects(compressor)
11823                         && can_shift_lhs_of_tail(consequent)
11824                         && can_shift_lhs_of_tail(alternative))) {
11825                 return make_node(AST_Assign, self, {
11826                     operator: seq_tail.operator,
11827                     left: seq_tail.left,
11828                     right: make_node(AST_Conditional, self, {
11829                         condition: condition,
11830                         consequent: pop_lhs(consequent),
11831                         alternative: pop_lhs(alternative)
11832                     })
11833                 });
11834             }
11835         }
11836         // x ? y : y ---> x, y
11837         if (consequent.equivalent_to(alternative)) return make_sequence(self, [
11838             condition,
11839             consequent
11840         ]).optimize(compressor);
11841         // x ? y.p : z.p ---> (x ? y : z).p
11842         // x ? y(a) : z(a) ---> (x ? y : z)(a)
11843         // x ? y.f(a) : z.f(a) ---> (x ? y : z).f(a)
11844         var combined = combine_tail(consequent, alternative, true);
11845         if (combined) return combined;
11846         // x ? y(a) : y(b) ---> y(x ? a : b)
11847         var arg_index;
11848         if (consequent instanceof AST_Call
11849             && alternative.TYPE == consequent.TYPE
11850             && (arg_index = arg_diff(consequent, alternative)) >= 0
11851             && consequent.expression.equivalent_to(alternative.expression)
11852             && !condition.has_side_effects(compressor)
11853             && !consequent.expression.has_side_effects(compressor)) {
11854             var node = consequent.clone();
11855             var arg = consequent.args[arg_index];
11856             node.args[arg_index] = arg instanceof AST_Spread ? make_node(AST_Spread, self, {
11857                 expression: make_node(AST_Conditional, self, {
11858                     condition: condition,
11859                     consequent: arg.expression,
11860                     alternative: alternative.args[arg_index].expression,
11861                 }),
11862             }) : make_node(AST_Conditional, self, {
11863                 condition: condition,
11864                 consequent: arg,
11865                 alternative: alternative.args[arg_index],
11866             });
11867             return node;
11868         }
11869         // x ? (y ? a : b) : b ---> x && y ? a : b
11870         if (consequent instanceof AST_Conditional
11871             && consequent.alternative.equivalent_to(alternative)) {
11872             return make_node(AST_Conditional, self, {
11873                 condition: make_node(AST_Binary, self, {
11874                     left: condition,
11875                     operator: "&&",
11876                     right: consequent.condition
11877                 }),
11878                 consequent: consequent.consequent,
11879                 alternative: alternative
11880             });
11881         }
11882         // x ? (y ? a : b) : a ---> !x || y ? a : b
11883         if (consequent instanceof AST_Conditional
11884             && consequent.consequent.equivalent_to(alternative)) {
11885             return make_node(AST_Conditional, self, {
11886                 condition: make_node(AST_Binary, self, {
11887                     left: negated,
11888                     operator: "||",
11889                     right: consequent.condition
11890                 }),
11891                 consequent: alternative,
11892                 alternative: consequent.alternative
11893             });
11894         }
11895         // x ? a : (y ? a : b) ---> x || y ? a : b
11896         if (alternative instanceof AST_Conditional
11897             && consequent.equivalent_to(alternative.consequent)) {
11898             return make_node(AST_Conditional, self, {
11899                 condition: make_node(AST_Binary, self, {
11900                     left: condition,
11901                     operator: "||",
11902                     right: alternative.condition
11903                 }),
11904                 consequent: consequent,
11905                 alternative: alternative.alternative
11906             });
11907         }
11908         // x ? b : (y ? a : b) ---> !x && y ? a : b
11909         if (alternative instanceof AST_Conditional
11910             && consequent.equivalent_to(alternative.alternative)) {
11911             return make_node(AST_Conditional, self, {
11912                 condition: make_node(AST_Binary, self, {
11913                     left: negated,
11914                     operator: "&&",
11915                     right: alternative.condition
11916                 }),
11917                 consequent: alternative.consequent,
11918                 alternative: consequent
11919             });
11920         }
11921         // x ? (a, c) : (b, c) ---> x ? a : b, c
11922         if ((consequent instanceof AST_Sequence || alternative instanceof AST_Sequence)
11923             && consequent.tail_node().equivalent_to(alternative.tail_node())) {
11924             return make_sequence(self, [
11925                 make_node(AST_Conditional, self, {
11926                     condition: condition,
11927                     consequent: pop_seq(consequent),
11928                     alternative: pop_seq(alternative)
11929                 }),
11930                 consequent.tail_node()
11931             ]).optimize(compressor);
11932         }
11933         // x ? y && a : a ---> (!x || y) && a
11934         if (consequent instanceof AST_Binary
11935             && consequent.operator == "&&"
11936             && consequent.right.equivalent_to(alternative)) {
11937             return make_node(AST_Binary, self, {
11938                 operator: "&&",
11939                 left: make_node(AST_Binary, self, {
11940                     operator: "||",
11941                     left: negated,
11942                     right: consequent.left
11943                 }),
11944                 right: alternative
11945             }).optimize(compressor);
11946         }
11947         // x ? y || a : a ---> x && y || a
11948         if (consequent instanceof AST_Binary
11949             && consequent.operator == "||"
11950             && consequent.right.equivalent_to(alternative)) {
11951             return make_node(AST_Binary, self, {
11952                 operator: "||",
11953                 left: make_node(AST_Binary, self, {
11954                     operator: "&&",
11955                     left: condition,
11956                     right: consequent.left
11957                 }),
11958                 right: alternative
11959             }).optimize(compressor);
11960         }
11961         // x ? a : y && a ---> (x || y) && a
11962         if (alternative instanceof AST_Binary
11963             && alternative.operator == "&&"
11964             && alternative.right.equivalent_to(consequent)) {
11965             return make_node(AST_Binary, self, {
11966                 operator: "&&",
11967                 left: make_node(AST_Binary, self, {
11968                     operator: "||",
11969                     left: condition,
11970                     right: alternative.left
11971                 }),
11972                 right: consequent
11973             }).optimize(compressor);
11974         }
11975         // x ? a : y || a ---> !x && y || a
11976         if (alternative instanceof AST_Binary
11977             && alternative.operator == "||"
11978             && alternative.right.equivalent_to(consequent)) {
11979             return make_node(AST_Binary, self, {
11980                 operator: "||",
11981                 left: make_node(AST_Binary, self, {
11982                     operator: "&&",
11983                     left: negated,
11984                     right: alternative.left
11985                 }),
11986                 right: consequent
11987             }).optimize(compressor);
11988         }
11989         var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
11990         if (is_true(consequent)) {
11991             if (is_false(alternative)) {
11992                 // c ? true : false ---> !!c
11993                 return booleanize(condition);
11994             }
11995             // c ? true : x ---> !!c || x
11996             return make_node(AST_Binary, self, {
11997                 operator: "||",
11998                 left: booleanize(condition),
11999                 right: alternative
12000             });
12001         }
12002         if (is_false(consequent)) {
12003             if (is_true(alternative)) {
12004                 // c ? false : true ---> !c
12005                 return booleanize(condition.negate(compressor));
12006             }
12007             // c ? false : x ---> !c && x
12008             return make_node(AST_Binary, self, {
12009                 operator: "&&",
12010                 left: booleanize(condition.negate(compressor)),
12011                 right: alternative
12012             });
12013         }
12014         if (is_true(alternative)) {
12015             // c ? x : true ---> !c || x
12016             return make_node(AST_Binary, self, {
12017                 operator: "||",
12018                 left: booleanize(condition.negate(compressor)),
12019                 right: consequent
12020             });
12021         }
12022         if (is_false(alternative)) {
12023             // c ? x : false ---> !!c && x
12024             return make_node(AST_Binary, self, {
12025                 operator: "&&",
12026                 left: booleanize(condition),
12027                 right: consequent
12028             });
12029         }
12030         if (compressor.option("typeofs")) mark_locally_defined(condition, consequent, alternative);
12031         return self;
12032
12033         function booleanize(node) {
12034             if (node.is_boolean(compressor)) return node;
12035             // !!expression
12036             return make_node(AST_UnaryPrefix, node, {
12037                 operator: "!",
12038                 expression: node.negate(compressor)
12039             });
12040         }
12041
12042         // AST_True or !0
12043         function is_true(node) {
12044             return node instanceof AST_True
12045                 || in_bool
12046                     && node instanceof AST_Constant
12047                     && node.value
12048                 || (node instanceof AST_UnaryPrefix
12049                     && node.operator == "!"
12050                     && node.expression instanceof AST_Constant
12051                     && !node.expression.value);
12052         }
12053         // AST_False or !1 or void 0
12054         function is_false(node) {
12055             return node instanceof AST_False
12056                 || in_bool
12057                     && (node instanceof AST_Constant
12058                             && !node.value
12059                         || node instanceof AST_UnaryPrefix
12060                             && node.operator == "void"
12061                             && !node.expression.has_side_effects(compressor))
12062                 || (node instanceof AST_UnaryPrefix
12063                     && node.operator == "!"
12064                     && node.expression instanceof AST_Constant
12065                     && node.expression.value);
12066         }
12067
12068         function arg_diff(consequent, alternative) {
12069             var a = consequent.args;
12070             var b = alternative.args;
12071             var len = a.length;
12072             if (len != b.length) return -2;
12073             for (var i = 0; i < len; i++) {
12074                 if (!a[i].equivalent_to(b[i])) {
12075                     if (a[i] instanceof AST_Spread !== b[i] instanceof AST_Spread) return -3;
12076                     for (var j = i + 1; j < len; j++) {
12077                         if (!a[j].equivalent_to(b[j])) return -2;
12078                     }
12079                     return i;
12080                 }
12081             }
12082             return -1;
12083         }
12084
12085         function is_tail_equivalent(consequent, alternative) {
12086             if (consequent.TYPE != alternative.TYPE) return;
12087             if (consequent.optional != alternative.optional) return;
12088             if (consequent instanceof AST_Call) {
12089                 if (arg_diff(consequent, alternative) != -1) return;
12090                 return consequent.TYPE != "Call"
12091                     || !(consequent.expression instanceof AST_PropAccess
12092                         || alternative.expression instanceof AST_PropAccess)
12093                     || is_tail_equivalent(consequent.expression, alternative.expression);
12094             }
12095             if (!(consequent instanceof AST_PropAccess)) return;
12096             var p = consequent.property;
12097             var q = alternative.property;
12098             return (p instanceof AST_Node ? p.equivalent_to(q) : p == q)
12099                 && !(consequent.expression instanceof AST_Super || alternative.expression instanceof AST_Super);
12100         }
12101
12102         function combine_tail(consequent, alternative, top) {
12103             if (!is_tail_equivalent(consequent, alternative)) return !top && make_node(AST_Conditional, self, {
12104                 condition: condition,
12105                 consequent: consequent,
12106                 alternative: alternative,
12107             });
12108             var node = consequent.clone();
12109             node.expression = combine_tail(consequent.expression, alternative.expression);
12110             return node;
12111         }
12112
12113         function can_shift_lhs_of_tail(node) {
12114             return node === node.tail_node() || all(node.expressions.slice(0, -1), function(expr) {
12115                 return !expr.has_side_effects(compressor);
12116             });
12117         }
12118
12119         function pop_lhs(node) {
12120             if (!(node instanceof AST_Sequence)) return node.right;
12121             var exprs = node.expressions.slice();
12122             exprs.push(exprs.pop().right);
12123             return make_sequence(node, exprs);
12124         }
12125
12126         function pop_seq(node) {
12127             if (!(node instanceof AST_Sequence)) return make_node(AST_Number, node, {
12128                 value: 0
12129             });
12130             return make_sequence(node, node.expressions.slice(0, -1));
12131         }
12132     });
12133
12134     OPT(AST_Boolean, function(self, compressor) {
12135         if (!compressor.option("booleans")) return self;
12136         if (compressor.in_boolean_context()) return make_node(AST_Number, self, {
12137             value: +self.value
12138         });
12139         var p = compressor.parent();
12140         if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) {
12141             AST_Node.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
12142                 operator : p.operator,
12143                 value    : self.value,
12144                 file     : p.start.file,
12145                 line     : p.start.line,
12146                 col      : p.start.col,
12147             });
12148             return make_node(AST_Number, self, {
12149                 value: +self.value
12150             });
12151         }
12152         return make_node(AST_UnaryPrefix, self, {
12153             operator: "!",
12154             expression: make_node(AST_Number, self, {
12155                 value: 1 - self.value
12156             })
12157         });
12158     });
12159
12160     OPT(AST_Spread, function(self, compressor) {
12161         var exp = self.expression;
12162         if (compressor.option("spreads") && exp instanceof AST_Array && !(compressor.parent() instanceof AST_Object)) {
12163             return List.splice(exp.elements.map(function(node) {
12164                 return node instanceof AST_Hole ? make_node(AST_Undefined, node).optimize(compressor) : node;
12165             }));
12166         }
12167         return self;
12168     });
12169
12170     function safe_to_flatten(value, compressor) {
12171         if (!value) return false;
12172         var parent = compressor.parent();
12173         if (parent.TYPE != "Call") return true;
12174         if (parent.expression !== compressor.self()) return true;
12175         if (value instanceof AST_SymbolRef) {
12176             value = value.fixed_value();
12177             if (!value) return false;
12178         }
12179         return value instanceof AST_Lambda && !value.contains_this();
12180     }
12181
12182     OPT(AST_Sub, function(self, compressor) {
12183         var expr = self.expression;
12184         var prop = self.property;
12185         var terminated = trim_optional_chain(self, compressor);
12186         if (terminated) return terminated;
12187         if (compressor.option("properties")) {
12188             var key = prop.evaluate(compressor);
12189             if (key !== prop) {
12190                 if (typeof key == "string") {
12191                     if (key == "undefined") {
12192                         key = undefined;
12193                     } else {
12194                         var value = parseFloat(key);
12195                         if (value.toString() == key) {
12196                             key = value;
12197                         }
12198                     }
12199                 }
12200                 prop = self.property = best_of_expression(prop, make_node_from_constant(key, prop).transform(compressor));
12201                 var property = "" + key;
12202                 if (is_identifier_string(property)
12203                     && property.length <= prop.print_to_string().length + 1) {
12204                     return make_node(AST_Dot, self, {
12205                         optional: self.optional,
12206                         expression: expr,
12207                         property: property,
12208                     }).optimize(compressor);
12209                 }
12210             }
12211         }
12212         var parent = compressor.parent();
12213         var assigned = is_lhs(compressor.self(), parent);
12214         var def, fn, fn_parent, index;
12215         if (compressor.option("arguments")
12216             && expr instanceof AST_SymbolRef
12217             && is_arguments(def = expr.definition())
12218             && !expr.in_arg
12219             && prop instanceof AST_Number
12220             && Math.floor(index = prop.value) == index
12221             && (fn = def.scope) === find_lambda()
12222             && fn.uses_arguments < (assigned ? 2 : 3)) {
12223             if (parent instanceof AST_UnaryPrefix && parent.operator == "delete") {
12224                 if (!def.deleted) def.deleted = [];
12225                 def.deleted[index] = true;
12226             }
12227             var argname = fn.argnames[index];
12228             if (def.deleted && def.deleted[index]) {
12229                 argname = null;
12230             } else if (argname) {
12231                 var arg_def;
12232                 if (!(argname instanceof AST_SymbolFunarg)
12233                     || argname.name == "await"
12234                     || expr.scope.find_variable(argname.name) !== (arg_def = argname.definition())) {
12235                     argname = null;
12236                 } else if (compressor.has_directive("use strict")
12237                     || fn.name
12238                     || fn.rest
12239                     || !(fn_parent instanceof AST_Call
12240                         && index < fn_parent.args.length
12241                         && all(fn_parent.args.slice(0, index + 1), function(arg) {
12242                             return !(arg instanceof AST_Spread);
12243                         }))
12244                     || !all(fn.argnames, function(argname) {
12245                         return argname instanceof AST_SymbolFunarg;
12246                     })) {
12247                     if (has_reassigned() || arg_def.assignments || arg_def.orig.length > 1) argname = null;
12248                 }
12249             } else if ((assigned || !has_reassigned())
12250                 && index < fn.argnames.length + 5
12251                 && compressor.drop_fargs(fn, fn_parent)
12252                 && !fn.rest) {
12253                 while (index >= fn.argnames.length) {
12254                     argname = fn.make_var(AST_SymbolFunarg, fn, "argument_" + fn.argnames.length);
12255                     fn.argnames.push(argname);
12256                 }
12257             }
12258             if (argname && find_if(function(node) {
12259                 return node.name === argname.name;
12260             }, fn.argnames) === argname) {
12261                 if (assigned) def.reassigned--;
12262                 var sym = make_node(AST_SymbolRef, self, argname);
12263                 sym.reference();
12264                 delete argname.__unused;
12265                 return sym;
12266             }
12267         }
12268         if (assigned) return self;
12269         if (compressor.option("sequences")
12270             && parent.TYPE != "Call"
12271             && !(parent instanceof AST_ForEnumeration && parent.init === self)) {
12272             var seq = lift_sequence_in_expression(self, compressor);
12273             if (seq !== self) return seq.optimize(compressor);
12274         }
12275         if (key !== prop) {
12276             var sub = self.flatten_object(property, compressor);
12277             if (sub) {
12278                 expr = self.expression = sub.expression;
12279                 prop = self.property = sub.property;
12280             }
12281         }
12282         var elements;
12283         if (compressor.option("properties")
12284             && compressor.option("side_effects")
12285             && prop instanceof AST_Number
12286             && expr instanceof AST_Array
12287             && all(elements = expr.elements, function(value) {
12288                 return !(value instanceof AST_Spread);
12289             })) {
12290             var index = prop.value;
12291             var retValue = elements[index];
12292             if (safe_to_flatten(retValue, compressor)) {
12293                 var is_hole = retValue instanceof AST_Hole;
12294                 var flatten = !is_hole;
12295                 var values = [];
12296                 for (var i = elements.length; --i > index;) {
12297                     var value = elements[i].drop_side_effect_free(compressor);
12298                     if (value) {
12299                         values.unshift(value);
12300                         if (flatten && value.has_side_effects(compressor)) flatten = false;
12301                     }
12302                 }
12303                 if (!flatten) values.unshift(retValue);
12304                 while (--i >= 0) {
12305                     var value = elements[i].drop_side_effect_free(compressor);
12306                     if (value) {
12307                         values.unshift(value);
12308                     } else if (is_hole) {
12309                         values.unshift(make_node(AST_Hole, elements[i]));
12310                     } else {
12311                         index--;
12312                     }
12313                 }
12314                 if (flatten) {
12315                     values.push(retValue);
12316                     return make_sequence(self, values).optimize(compressor);
12317                 } else return make_node(AST_Sub, self, {
12318                     expression: make_node(AST_Array, expr, { elements: values }),
12319                     property: make_node(AST_Number, prop, { value: index }),
12320                 });
12321             }
12322         }
12323         return try_evaluate(compressor, self);
12324
12325         function find_lambda() {
12326             var i = 0, p;
12327             while (p = compressor.parent(i++)) {
12328                 if (p instanceof AST_Lambda) {
12329                     if (p instanceof AST_Accessor) return;
12330                     if (is_arrow(p)) continue;
12331                     fn_parent = compressor.parent(i);
12332                     return p;
12333                 }
12334             }
12335         }
12336
12337         function has_reassigned() {
12338             return !compressor.option("reduce_vars") || def.reassigned;
12339         }
12340     });
12341
12342     AST_Arrow.DEFMETHOD("contains_super", return_false);
12343     AST_AsyncArrow.DEFMETHOD("contains_super", return_false);
12344     AST_Lambda.DEFMETHOD("contains_super", function() {
12345         var result;
12346         var self = this;
12347         self.walk(new TreeWalker(function(node) {
12348             if (result) return true;
12349             if (node instanceof AST_Super) return result = true;
12350             if (node !== self && node instanceof AST_Scope && !is_arrow(node)) return true;
12351         }));
12352         return result;
12353     });
12354     AST_LambdaDefinition.DEFMETHOD("contains_super", return_false);
12355     AST_Scope.DEFMETHOD("contains_super", return_false);
12356
12357     AST_Arrow.DEFMETHOD("contains_this", return_false);
12358     AST_AsyncArrow.DEFMETHOD("contains_this", return_false);
12359     AST_Node.DEFMETHOD("contains_this", function() {
12360         var result;
12361         var self = this;
12362         self.walk(new TreeWalker(function(node) {
12363             if (result) return true;
12364             if (node instanceof AST_This) return result = true;
12365             if (node !== self && node instanceof AST_Scope && !is_arrow(node)) return true;
12366         }));
12367         return result;
12368     });
12369
12370     function can_hoist_property(prop) {
12371         return prop instanceof AST_ObjectKeyVal
12372             && typeof prop.key == "string"
12373             && !(prop instanceof AST_ObjectMethod && prop.value.contains_super());
12374     }
12375
12376     AST_PropAccess.DEFMETHOD("flatten_object", function(key, compressor) {
12377         if (!compressor.option("properties")) return;
12378         if (key === "__proto__") return;
12379         var expr = this.expression;
12380         if (expr instanceof AST_Object) {
12381             var props = expr.properties;
12382             for (var i = props.length; --i >= 0;) {
12383                 var prop = props[i];
12384                 if (prop.key !== key) continue;
12385                 if (!all(props, can_hoist_property)) return;
12386                 if (!safe_to_flatten(prop.value, compressor)) return;
12387                 var scope, values = [];
12388                 for (var j = 0; j < props.length; j++) {
12389                     var value = props[j].value;
12390                     if (props[j] instanceof AST_ObjectMethod) {
12391                         var arrow = !(value.uses_arguments || is_generator(value) || value.contains_this());
12392                         if (arrow) {
12393                             if (!scope) scope = compressor.find_parent(AST_Scope);
12394                             var avoid = avoid_await_yield(scope);
12395                             value.each_argname(function(argname) {
12396                                 if (avoid[argname.name]) arrow = false;
12397                             });
12398                         }
12399                         var ctor;
12400                         if (arrow) {
12401                             ctor = is_async(value) ? AST_AsyncArrow : AST_Arrow;
12402                         } else if (i === j && !(compressor.parent() instanceof AST_Call)) {
12403                             return;
12404                         } else {
12405                             ctor = value.CTOR;
12406                         }
12407                         value = make_node(ctor, value, value);
12408                     }
12409                     values.push(value);
12410                 }
12411                 return make_node(AST_Sub, this, {
12412                     expression: make_node(AST_Array, expr, { elements: values }),
12413                     property: make_node(AST_Number, this, { value: i }),
12414                 });
12415             }
12416         }
12417     });
12418
12419     OPT(AST_Dot, function(self, compressor) {
12420         if (self.property == "arguments" || self.property == "caller") {
12421             AST_Node.warn("Function.prototype.{prop} not supported [{file}:{line},{col}]", {
12422                 prop: self.property,
12423                 file: self.start.file,
12424                 line: self.start.line,
12425                 col: self.start.col,
12426             });
12427         }
12428         var parent = compressor.parent();
12429         if (is_lhs(compressor.self(), parent)) return self;
12430         var terminated = trim_optional_chain(self, compressor);
12431         if (terminated) return terminated;
12432         if (compressor.option("sequences")
12433             && parent.TYPE != "Call"
12434             && !(parent instanceof AST_ForEnumeration && parent.init === self)) {
12435             var seq = lift_sequence_in_expression(self, compressor);
12436             if (seq !== self) return seq.optimize(compressor);
12437         }
12438         if (compressor.option("unsafe_proto")
12439             && self.expression instanceof AST_Dot
12440             && self.expression.property == "prototype") {
12441             var exp = self.expression.expression;
12442             if (is_undeclared_ref(exp)) switch (exp.name) {
12443               case "Array":
12444                 self.expression = make_node(AST_Array, self.expression, {
12445                     elements: []
12446                 });
12447                 break;
12448               case "Function":
12449                 self.expression = make_node(AST_Function, self.expression, {
12450                     argnames: [],
12451                     body: []
12452                 }).init_vars(exp.scope);
12453                 break;
12454               case "Number":
12455                 self.expression = make_node(AST_Number, self.expression, {
12456                     value: 0
12457                 });
12458                 break;
12459               case "Object":
12460                 self.expression = make_node(AST_Object, self.expression, {
12461                     properties: []
12462                 });
12463                 break;
12464               case "RegExp":
12465                 self.expression = make_node(AST_RegExp, self.expression, {
12466                     value: /t/
12467                 });
12468                 break;
12469               case "String":
12470                 self.expression = make_node(AST_String, self.expression, {
12471                     value: ""
12472                 });
12473                 break;
12474             }
12475         }
12476         var sub = self.flatten_object(self.property, compressor);
12477         if (sub) return sub.optimize(compressor);
12478         return try_evaluate(compressor, self);
12479     });
12480
12481     OPT(AST_DestructuredArray, function(self, compressor) {
12482         if (compressor.option("rests") && self.rest instanceof AST_DestructuredArray) {
12483             return make_node(AST_DestructuredArray, self, {
12484                 elements: self.elements.concat(self.rest.elements),
12485                 rest: self.rest.rest,
12486             });
12487         }
12488         return self;
12489     });
12490
12491     OPT(AST_DestructuredKeyVal, function(self, compressor) {
12492         if (compressor.option("objects")) {
12493             var key = self.key;
12494             if (key instanceof AST_Node) {
12495                 key = key.evaluate(compressor);
12496                 if (key !== self.key) self.key = "" + key;
12497             }
12498         }
12499         return self;
12500     });
12501
12502     OPT(AST_Object, function(self, compressor) {
12503         if (!compressor.option("objects")) return self;
12504         var changed = false;
12505         var found = false;
12506         var generated = false;
12507         var keep_duplicate = compressor.has_directive("use strict");
12508         var keys = new Dictionary();
12509         var values = [];
12510         self.properties.forEach(function(prop) {
12511             if (!(prop instanceof AST_Spread)) return process(prop);
12512             found = true;
12513             var exp = prop.expression;
12514             if (compressor.option("spreads") && exp instanceof AST_Object && all(exp.properties, function(prop) {
12515                 if (prop instanceof AST_ObjectGetter) return false;
12516                 if (prop instanceof AST_Spread) return false;
12517                 if (prop.key !== "__proto__") return true;
12518                 if (prop instanceof AST_ObjectSetter) return true;
12519                 return !prop.value.has_side_effects(compressor);
12520             })) {
12521                 changed = true;
12522                 exp.properties.forEach(function(prop) {
12523                     var key = prop.key;
12524                     var setter = prop instanceof AST_ObjectSetter;
12525                     if (key === "__proto__") {
12526                         if (!setter) return;
12527                         key = make_node_from_constant(key, prop);
12528                     }
12529                     process(setter ? make_node(AST_ObjectKeyVal, prop, {
12530                         key: key,
12531                         value: make_node(AST_Undefined, prop).optimize(compressor),
12532                     }) : prop);
12533                 });
12534             } else {
12535                 generated = true;
12536                 flush();
12537                 values.push(prop);
12538             }
12539         });
12540         flush();
12541         if (!changed) return self;
12542         if (found && generated && values.length == 1) {
12543             var value = values[0];
12544             if (value instanceof AST_ObjectProperty && value.key instanceof AST_Number) {
12545                 value.key = "" + value.key.value;
12546             }
12547         }
12548         return make_node(AST_Object, self, { properties: values });
12549
12550         function flush() {
12551             keys.each(function(props) {
12552                 if (props.length == 1) return values.push(props[0]);
12553                 changed = true;
12554                 var tail = keep_duplicate && !generated && props.pop();
12555                 values.push(props.length == 1 ? props[0] : make_node(AST_ObjectKeyVal, self, {
12556                     key: props[0].key,
12557                     value: make_sequence(self, props.map(function(prop) {
12558                         return prop.value;
12559                     }))
12560                 }));
12561                 if (tail) values.push(tail);
12562             });
12563             keys = new Dictionary();
12564         }
12565
12566         function process(prop) {
12567             var key = prop.key;
12568             if (key instanceof AST_Node) {
12569                 found = true;
12570                 key = key.evaluate(compressor);
12571                 if (key === prop.key || key === "__proto__") {
12572                     generated = true;
12573                 } else {
12574                     key = prop.key = "" + key;
12575                 }
12576             }
12577             if (can_hoist_property(prop)) {
12578                 if (prop.value.has_side_effects(compressor)) flush();
12579                 keys.add(key, prop);
12580             } else {
12581                 flush();
12582                 values.push(prop);
12583             }
12584             if (found && !generated && typeof key == "string" && RE_POSITIVE_INTEGER.test(key)) {
12585                 generated = true;
12586                 if (keys.has(key)) prop = keys.get(key)[0];
12587                 prop.key = make_node(AST_Number, prop, { value: +key });
12588             }
12589         }
12590     });
12591
12592     OPT(AST_Return, function(self, compressor) {
12593         if (compressor.option("side_effects")
12594             && self.value
12595             && is_undefined(self.value, compressor)
12596             && !in_async_generator(compressor.find_parent(AST_Scope))) {
12597             self.value = null;
12598         }
12599         return self;
12600     });
12601 })(function(node, optimizer) {
12602     node.DEFMETHOD("optimize", function(compressor) {
12603         var self = this;
12604         if (self._optimized) return self;
12605         if (compressor.has_directive("use asm")) return self;
12606         var opt = optimizer(self, compressor);
12607         opt._optimized = true;
12608         return opt;
12609     });
12610 });